new: custom boot screen before main menu

- Show "SMOO made by CraftyBoss" for about 5 seconds
- Then show "Freeze-Tag & Sardines made by Amethyst-szs" for about 5 seconds.
- This does only overlay the game starting normally and does not slow down the process additionally.

---

Changes compared to the original cherry-picked version:
- Put all into the speedboot namespace instead of only a few things.
- Don't load into the game, but stay in the main menu
  - (Because of issues with new save files)
- Added text: "Made by CraftyBoss"
- Added text: "& Sardines"
- Speed up transition animation between the two screens
  - (Because it is now shown for a shorter total time)

Instead of directly (speed)booting into the game like in the original cherry-picked commit, this stays in the main menu.
The original behavior can be restored by setting `speedbootAutoload` to `true` inside `BootHooks.cpp`.

This is to prevent issues with new empty save files.
They normally don't get to the main menu but to an extra menu that now is invisible behind the custom boot screen.
The game doesn't load automatically then but the player needs to blindly navigate the invisible menu to start the game.
Till Cap Kingdom is fully loaded only the custom boot screen is visible (e.g. during the first cutscene video).

(cherry picked from commit c1eac0852eb839560dbc2ccafe373176c9911684)

Co-authored-by: Robin C. Ladiges <rcl.git@blackpinguin.de>
This commit is contained in:
Amethyst-szs 2022-12-30 14:39:56 -08:00 committed by Robin C. Ladiges
parent 6af21f1b8f
commit 7fa8c659e1
No known key found for this signature in database
GPG key ID: B494D3DF92661B99
10 changed files with 342 additions and 3 deletions

View file

@ -36,6 +36,7 @@ SOURCES := \
source/sead/time \ source/sead/time \
source/sead \ source/sead \
source/puppets \ source/puppets \
source/speedboot \
source/server/hns \ source/server/hns \
source/server/sardines \ source/server/sardines \
source/server/freeze-tag \ source/server/freeze-tag \

View file

@ -121,6 +121,7 @@ namespace al
void setPaneLocalSize( al::IUseLayout* layout, const char* paneName, sead::Vector2f const&); void setPaneLocalSize( al::IUseLayout* layout, const char* paneName, sead::Vector2f const&);
void setPaneLocalScale( al::IUseLayout* layout, const char* paneName, sead::Vector2f const&); void setPaneLocalScale( al::IUseLayout* layout, const char* paneName, sead::Vector2f const&);
void setPaneLocalRotate(al::IUseLayout* layout, const char* paneName, sead::Vector3f const&); void setPaneLocalRotate(al::IUseLayout* layout, const char* paneName, sead::Vector3f const&);
void setPaneVtxColor(al::IUseLayout const* layout, char const* paneName, sead::Color4u8 const&);
sead::Vector3f& getPaneLocalTrans(const al::IUseLayout* layout, const char* paneName); sead::Vector3f& getPaneLocalTrans(const al::IUseLayout* layout, const char* paneName);
void getPaneLocalSize(sead::Vector2f*, const al::IUseLayout* layout, const char* paneName); void getPaneLocalSize(sead::Vector2f*, const al::IUseLayout* layout, const char* paneName);

View file

@ -19,17 +19,17 @@ class WorldResourceLoader : public al::HioNode {
void requestLoadWorldResource(int); void requestLoadWorldResource(int);
void createResourcePlayer(void); void createResourcePlayer(void);
void tryDestroyWorldResourceOnlyCap(void); void tryDestroyWorldResourceOnlyCap(void);
void calcLoadPercent(void); float calcLoadPercent(void) const;
void getLoadWorldId(void); void getLoadWorldId(void);
bool tryLoadResource(char const*, char const*, char const*); bool tryLoadResource(char const*, char const*, char const*);
void loadWorldResource(int, int, bool, char const*); void loadWorldResource(int, int, bool, char const*);
void calcWorldResourceHeapSize(void); void calcWorldResourceHeapSize(void);
al::AsyncFunctorThread* mResourceLoadThread; // 0x08 al::AsyncFunctorThread* mResourceLoadThread; // 0x08
sead::Heap* mWorldResHeap = nullptr; // 0x10 sead::Heap* mSceneHeap = nullptr; // 0x10
sead::Heap* mCapHeap = nullptr; // 0x18 sead::Heap* mCapHeap = nullptr; // 0x18
sead::Heap* mWaterfallHeap = nullptr; // 0x20 sead::Heap* mWaterfallHeap = nullptr; // 0x20
int mCurWorld = -1; // 0x28 int mCurLoadedWorldId = -1; // 0x28
int mCurScenario = -1; // 0x2C int mCurScenario = -1; // 0x2C
bool unkBool = true; // 0x30 bool unkBool = true; // 0x30
bool mIsCancelLoad = true; // 0x31 bool mIsCancelLoad = true; // 0x31

View file

@ -0,0 +1,22 @@
#pragma once
#include "al/nerve/Nerve.h"
#include "al/nerve/NerveKeeper.h"
#include "al/util/NerveUtil.h"
#include "game/HakoniwaSequence/HakoniwaSequence.h"
NERVE_HEADER(HakoniwaSequence, LoadStage);
NERVE_HEADER(HakoniwaSequence, LoadWorldResourceWithBoot);
NERVE_IMPL(HakoniwaSequence, LoadStage);
NERVE_IMPL(HakoniwaSequence, LoadWorldResourceWithBoot);
namespace speedboot {
class CustomBootNerve : public al::Nerve {
public:
void execute(al::NerveKeeper* keeper) override {
if (al::updateNerveState(keeper->mParent)) {
al::setNerve(keeper->mParent, &nrvHakoniwaSequenceLoadStage);
}
}
};
}

View file

@ -0,0 +1,79 @@
#pragma once
#include "al/nerve/NerveStateBase.h"
#include "al/util/NerveUtil.h"
#include "game/GameData/GameDataFunction.h"
#include "game/HakoniwaSequence/HakoniwaSequence.h"
namespace speedboot {
namespace {
NERVE_HEADER(HakoniwaSequenceSpeedboot, InitThread)
NERVE_HEADER(HakoniwaSequenceSpeedboot, LoadStage)
NERVE_HEADER(HakoniwaSequenceSpeedboot, WipeToKill)
}
struct HakoniwaSequenceSpeedboot : public al::NerveStateBase {
public:
HakoniwaSequenceSpeedboot(HakoniwaSequence* sequence) : al::NerveStateBase("Speedboot"), mSequence(sequence) {
initNerve(&nrvHakoniwaSequenceSpeedbootLoadStage, 0);
}
void exeInitThread() {
if (al::isFirstStep(this)) {
mSequence->mInitThread->start();
}
if (mSequence->mInitThread->isDone()) {
al::setNerve(this, &nrvHakoniwaSequenceSpeedbootLoadStage);
}
}
bool isDoneLoading() const {
return mSequence->mResourceLoader->isEndLoadWorldResource()
&& mSequence->mInitThread->isDone()
;
}
void exeLoadStage() {
if (al::isFirstStep(this)) {
mSequence->mInitThread->start();
const char* name = GameDataFunction::getNextStageName(this->mSequence->mGameDataHolder);
if (name == nullptr) {
name = GameDataFunction::getMainStageName(this->mSequence->mGameDataHolder, 0);
}
int scenario = GameDataFunction::calcNextScenarioNo(this->mSequence->mGameDataHolder);
if (scenario == -1) {
scenario = 1;
}
int world = this->mSequence->mGameDataHolder.mData->mWorldList->tryFindWorldIndexByStageName(name);
if (world > -1) {
mSequence->mResourceLoader->requestLoadWorldHomeStageResource(world, scenario);
}
}
if (isDoneLoading()) {
al::setNerve(this, &nrvHakoniwaSequenceSpeedbootWipeToKill);
}
}
void exeWipeToKill() {
if (al::isFirstStep(this)) {
mSequence->mWipeHolder->startClose("FadeWhite", -1);
}
if (mSequence->mWipeHolder->isCloseEnd()) {
kill();
}
}
private:
HakoniwaSequence* mSequence;
};
namespace {
NERVE_IMPL(HakoniwaSequenceSpeedboot, InitThread);
NERVE_IMPL(HakoniwaSequenceSpeedboot, LoadStage);
NERVE_IMPL(HakoniwaSequenceSpeedboot, WipeToKill);
}
}

View file

@ -0,0 +1,60 @@
#pragma once
#include "al/layout/LayoutActor.h"
#include "al/layout/LayoutInitInfo.h"
#include "al/util/NerveUtil.h"
#include "game/WorldList/WorldResourceLoader.h"
#include "sead/math/seadVector.h"
namespace speedboot {
class SpeedbootLoad : public al::LayoutActor {
public:
SpeedbootLoad(
WorldResourceLoader* resourceLoader,
const al::LayoutInitInfo& initInfo,
float autoCloseAfter
);
void exeAppear();
void exeWait();
void exeDecrease();
void exeEnd();
float mTime = 0.f;
float mProgression = 0.f;
float mRotTime = 0.f;
// Online logo part
sead::Vector2f mOnlineLogoTrans = sead::Vector2f::zero;
sead::Vector2f mOnlineLogoTransTarget = sead::Vector2f::zero;
float mOnlineLogoScale = 0.f;
float mOnlineLogoScaleTarget = 0.f;
float mOnlineCreditScale = 1.f;
float mOnlineCreditScaleTarget = 1.f;
// Freze tag logo root
float mFreezeLogoTransX = 1000.f;
float mFreezeLogoTransXTarget = 1000.f;
// Borders
sead::Vector2f mFreezeBorder = { 700.f, 420.f };
sead::Vector2f mFreezeBorderTarget = { 700.f, 420.f };
// Backgrounds
float mFreezeBGTransX = 0.f;
float mFreezeBGTransXTarget = 0.f;
private:
float mAutoCloseAfter = 0.f;
WorldResourceLoader* worldResourceLoader;
};
namespace {
NERVE_HEADER(SpeedbootLoad, Appear)
NERVE_HEADER(SpeedbootLoad, Wait)
NERVE_HEADER(SpeedbootLoad, Decrease)
NERVE_HEADER(SpeedbootLoad, End)
}
}

View file

@ -132,3 +132,8 @@ B59E28 B seadPrintHook // sead::system::print
538A94 B freezePlayerHitPointDamage // Reimplements the damage function, disabling it's functionality in Freeze Tag 538A94 B freezePlayerHitPointDamage // Reimplements the damage function, disabling it's functionality in Freeze Tag
51B280 B freezeKidsMode // Forces kids mode to be enabled during Freeze Tag 51B280 B freezeKidsMode // Forces kids mode to be enabled during Freeze Tag
1CFBA0 BL freezeMoonHitboxDisable // When mode enabled, disable hitboxes with moons to avoid softlocks 1CFBA0 BL freezeMoonHitboxDisable // When mode enabled, disable hitboxes with moons to avoid softlocks
// custom bootscreen hooks
50EF28 BL speedboot::hakoniwaSetNerveSetup
50EB88 ORR w2, wzr, #0x1f // nerve state count
50EB64 BL speedboot::prepareLayoutInitInfo

Binary file not shown.

View file

@ -0,0 +1,40 @@
#include "al/layout/LayoutInitInfo.h"
#include "al/nerve/Nerve.h"
#include "al/util/NerveUtil.h"
#include "game/HakoniwaSequence/HakoniwaSequence.h"
#include "speedboot/SpeedbootLoad.hpp"
#include "speedboot/CustomBootNerve.hpp"
#include "speedboot/HakoniwaSequenceSpeedboot.hpp"
namespace speedboot {
CustomBootNerve nrvSpeedboot;
const bool speedbootAutoload = false; // set this to true, to automatically load the game, which skips the main menu (this has issues with empty save files)
al::LayoutInitInfo copiedInitInfo;
HakoniwaSequenceSpeedboot* deezNutsState;
extern "C" void _ZN10BootLayoutC1ERKN2al14LayoutInitInfoE(BootLayout* layout, const al::LayoutInitInfo& layoutInitInfo);
void prepareLayoutInitInfo(BootLayout* layout, const al::LayoutInitInfo& layoutInitInfo) {
register HakoniwaSequence* sequence asm("x19");
new SpeedbootLoad(
sequence->mResourceLoader,
layoutInitInfo,
speedbootAutoload ? 0.f : 10.f
);
_ZN10BootLayoutC1ERKN2al14LayoutInitInfoE(layout, layoutInitInfo);
}
void hakoniwaSetNerveSetup(al::IUseNerve* useNerve, al::Nerve* nerve) {
if (!speedbootAutoload) {
return;
}
al::setNerve(useNerve, &nrvSpeedboot);
auto* sequence = static_cast<HakoniwaSequence*>(useNerve);
deezNutsState = new HakoniwaSequenceSpeedboot(sequence);
al::initNerveState(useNerve, deezNutsState, &nrvSpeedboot, "Speedboot");
}
}

View file

@ -0,0 +1,131 @@
#include "speedboot/SpeedbootLoad.hpp"
#include "al/util.hpp"
#include "al/util/LayoutUtil.h"
#include "al/util/LiveActorUtil.h"
#include "al/util/MathUtil.h"
#include "sead/gfx/seadColor.h"
#include "sead/math/seadMathCalcCommon.h"
#include "sead/prim/seadSafeString.h"
#include "server/DeltaTime.hpp"
namespace speedboot {
SpeedbootLoad::SpeedbootLoad(
WorldResourceLoader* resourceLoader,
const al::LayoutInitInfo& initInfo,
float autoCloseAfter
) : al::LayoutActor("SpeedbootLoad"), worldResourceLoader(resourceLoader), mAutoCloseAfter(autoCloseAfter) {
al::initLayoutActor(this, initInfo, "SpeedbootLoad", nullptr);
initNerve(&nrvSpeedbootLoadAppear, 0);
appear();
}
void SpeedbootLoad::exeAppear() {
if (al::isFirstStep(this)) {
al::startAction(this, "Appear", nullptr);
}
if (al::isActionEnd(this, nullptr)) {
al::setNerve(this, &nrvSpeedbootLoadWait);
}
}
void SpeedbootLoad::exeWait() {
if (al::isActionEnd(this, nullptr)) {
al::setNerve(this, &nrvSpeedbootLoadDecrease);
}
}
void SpeedbootLoad::exeDecrease() {
mTime += 0.016666f;
mProgression = (
mAutoCloseAfter <= 0.1f
? worldResourceLoader->calcLoadPercent() / 100.0f
: mTime / mAutoCloseAfter
);
float rotation = cosf(mTime) * 3;
// Debug stuff
sead::WFormatFixedSafeString<0x40> string(u"Time: %.02f\nSine Value: %.02f", mTime, rotation);
al::setPaneString(this, "TxtDebug", string.cstr(), 0);
if (mProgression < 1.f) {
// Target setup
if (
(mAutoCloseAfter <= 0.1f && mTime < 7.f)
|| (mAutoCloseAfter > 0.1f && mTime < (mAutoCloseAfter * 0.5f))
) {
mOnlineLogoTransTarget = { 0.f, 0.f };
mOnlineLogoScaleTarget = 1.f;
mOnlineCreditScaleTarget = 1.f;
mFreezeLogoTransXTarget = 1000.f;
mFreezeBorderTarget = sead::Vector2f(700.f, 420.f);
mFreezeBGTransXTarget = 0.f;
} else {
mOnlineLogoTransTarget = { -520.f, 260.f };
mOnlineLogoScaleTarget = 0.3f;
mOnlineCreditScaleTarget = 0.f;
mFreezeLogoTransXTarget = 0.f;
mFreezeBorderTarget = sead::Vector2f(640.f, 360.f);
mFreezeBGTransXTarget = -1280.f;
}
// SMOO logo
mOnlineLogoTrans.x = al::lerpValue(mOnlineLogoTrans.x, mOnlineLogoTransTarget.x, 0.06f);
mOnlineLogoTrans.y = al::lerpValue(mOnlineLogoTrans.y, mOnlineLogoTransTarget.y, 0.06f);
mOnlineLogoScale = al::lerpValue(mOnlineLogoScale, mOnlineLogoScaleTarget, 0.06f);
mOnlineCreditScale = al::lerpValue(mOnlineCreditScale, mOnlineCreditScaleTarget, 0.06f);
al::setPaneLocalTrans(this, "PartOnlineLogo", mOnlineLogoTrans);
al::setPaneLocalScale(this, "PartOnlineLogo", { mOnlineLogoScale, mOnlineLogoScale });
al::setPaneLocalScale(this, "PicCraftyBossCredit", { mOnlineCreditScale, mOnlineCreditScale });
// Freeze-Tag logo
mFreezeLogoTransX = al::lerpValue(mFreezeLogoTransX, mFreezeLogoTransXTarget, 0.04f);
al::setPaneLocalTrans(this, "FreezeLogoRoot", { mFreezeLogoTransX, 0.f });
al::setPaneLocalRotate(this, "PicFreezeLogo", { 0.f, 0.f, rotation });
// Freeze borders
mFreezeBorder.x = al::lerpValue(mFreezeBorder.x, mFreezeBorderTarget.x, 0.02f);
mFreezeBorder.y = al::lerpValue(mFreezeBorder.y, mFreezeBorderTarget.y, 0.02f);
al::setPaneLocalTrans(this, "PicFreezeEdgeLeft", { -mFreezeBorder.x, 0.f });
al::setPaneLocalTrans(this, "PicFreezeEdgeRight", { mFreezeBorder.x, 0.f });
al::setPaneLocalTrans(this, "PicFreezeEdgeTop", { 0.f, mFreezeBorder.y });
al::setPaneLocalTrans(this, "PicFreezeEdgeBot", { 0.f, -mFreezeBorder.y });
// Freeze BG
mFreezeBGTransX = al::lerpValue(mFreezeBGTransX, mFreezeBGTransXTarget, 0.04f);
al::setPaneLocalTrans(this, "BackgroundsRoot", { mFreezeBGTransX, 0.f });
}
if (mProgression >= 1.f) {
al::setNerve(this, &nrvSpeedbootLoadEnd);
}
}
void SpeedbootLoad::exeEnd() {
if (al::isFirstStep(this)) {
al::startAction(this, "End", nullptr);
}
if (al::isActionEnd(this, nullptr)) {
kill();
}
}
namespace {
NERVE_IMPL(SpeedbootLoad, Appear)
NERVE_IMPL(SpeedbootLoad, Wait)
NERVE_IMPL(SpeedbootLoad, Decrease)
NERVE_IMPL(SpeedbootLoad, End)
}
}