Boss/Koopa: Implement KoopaStateDeadAndDemoBattleEnd

This commit is contained in:
guymakinggames 2026-04-22 09:23:58 +01:00
parent b6f947c559
commit 153cb9beb3
6 changed files with 331 additions and 19 deletions

View file

@ -3324,7 +3324,7 @@ Boss/BossForest/BossForest.o:
- offset: 0x0183d8
size: 4
label: _ZN12IUseDemoSkip22updateOnlyDemoGraphicsEv
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x0183dc
size: 8
@ -18326,79 +18326,79 @@ Boss/Koopa/KoopaStateDeadAndDemoBattleEnd.o:
label:
- _ZN30KoopaStateDeadAndDemoBattleEndC1EPN2al9LiveActorEP17KoopaDemoExecutorP8KoopaCapP17KoopaWeaponHolderS2_b
- _ZN30KoopaStateDeadAndDemoBattleEndC2EPN2al9LiveActorEP17KoopaDemoExecutorP8KoopaCapP17KoopaWeaponHolderS2_b
status: NotDecompiled
status: Matching
- offset: 0x09daa8
size: 48
label: _ZN30KoopaStateDeadAndDemoBattleEnd6appearEv
status: NotDecompiled
status: Matching
- offset: 0x09dad8
size: 72
label: _ZN30KoopaStateDeadAndDemoBattleEnd4killEv
status: NotDecompiled
status: Matching
- offset: 0x09db20
size: 52
label: _ZNK30KoopaStateDeadAndDemoBattleEnd11isFirstDemoEv
status: NotDecompiled
status: Matching
- offset: 0x09db54
size: 52
label: _ZThn32_NK30KoopaStateDeadAndDemoBattleEnd11isFirstDemoEv
status: NotDecompiled
status: Matching
- offset: 0x09db88
size: 68
label: _ZNK30KoopaStateDeadAndDemoBattleEnd16isEnableSkipDemoEv
status: NotDecompiled
status: Matching
- offset: 0x09dbcc
size: 72
label: _ZThn32_NK30KoopaStateDeadAndDemoBattleEnd16isEnableSkipDemoEv
status: NotDecompiled
status: Matching
- offset: 0x09dc14
size: 72
label: _ZN30KoopaStateDeadAndDemoBattleEnd8skipDemoEv
status: NotDecompiled
status: Matching
- offset: 0x09dc5c
size: 76
label: _ZThn32_N30KoopaStateDeadAndDemoBattleEnd8skipDemoEv
status: NotDecompiled
status: Matching
- offset: 0x09dca8
size: 160
label: _ZN30KoopaStateDeadAndDemoBattleEnd7exeDeadEv
status: NotDecompiled
status: Matching
- offset: 0x09dd48
size: 76
label: _ZN30KoopaStateDeadAndDemoBattleEnd8exeStartEv
status: NotDecompiled
status: Matching
- offset: 0x09dd94
size: 120
label: _ZN30KoopaStateDeadAndDemoBattleEnd7exeDemoEv
status: NotDecompiled
status: Matching
- offset: 0x09de0c
size: 4
label: _ZN30KoopaStateDeadAndDemoBattleEnd7exeSkipEv
status: NotDecompiled
status: Matching
- offset: 0x09de10
size: 36
label: _ZN30KoopaStateDeadAndDemoBattleEndD0Ev
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x09de34
size: 8
label: _ZNK12_GLOBAL__N_137KoopaStateDeadAndDemoBattleEndNrvDead7executeEPN2al11NerveKeeperE
status: NotDecompiled
status: Matching
guess: true
- offset: 0x09de3c
size: 8
label: _ZNK12_GLOBAL__N_137KoopaStateDeadAndDemoBattleEndNrvDemo7executeEPN2al11NerveKeeperE
status: NotDecompiled
status: Matching
guess: true
- offset: 0x09de44
size: 4
label: _ZNK12_GLOBAL__N_137KoopaStateDeadAndDemoBattleEndNrvSkip7executeEPN2al11NerveKeeperE
status: NotDecompiled
status: Matching
guess: true
- offset: 0x09de48
size: 76
label: _ZNK12_GLOBAL__N_138KoopaStateDeadAndDemoBattleEndNrvStart7executeEPN2al11NerveKeeperE
status: NotDecompiled
status: Matching
guess: true
Boss/Koopa/KoopaStateDemoBattleStart.o:
'.text':

84
src/Boss/Koopa/KoopaCap.h Normal file
View file

@ -0,0 +1,84 @@
#pragma once
#include <basis/seadTypes.h>
#include <math/seadVector.h>
#include "Library/LiveActor/LiveActor.h"
namespace al {
struct ActorInitInfo;
class HitSensor;
class LiveActor;
class SensorMsg;
} // namespace al
class KoopaCapPlayerBinder;
class KoopaCap : public al::LiveActor {
public:
static KoopaCap* create(const al::LiveActor*, const al::ActorInitInfo&, KoopaCapPlayerBinder*,
bool);
KoopaCap(const char*, const al::LiveActor*);
void init(const al::ActorInitInfo&) override;
void makeActorDead() override;
void appear() override;
void kill() override;
void disappear();
void control() override;
void updateCollider() override;
void attackSensor(al::HitSensor* self, al::HitSensor* other) override;
bool receiveMsg(const al::SensorMsg* message, al::HitSensor* other,
al::HitSensor* self) override;
bool isEquip() const;
bool isWaitHover() const;
bool isDown() const;
bool isEndWaitHoverStart() const;
bool isSpinMove() const;
bool isAttach() const;
bool isNecessaryUpdateAttachQT() const;
bool isFlyBack() const;
bool isEndFlyBack() const;
bool isDownRecover() const;
bool isPlayingCatchDemo() const;
bool isPlayerBinding() const;
void startDemo();
void endDemo();
void startAttach(const char*);
void startWaitHover(s32);
void startSpinThrowChase(const sead::Vector3f*, f32, bool);
void startDownRecover();
void forceStartFlyBackIfNearPlayerToKoopa();
void requestStartAction(const char*);
void onFinish();
void offFinish();
void setFastPunchRate(f32);
void endEquipAndBlowDown();
void endEquipAndKill();
void exeDemo();
void exeAppear();
void exeWait();
void exeAttach();
void exeWaitHoverDelay();
void exeWaitHoverStart();
void exeWaitHover();
void exeSpinThrow();
void exeFlyBack();
void exeFlyBackAfter();
void exeSpinHit();
void exeDownJumpStart();
void exeDownJump();
void exeDownJumpEnd();
void exeDown();
void exeDownRecoverStart();
void exeDownRecover();
void exeDownRecoverEnd();
void exeEquip();
private:
const al::LiveActor* mOwner = nullptr;
u8 _110[0x60];
};
static_assert(sizeof(KoopaCap) == 0x170);

View file

@ -0,0 +1,44 @@
#pragma once
#include <basis/seadTypes.h>
namespace al {
struct ActorInitInfo;
class AddDemoInfo;
class EventFlowExecutor;
class LiveActor;
} // namespace al
class ChurchDoor;
class IUseDemoSkip;
class KoopaCameraCtrl;
class KoopaCap;
class KoopaShip;
class Peach;
class KoopaDemoExecutor {
public:
KoopaDemoExecutor();
void init(al::LiveActor*, const al::ActorInitInfo&, al::EventFlowExecutor*, KoopaCameraCtrl*,
Peach*);
void initLv1(const al::ActorInitInfo&, al::LiveActor*, KoopaCap*, KoopaShip*);
void registerDemoModel(al::LiveActor*);
void initMoonChurch(const al::ActorInitInfo&, ChurchDoor*, al::LiveActor*);
void initLv2(const al::ActorInitInfo&, al::LiveActor*, KoopaCap*, al::LiveActor*);
bool update();
void startDemoAction(const char*, bool);
void skip();
void killAll();
bool tryStartChurchEnterDemo(IUseDemoSkip*, al::AddDemoInfo*);
void start(const char*);
bool tryStartChurchStartDemo(IUseDemoSkip*, al::AddDemoInfo*);
bool tryStartBattleStartDemo(IUseDemoSkip*);
bool tryStartBattleEndDemo(IUseDemoSkip* demoSkip);
bool tryStartClashBasementDemo(IUseDemoSkip*);
private:
u8 _0[0x120];
};
static_assert(sizeof(KoopaDemoExecutor) == 0x120);

View file

@ -0,0 +1,107 @@
#include "Boss/Koopa/KoopaStateDeadAndDemoBattleEnd.h"
#include "Library/LiveActor/ActorActionFunction.h"
#include "Library/LiveActor/ActorMovementFunction.h"
#include "Library/LiveActor/ActorSensorUtil.h"
#include "Library/Nerve/NerveSetupUtil.h"
#include "Library/Nerve/NerveUtil.h"
#include "Boss/BossUtil/BossUtil.h"
#include "Boss/Koopa/KoopaCap.h"
#include "Boss/Koopa/KoopaDemoExecutor.h"
#include "Boss/Koopa/KoopaFunction.h"
#include "Boss/Koopa/KoopaWeaponHolder.h"
#include "Util/DemoUtil.h"
namespace {
IUseDemoSkip* getDemoSkip(KoopaStateDeadAndDemoBattleEnd* state) {
return state;
}
NERVE_IMPL(KoopaStateDeadAndDemoBattleEnd, Dead);
NERVE_IMPL(KoopaStateDeadAndDemoBattleEnd, Demo);
NERVE_IMPL(KoopaStateDeadAndDemoBattleEnd, Skip);
NERVE_IMPL(KoopaStateDeadAndDemoBattleEnd, Start);
NERVES_MAKE_NOSTRUCT(KoopaStateDeadAndDemoBattleEnd, Dead, Demo, Skip, Start);
} // namespace
KoopaStateDeadAndDemoBattleEnd::KoopaStateDeadAndDemoBattleEnd(
al::LiveActor* actor, KoopaDemoExecutor* demoExecutor, KoopaCap* cap,
KoopaWeaponHolder* weaponHolder, al::LiveActor* demoActor, bool isKoopaLv2)
: al::ActorStateBase("死亡~戦闘終了デモ", actor), mDemoExecutor(demoExecutor), mCap(cap),
mWeaponHolder(weaponHolder), mDemoActor(demoActor), mIsKoopaLv2(isKoopaLv2) {
initNerve(&Dead, 0);
}
void KoopaStateDeadAndDemoBattleEnd::appear() {
NerveStateBase::appear();
al::setNerve(this, &Dead);
al::invalidateHitSensors(mActor);
}
void KoopaStateDeadAndDemoBattleEnd::kill() {
NerveStateBase::kill();
rs::requestEndDemoWithPlayer(mActor);
if (mIsKoopaLv2)
rs::saveShowDemoBossBattleEndKoopaLv2(mActor);
mDemoActor->makeActorDead();
}
bool KoopaStateDeadAndDemoBattleEnd::isFirstDemo() const {
if (mIsKoopaLv2)
return rs::isAlreadyShowDemoBossBattleEndKoopaLv2(mActor) ^ 1;
return true;
}
bool KoopaStateDeadAndDemoBattleEnd::isEnableSkipDemo() const {
if (al::isNerve(this, &Demo))
return al::isFirstStep(this) ^ 1;
return false;
}
void KoopaStateDeadAndDemoBattleEnd::skipDemo() {
mDemoExecutor->skip();
KoopaFunction::onSwitchGraphicsLevelBattleEnd(mActor);
al::setNerve(this, &Skip);
kill();
}
void KoopaStateDeadAndDemoBattleEnd::exeDead() {
if (al::isFirstStep(this)) {
al::startAction(mActor, "Die");
al::setVelocityZero(mActor);
}
if (al::isStep(this, 1))
al::startHitReaction(mActor, "ダメージとどめ");
if (al::isActionEnd(mActor)) {
KoopaStateDeadAndDemoBattleEnd* demoSkip = this;
if (mDemoExecutor->tryStartBattleEndDemo(demoSkip)) {
al::setNerve(this, &Demo);
return;
}
al::setNerve(this, &Start);
}
}
void KoopaStateDeadAndDemoBattleEnd::exeStart() {
if (mDemoExecutor->tryStartBattleEndDemo(getDemoSkip(this)))
al::setNerve(this, &Demo);
}
void KoopaStateDeadAndDemoBattleEnd::exeDemo() {
if (al::isFirstStep(this)) {
mCap->endEquipAndKill();
mWeaponHolder->makeActorDeadAll();
KoopaFunction::offSwitchBattleKeepOn(mActor);
}
if (al::isGreaterEqualStep(this, 436))
KoopaFunction::onSwitchGraphicsLevelBattleEnd(mActor);
if (mDemoExecutor->update())
kill();
}
void KoopaStateDeadAndDemoBattleEnd::exeSkip() {}

View file

@ -0,0 +1,40 @@
#pragma once
#include "Library/Nerve/NerveStateBase.h"
#include "Demo/IUseDemoSkip.h"
namespace al {
class LiveActor;
}
class KoopaCap;
class KoopaDemoExecutor;
class KoopaWeaponHolder;
class KoopaStateDeadAndDemoBattleEnd : public al::ActorStateBase, public IUseDemoSkip {
public:
KoopaStateDeadAndDemoBattleEnd(al::LiveActor* actor, KoopaDemoExecutor* demoExecutor,
KoopaCap* cap, KoopaWeaponHolder* weaponHolder,
al::LiveActor* demoActor, bool isKoopaLv2);
void appear() override;
void kill() override;
bool isFirstDemo() const override;
bool isEnableSkipDemo() const override;
void skipDemo() override;
void exeDead();
void exeStart();
void exeDemo();
void exeSkip();
private:
KoopaDemoExecutor* mDemoExecutor = nullptr;
KoopaCap* mCap = nullptr;
KoopaWeaponHolder* mWeaponHolder = nullptr;
al::LiveActor* mDemoActor = nullptr;
bool mIsKoopaLv2 = false;
};
static_assert(sizeof(KoopaStateDeadAndDemoBattleEnd) == 0x50);

View file

@ -0,0 +1,37 @@
#pragma once
#include <basis/seadTypes.h>
namespace al {
struct ActorInitInfo;
class LiveActor;
} // namespace al
class KoopaCap;
class KoopaWeaponHolder {
public:
static KoopaWeaponHolder* create(const al::LiveActor*, const al::ActorInitInfo&, s32);
KoopaWeaponHolder();
void makeActorDeadAll();
void killAll();
void cancelAll();
void* findDeadDamageBall() const;
void* findDeadDamageBallBomb() const;
void disappearAllDummyCap();
void killAllDummyCapWaitHover();
bool isAllAliveDummyCapWaitHover() const;
KoopaCap* getDummyCap(s32) const;
KoopaCap* findDeadDummyCap() const;
void resetWeaponItemGenerateLimitByDamage();
private:
void* mItemHolder = nullptr;
void* mRingBeamEmitter = nullptr;
void* mWeaponCapGroup = nullptr;
void* mDamageBallGroup = nullptr;
void* mDamageBallBombGroup = nullptr;
};
static_assert(sizeof(KoopaWeaponHolder) == 0x28);