#pragma once #include "CoreMinimal.h" class UClass; /** * Which pawn / controller / HUD class ADescentGameMode uses, chosen by registration instead of by editing the game * mode: the track that owns a class registers it from its own .cpp with a priority, the highest priority wins. * * // Player/DescentCharacter.cpp (M2): replaces the M0 fly pawn without touching Core/DescentGameMode.cpp * DESCENT_REGISTER_GAME_CLASS(Pawn, 100, ADescentCharacter) * // Player/DescentPlayerController.cpp * DESCENT_REGISTER_GAME_CLASS(PlayerController, 100, ADescentPlayerController) * * The placeholder fly pawn registers at priority 0 (Core/DescentFlyPawn.cpp). Registration stores a getter, not a * UClass: StaticClass() must not be called while static initialisers run. The game mode resolves the slots in its * constructor, which the engine runs after every module has loaded. */ enum class EDescentGameClassSlot : uint8 { Pawn, PlayerController, HUD, GameState, PlayerState, Count }; class THEDESCENT_API FDescentGameClassRegistry { public: using FClassGetter = TFunction; /** The registry the game mode reads. Tests make their own instance instead. */ static FDescentGameClassRegistry& Get(); void Register(EDescentGameClassSlot Slot, int32 Priority, FName Owner, FClassGetter Getter); void Unregister(EDescentGameClassSlot Slot, FName Owner); /** The class of the highest-priority registration of the slot (ties: the owner name that sorts first), or null if none. */ UClass* Resolve(EDescentGameClassSlot Slot) const; /** Owner of the registration Resolve would pick, NAME_None if none. */ FName ResolveOwner(EDescentGameClassSlot Slot) const; private: struct FEntry { EDescentGameClassSlot Slot = EDescentGameClassSlot::Pawn; int32 Priority = 0; FName Owner; FClassGetter Getter; }; const FEntry* Pick(EDescentGameClassSlot Slot) const; TArray Entries; }; struct THEDESCENT_API FDescentGameClassRegistrar { FDescentGameClassRegistrar(EDescentGameClassSlot InSlot, int32 Priority, const TCHAR* Owner, FDescentGameClassRegistry::FClassGetter Getter) : Slot(InSlot), OwnerName(Owner) { FDescentGameClassRegistry::Get().Register(Slot, Priority, OwnerName, MoveTemp(Getter)); } ~FDescentGameClassRegistrar() { FDescentGameClassRegistry::Get().Unregister(Slot, OwnerName); } EDescentGameClassSlot Slot; FName OwnerName; }; /** Registers ClassName (a UCLASS already declared) for a slot: DESCENT_REGISTER_GAME_CLASS(Pawn, 100, ADescentCharacter). */ #define DESCENT_REGISTER_GAME_CLASS(SlotName, Priority, ClassName) \ static FDescentGameClassRegistrar GDescentGameClass_##SlotName##_##ClassName( \ EDescentGameClassSlot::SlotName, (Priority), TEXT(#ClassName), []() -> UClass* { return ClassName::StaticClass(); });