Npc: Implement FlyObject (#969)

This commit is contained in:
Narr the Reg 2026-04-03 05:37:50 -06:00 committed by GitHub
parent a3ed1588c4
commit 82fae85d34
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 414 additions and 39 deletions

View file

@ -101076,156 +101076,156 @@ Npc/FlyObject.o:
label:
- _ZN22WaveMovementControllerC1Ev
- _ZN22WaveMovementControllerC2Ev
status: NotDecompiled
status: Matching
- offset: 0x3898e4
size: 132
label: _ZN22WaveMovementController17calcUpdatedYCoordERKN4sead7Vector3IfEE
status: NotDecompiled
status: Matching
- offset: 0x389968
size: 84
label: _ZN7OnRails5setUpEPN2al9LiveActorE
status: NotDecompiled
status: Matching
- offset: 0x3899bc
size: 344
label: _ZN7OnRails4moveEPN2al9LiveActorEf
status: NotDecompiled
status: Matching
- offset: 0x389b14
size: 60
label: _ZN10Stationary5setUpEPN2al9LiveActorE
status: NotDecompiled
status: Matching
- offset: 0x389b50
size: 140
label: _ZN10Stationary4moveEPN2al9LiveActorEf
status: NotDecompiled
status: Matching
- offset: 0x389bdc
size: 32
label: _ZN23FukanKunInteractionBaseC2Ei
status: NotDecompiled
status: Matching
- offset: 0x389bfc
size: 8
label: _ZN23FukanKunInteractionBase4initEP9FlyObjectRKN2al13ActorInitInfoE
status: NotDecompiled
status: Matching
- offset: 0x389c04
size: 24
label: _ZN23FukanKunInteractionBase5setUpEP9FlyObject
status: NotDecompiled
status: Matching
- offset: 0x389c1c
size: 96
label: _ZN23FukanKunInteractionBase7controlEP9FlyObject
status: NotDecompiled
status: Matching
- offset: 0x389c7c
size: 144
label:
- _ZN21FukanKunMessageHolderC1Ev
- _ZN21FukanKunMessageHolderC2Ev
status: NotDecompiled
status: Matching
- offset: 0x389d0c
size: 300
label: _ZN21FukanKunMessageHolder4initEP9FlyObjectRKN2al13ActorInitInfoE
status: NotDecompiled
status: Matching
- offset: 0x389e38
size: 72
label: _ZN21FukanKunMessageHolder8interactEP9FlyObject
status: NotDecompiled
status: Matching
- offset: 0x389e80
size: 40
label:
- _ZN19FukanKunShineHolderC1Ev
- _ZN19FukanKunShineHolderC2Ev
status: NotDecompiled
status: Matching
- offset: 0x389ea8
size: 60
label: _ZN19FukanKunShineHolder4initEP9FlyObjectRKN2al13ActorInitInfoE
status: NotDecompiled
status: Matching
- offset: 0x389ee4
size: 8
label: _ZN19FukanKunShineHolder8interactEP9FlyObject
status: NotDecompiled
status: Matching
- offset: 0x389eec
size: 156
label: _ZN9FlyObjectC2EPKc
status: NotDecompiled
status: Matching
- offset: 0x389f88
size: 152
label: _ZN9FlyObjectC1EPKc
status: NotDecompiled
status: Matching
- offset: 0x38a020
size: 684
label: _ZN9FlyObject4initERKN2al13ActorInitInfoE
status: NotDecompiled
status: NonMatchingMajor
- offset: 0x38a2cc
size: 64
label: _ZN9FlyObject18initAfterPlacementEv
status: NotDecompiled
status: Matching
- offset: 0x38a30c
size: 24
label: _ZN9FlyObject7controlEv
status: NotDecompiled
status: Matching
- offset: 0x38a324
size: 16
label: _ZNK9FlyObject16getMessageSystemEv
status: NotDecompiled
status: Matching
- offset: 0x38a334
size: 16
label: _ZThn264_NK9FlyObject16getMessageSystemEv
status: NotDecompiled
status: Matching
- offset: 0x38a344
size: 72
label: _ZN9FlyObject6exeFlyEv
status: NotDecompiled
status: Matching
- offset: 0x38a38c
size: 12
label: _ZN9FlyObject12exeDisappearEv
status: NotDecompiled
status: Matching
- offset: 0x38a398
size: 4
label: _ZN7OnRailsD0Ev
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x38a39c
size: 4
label: _ZN10StationaryD0Ev
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x38a3a0
size: 8
label: _ZNK24FukanKunInteractionEmpty16getMessageSystemEv
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x38a3a8
size: 8
label: _ZNK21FukanKunMessageHolder16getMessageSystemEv
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x38a3b0
size: 16
label: _ZNK12_GLOBAL__N_121FlyObjectNrvDisappear7executeEPN2al11NerveKeeperE
status: NotDecompiled
status: Matching
guess: true
- offset: 0x38a3c0
size: 4
label: _ZN24FukanKunInteractionEmpty4initEP9FlyObjectRKN2al13ActorInitInfoE
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x38a3c4
size: 4
label: _ZN24FukanKunInteractionEmpty5setUpEP9FlyObject
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x38a3c8
size: 4
label: _ZN24FukanKunInteractionEmpty7controlEP9FlyObject
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x38a3cc
size: 4
label: _ZN8BehaviorD2Ev
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x38a3d0
size: 76
label: _ZNK12_GLOBAL__N_115FlyObjectNrvFly7executeEPN2al11NerveKeeperE
status: NotDecompiled
status: Matching
guess: true
Npc/ForestManSeed.o:
'.text':
@ -134363,7 +134363,7 @@ Scene/ProjectActorFactory.o:
- offset: 0x4b8470
size: 52
label: _ZN2al19createActorFunctionI9FlyObjectEEPNS_9LiveActorEPKc
status: NotDecompiled
status: Matching
lazy: true
- offset: 0x4b84a4
size: 52

View file

@ -0,0 +1,32 @@
#pragma once
#include <basis/seadTypes.h>
#include <math/seadMatrix.h>
#include <math/seadVector.h>
namespace al {
class LiveActor;
class PlacementInfo;
class IUseSceneObjHolder;
} // namespace al
class Fukankun;
class FukankunZoomObj;
namespace FukankunZoomTargetFunction {
void declareUseFukankunZoomTargetActor(const al::LiveActor*);
void registerFukankunZoomTargetActor(const al::LiveActor*, s32, const sead::Vector3f&, const char*);
s32 getWatchCount(const al::LiveActor*);
s32 getFukankunWatchCountDefault();
f32 getFukankunCameraNearDistThres();
bool tryGetActiveFukankunLinkedShineMtx(const sead::Matrix34f**, const al::LiveActor*);
FukankunZoomObj* tryGetFukankunZoomObj(const al::IUseSceneObjHolder*, const al::PlacementInfo&);
void registerFukankunZoomObj(const al::LiveActor*, const al::PlacementInfo&);
s32 getFukankunZoomTargetActorNum(const al::IUseSceneObjHolder*);
al::LiveActor* tryGetFukankunZoomTargetActor(const al::IUseSceneObjHolder*, s32);
bool tryGetFukankunZoomTargetActorIsNoZoomOn(const al::IUseSceneObjHolder*, s32);
s32 tryGetFukankunZoomTargetActorZoomType(const al::IUseSceneObjHolder*, s32);
const sead::Vector3f& tryGetFukankunZoomTargetActorOffset(const al::IUseSceneObjHolder*, s32);
const char* tryGetFukankunZoomTargetActorTargetJointName(const al::IUseSceneObjHolder*, s32);
void declareWatchFukankunZoomTargetActor(const Fukankun*, s32);
} // namespace FukankunZoomTargetFunction

193
src/Npc/FlyObject.cpp Normal file
View file

@ -0,0 +1,193 @@
#include "Npc/FlyObject.h"
#include "Library/Base/StringUtil.h"
#include "Library/Layout/LayoutInitInfo.h"
#include "Library/LiveActor/ActorActionFunction.h"
#include "Library/LiveActor/ActorClippingFunction.h"
#include "Library/LiveActor/ActorInitUtil.h"
#include "Library/LiveActor/ActorPoseUtil.h"
#include "Library/Math/MathUtil.h"
#include "Library/Nerve/NerveSetupUtil.h"
#include "Library/Nerve/NerveUtil.h"
#include "Library/Placement/PlacementFunction.h"
#include "Library/Placement/PlacementId.h"
#include "Library/Rail/RailUtil.h"
#include "MapObj/CapMessageShowInfo.h"
#include "MapObj/FukankunZoomTargetFunction.h"
#include "Util/ItemUtil.h"
namespace {
NERVE_IMPL(FlyObject, Disappear)
NERVE_IMPL(FlyObject, Fly)
NERVES_MAKE_NOSTRUCT(FlyObject, Disappear, Fly)
} // namespace
WaveMovementController::WaveMovementController() = default;
f32 WaveMovementController::calcUpdatedYCoord(const sead::Vector3f& trans) {
if (mVerticalSpeed == 0.0f || mVerticalAmplitude == 0.0f)
return trans.y;
f32 nextAngle = mVerticalSpeed * (1.0f / 60.0f) + mVerticalAngle;
if (nextAngle > sead::Mathf::pi2())
nextAngle = 0.0f;
mVerticalAngle = nextAngle;
return trans.y + sead::Mathf::sin(mVerticalAngle) * mVerticalAmplitude;
}
void Stationary::setUp(al::LiveActor* actor) {
mTrans = al::getTrans(actor);
}
void Stationary::move(al::LiveActor* actor, f32 speed) {
al::setTransY(actor, getWaveController()->calcUpdatedYCoord(mTrans));
}
void OnRails::setUp(al::LiveActor* actor) {
al::setSyncRailToNearestRailControlPoint(actor);
mMoveSyncRail = al::isLoopRail(actor) ? al::moveSyncRailLoop : al::moveSyncRail;
}
void OnRails::move(al::LiveActor* actor, f32 speed) {
mMoveSyncRail(actor, speed);
al::setTransY(actor, getWaveController()->calcUpdatedYCoord(al::getTrans(actor)));
sead::Vector3f railDir = al::getRailDir(actor);
const sead::Vector3f& up = -al::getGravity(actor);
sead::Vector3f front = railDir - railDir.dot(up) * up;
if (al::tryNormalizeOrZero(&front))
al::setFront(actor, front);
if (al::isRailReachedEnd(actor) && !al::isLoopRail(actor))
al::setNerve(actor, &Disappear);
}
void FukanKunInteractionEmpty::init(FlyObject* flyObject, const al::ActorInitInfo& info) {}
void FukanKunInteractionEmpty::setUp(FlyObject* flyObject) {}
void FukanKunInteractionEmpty::control(FlyObject* flyObject) {}
FukanKunInteractionBase::FukanKunInteractionBase(s32 displayTime) : mDisplayTime(displayTime) {}
void FukanKunInteractionBase::init(FlyObject* flyObject, const al::ActorInitInfo& info) {
FukankunZoomTargetFunction::declareUseFukankunZoomTargetActor(flyObject);
}
void FukanKunInteractionBase::setUp(FlyObject* flyObject) {
FukankunZoomTargetFunction::registerFukankunZoomTargetActor(flyObject, 0, sead::Vector3f::zero,
nullptr);
}
void FukanKunInteractionBase::control(FlyObject* flyObject) {
if (mHasInteraction && mDisplayTime < FukankunZoomTargetFunction::getWatchCount(flyObject)) {
interact(flyObject);
mHasInteraction = false;
}
}
FukanKunMessageHolder::FukanKunMessageHolder() : FukanKunInteractionBase(180) {}
void FukanKunMessageHolder::init(FlyObject* flyObject, const al::ActorInitInfo& info) {
mMessageSystem = al::getLayoutInitInfo(info).getMessageSystem();
mCapMsg = al::StringTmp<64>("CapMsg_%s", al::createPlacementId(info)->getId());
FukanKunInteractionBase::init(flyObject, info);
}
al::MessageSystem* FukanKunMessageHolder::getMessageSystem() const {
return mMessageSystem;
}
void FukanKunMessageHolder::interact(FlyObject* flyObject) {
rs::tryShowCapMessageFromCurrentStageMsg(flyObject, mCapMsg.cstr(), 90, 0);
}
FukanKunShineHolder::FukanKunShineHolder() : FukanKunInteractionBase(120) {}
void FukanKunShineHolder::init(FlyObject* flyObject, const al::ActorInitInfo& info) {
mShine = rs::tryInitLinkShine(info, "ShineActor", 0);
FukanKunInteractionBase::init(flyObject, info);
}
void FukanKunShineHolder::interact(FlyObject* flyObject) {
rs::appearPopupShine(mShine, flyObject);
}
FlyObject::FlyObject(const char* name) : al::LiveActor(name) {}
inline FukanKunInteractionEmpty* createFukanKunInteraction(const al::ActorInitInfo& info) {
FukanKunInteractionType interactionType = FukanKunInteractionType::None;
if (al::tryGetArg((s32*)&interactionType, info, "FukanKunInteractionType")) {
switch (interactionType) {
case FukanKunInteractionType::None:
return new FukanKunInteractionEmpty();
case FukanKunInteractionType::Message:
return new FukanKunMessageHolder();
case FukanKunInteractionType::Shine:
return new FukanKunShineHolder();
default:
return nullptr;
}
}
return nullptr;
}
// NON_MATCHING: Bad load order https://decomp.me/scratch/4Q3he
void FlyObject::init(const al::ActorInitInfo& info) {
al::initActor(this, info);
if (al::isExistRail(this))
mBehavior = new OnRails();
else
mBehavior = new Stationary();
al::tryGetArg(&mMovementSpeed, info, "MovementSpeed");
al::tryGetArg(mBehavior->getWaveController()->getVerticalSpeedPtr(), info, "VerticalSpeed");
al::tryGetArg(mBehavior->getWaveController()->getVerticalAmplitudePtr(), info,
"VerticalAmplitude");
FukanKunInteractionEmpty* interaction = createFukanKunInteraction(info);
if (interaction) {
mFukanKunInteraction = interaction;
} else {
bool isFukanKunMessageNeed = false;
al::tryGetArg(&isFukanKunMessageNeed, info, "IsFukanKunMessageNeed");
if (isFukanKunMessageNeed)
mFukanKunInteraction = new FukanKunMessageHolder();
else
mFukanKunInteraction = new FukanKunInteractionEmpty();
}
mFukanKunInteraction->init(this, info);
al::initNerve(this, &Fly, 0);
al::invalidateClipping(this);
al::trySyncStageSwitchAppear(this);
}
void FlyObject::initAfterPlacement() {
mFukanKunInteraction->setUp(this);
mBehavior->setUp(this);
}
void FlyObject::control() {
mFukanKunInteraction->control(this);
}
al::MessageSystem* FlyObject::getMessageSystem() const {
return mFukanKunInteraction->getMessageSystem();
}
void FlyObject::exeFly() {
if (al::isFirstStep(this))
al::startAction(this, "Fly");
mBehavior->move(this, mMovementSpeed);
}
void FlyObject::exeDisappear() {
makeActorDead();
}

149
src/Npc/FlyObject.h Normal file
View file

@ -0,0 +1,149 @@
#pragma once
#include <prim/seadSafeString.h>
#include "Library/LiveActor/LiveActor.h"
#include "Library/Message/IUseMessageSystem.h"
namespace al {
struct ActorInitInfo;
}
class FlyObject;
class Shine;
enum class FukanKunInteractionType : s32 {
None,
Message,
Shine,
};
class WaveMovementController {
public:
WaveMovementController();
f32 calcUpdatedYCoord(const sead::Vector3f&);
f32* getVerticalSpeedPtr() { return &mVerticalSpeed; }
f32* getVerticalAmplitudePtr() { return &mVerticalAmplitude; }
private:
f32 mVerticalSpeed = 2.0f;
f32 mVerticalAmplitude = 350.0f;
f32 mVerticalAngle = 0.0f;
};
static_assert(sizeof(WaveMovementController) == 0xc);
class Behavior {
public:
virtual void setUp(al::LiveActor* actor) = 0;
virtual void move(al::LiveActor* actor, f32 speed) = 0;
virtual ~Behavior() = default;
WaveMovementController* getWaveController() { return &mWaveController; }
private:
WaveMovementController mWaveController;
};
static_assert(sizeof(Behavior) == 0x18);
class Stationary : public Behavior {
public:
void setUp(al::LiveActor* actor) override;
void move(al::LiveActor* actor, f32 speed) override;
private:
sead::Vector3f mTrans;
};
static_assert(sizeof(Stationary) == 0x20);
class OnRails : public Behavior {
public:
void setUp(al::LiveActor* actor) override;
void move(al::LiveActor* actor, f32 speed) override;
private:
using MoveSyncRail = bool(al::LiveActor*, f32);
MoveSyncRail* mMoveSyncRail = nullptr;
};
static_assert(sizeof(OnRails) == 0x20);
class FukanKunInteractionEmpty {
public:
FukanKunInteractionEmpty() = default;
virtual void init(FlyObject* flyObject, const al::ActorInitInfo& info);
virtual void setUp(FlyObject* flyObject);
virtual void control(FlyObject* flyObject);
virtual al::MessageSystem* getMessageSystem() const { return nullptr; }
};
static_assert(sizeof(FukanKunInteractionEmpty) == 0x8);
class FukanKunInteractionBase : public FukanKunInteractionEmpty {
public:
FukanKunInteractionBase(s32 displayTime);
void init(FlyObject* flyObject, const al::ActorInitInfo& info) override;
void setUp(FlyObject* flyObject) override;
void control(FlyObject* flyObject) override;
virtual void interact(FlyObject* flyObject) = 0;
private:
bool mHasInteraction = true;
s32 mDisplayTime;
};
static_assert(sizeof(FukanKunInteractionBase) == 0x10);
class FukanKunMessageHolder : public FukanKunInteractionBase {
public:
FukanKunMessageHolder();
void init(FlyObject* flyObject, const al::ActorInitInfo& info) override;
al::MessageSystem* getMessageSystem() const override;
void interact(FlyObject* flyObject) override;
private:
al::MessageSystem* mMessageSystem = nullptr;
sead::FixedSafeString<64> mCapMsg;
};
static_assert(sizeof(FukanKunMessageHolder) == 0x70);
class FukanKunShineHolder : public FukanKunInteractionBase {
public:
FukanKunShineHolder();
void init(FlyObject* flyObject, const al::ActorInitInfo& info) override;
void interact(FlyObject* flyObject) override;
private:
Shine* mShine = nullptr;
};
static_assert(sizeof(FukanKunShineHolder) == 0x18);
class FlyObject : public al::LiveActor, public al::IUseMessageSystem {
public:
FlyObject(const char* name);
void init(const al::ActorInitInfo& info) override;
void initAfterPlacement() override;
void control() override;
al::MessageSystem* getMessageSystem() const override;
void exeFly();
void exeDisappear();
private:
Behavior* mBehavior = nullptr;
f32 mMovementSpeed = 22.0f;
FukanKunInteractionEmpty* mFukanKunInteraction = nullptr;
};
static_assert(sizeof(FlyObject) == 0x128);

View file

@ -105,6 +105,7 @@
#include "MapObj/WorldWarpHole.h"
#include "Npc/Bird.h"
#include "Npc/BirdPlayerGlideCtrl.h"
#include "Npc/FlyObject.h"
#include "Npc/KuriboGirl.h"
#include "Npc/RaceAudienceNpc.h"
#include "Npc/VocalMike.h"
@ -303,7 +304,7 @@ const al::NameToCreator<al::ActorCreatorFunction> sProjectActorFactoryEntries[]
{"FixMapPartsForceSafetyPoint", nullptr},
{"FixMapPartsFukankunZoomCapMessage", nullptr},
{"FixMapPartsScenarioAction", nullptr},
{"FlyObject", nullptr},
{"FlyObject", al::createActorFunction<FlyObject>},
{"ForestManSeed", nullptr},
{"ForestWorldHomeBreakParts000", nullptr},
{"FogRequester", nullptr},

View file

@ -446,8 +446,8 @@ def header_check_line(line, path, visibility, should_start_class, is_in_struct):
var_name = newline.split(" : ")[0].split(" ")[-1]
var_type = " ".join(newline.split(" ")[0:-1])
if var_type.startswith("enum") or var_type.startswith("friend"):
return # Allow enum and friend class
if var_type.startswith("enum") or var_type.startswith("friend") or var_type.startswith("using"):
return # Allow enum, friend class and using
PREFIXES = ["pad", "field", "unk", "gap", "_", "filler"]