Fix for stat forge, setup a test character

This commit is contained in:
2026-09-19 15:46:26 +02:00
parent ff35738d78
commit 1fe94cfa7a
11 changed files with 83 additions and 36 deletions

View File

@@ -170,7 +170,11 @@ bMoviesAreSkippable=False
[/Script/ProjectEleri.EleriGameSettings]
SpecialItemsMap=(((TagName="Items.Gold"), "ItemDataAsset:DA_Gold"),((TagName="Items.Crystals.Air"), "ItemDataAsset:DA_CrystalAir"),((TagName="Items.Crystals.Earth"), "ItemDataAsset:DA_CrystalEarth"),((TagName="Items.Crystals.Fire"), "ItemDataAsset:DA_CrystalFire"),((TagName="Items.Crystals.Water"), "ItemDataAsset:DA_CrystalWater"))
RemovableStaticMeshClass=/Script/Engine.BlueprintGeneratedClass'/Game/Prefabs/BP_RemovableStaticMeshActor.BP_RemovableStaticMeshActor_C'
ItemSeedCarouselClass=None
CameraZoomLevels=(((TagName="Camera.BoomOffset.Default"), (X=-1200.000000,Y=0.000000,Z=50.000000)),((TagName="Camera.BoomOffset.Cave"), (X=-800.000000,Y=0.000000,Z=40.000000)),((TagName="Camera.BoomOffset.Flying"), (X=-1500.000000,Y=0.000000,Z=60.000000)))
ExpIncreaseEffectClass=/Script/Engine.BlueprintGeneratedClass'/Game/Blueprints/GameEffects/GSE_AddExp_Template.GSE_AddExp_Template_C'
MovementDisableEffectClass=/Script/Engine.BlueprintGeneratedClass'/Game/Blueprints/GameEffects/GSE_MovementDisabled.GSE_MovementDisabled_C'
+ProfessionExpArray=100.000000
+ProfessionExpArray=500.000000
+ProfessionExpArray=1500.000000
@@ -191,15 +195,10 @@ CameraZoomLevels=(((TagName="Camera.BoomOffset.Default"), (X=-1200.000000,Y=0.00
+ProfessionExpArray=180000.000000
+ProfessionExpArray=208000.000000
+ProfessionExpArray=240000.000000
ExpIncreaseEffectClass=/Script/Engine.BlueprintGeneratedClass'/Game/Blueprints/GameEffects/GSE_AddExp_Template.GSE_AddExp_Template_C'
+NpcDataAssets=/Game/GameData/Npc/DT_Test_Npc.DT_Test_Npc
NpcSchedules=(((TagName="NpcTags.Test"), "/Game/GameData/Npc/DT_Test_Npc_Schedule.DT_Test_Npc_Schedule"))
GlobalSettingsDataSheet=/Game/GameData/CSV/GlobalKeyValueSettings.GlobalKeyValueSettings
ItemSeedCarouselClass=None
RemovableStaticMeshClass=/Script/Engine.BlueprintGeneratedClass'/Game/Prefabs/BP_RemovableStaticMeshActor.BP_RemovableStaticMeshActor_C'
MovementDisableEffectClass=/Script/Engine.BlueprintGeneratedClass'/Game/Blueprints/GameEffects/GSE_MovementDisabled.GSE_MovementDisabled_C'
SpriteShadowMaterial=/Game/Materials/Sprite/MI_SpriteShadow_Inst.MI_SpriteShadow_Inst
; NpcSchedules maps an NpcTags.* gameplay tag onto the schedule asset that drives every NPC carrying
; it. Deliberately left empty until the assets are authored, the Project Settings page writes it.
; +NpcSchedules=((TagName="NpcTags.Example"), "/Game/GameData/Npc/NpcSchedules/DA_NpcSchedule_Example.DA_NpcSchedule_Example")
[/Script/InteractionSystem.InteractionSettings]
DefaultInteractionWidgetClass=/Script/UMG.WidgetBlueprintGeneratedClass'/Game/UI/InteractionUI/WBP_Eleri_Interaction.WBP_Eleri_Interaction_C'

View File

@@ -45,6 +45,7 @@ NetIndexFirstBitSegment=16
+GameplayTagList=(Tag="Items.Crystals.Water",DevComment="")
+GameplayTagList=(Tag="Items.Gold",DevComment="")
+GameplayTagList=(Tag="NpcTags",DevComment="Identity of a scheduled NPC. Every NPC carrying the same tag is driven by the same schedule asset in NpcSchedules.")
+GameplayTagList=(Tag="NpcTags.Test",DevComment="")
+GameplayTagList=(Tag="SaveGameTags",DevComment="")
+GameplayTagList=(Tag="SaveGameTags.Alchemy.TableLevel",DevComment="")
+GameplayTagList=(Tag="SaveGameTags.Bazar.Table.InventorySpace",DevComment="")

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,28 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Data/StatForgeRegistry.h"
TArray<FStatDef>& FStatForgeRegistry::Get()
{
// Function-local static on purpose: it is created on first use, so stats declared during static
// initialization (STATFORGE_MAKE_STAT) can never touch an uninitialized registry.
static TArray<FStatDef> Registry;
return Registry;
}
void FStatForgeRegistry::RegisterStat(const FName& StatName)
{
UE_LOG(LogTemp, Verbose, TEXT("StatForge: Registering %s"), *StatName.ToString());
const bool bAlreadyRegistered = Get().ContainsByPredicate([&](const FStatDef& Def)
{
return Def.StatName == StatName;
});
if (!bAlreadyRegistered)
{
FStatDef StatDef;
StatDef.StatName = StatName;
Get().Add(StatDef);
}
}

View File

@@ -4,38 +4,35 @@
#include "CoreMinimal.h"
#include "StatForgeDefs.h"
/**
* Single, module-external registry of every stat declared with STATFORGE_MAKE_STAT / STATFORGE_MAKE_STAT_NS.
*
* The storage lives in StatForgeRegistry.cpp rather than in an inline body here on purpose: the registry has
* to be ONE instance shared by all module binaries. A function-local static inside an inline header function
* gets hidden (private-extern) visibility on macOS (-fvisibility-inlines-hidden) and is therefore duplicated
* into every module that calls it, which made the game module register its stats into a copy that
* UStatForgeDeveloperSettings::MergeRegistryIntoStatMap() - living in the plugin binary - never read.
*/
class STATFORGE_API FStatForgeRegistry
{
public:
static TArray<FStatDef>& Get()
{
static TArray<FStatDef> Registry;
return Registry;
}
static void RegisterStat(const FName& StatName)
{
UE_LOG(LogTemp, Warning, TEXT("StatForge: Registering %s"), *StatName.ToString());
const bool bAlreadyRegistered = Get().ContainsByPredicate([&](const FStatDef& Def)
{
return Def.StatName == StatName;
});
if (!bAlreadyRegistered)
{
FStatDef StatDef;
StatDef.StatName = StatName;
Get().Add(StatDef);
}
}
static TArray<FStatDef>& Get();
static void RegisterStat(const FName& StatName);
};
/**
* Linkage specifier for the FName symbols generated by the macros below. Define it before including this
* header (e.g. to PROJECTELERI_API) when the stats are declared in a module other than the one consuming
* them - otherwise those symbols are hidden on macOS and cross-module references fail to link there.
*/
#ifndef STATFORGE_STAT_API
#define STATFORGE_STAT_API
#endif
#define STATFORGE_DECLARE_STAT(StatName)\
namespace FStat\
{\
extern const FName StatName;\
extern STATFORGE_STAT_API const FName StatName;\
}\
#define STATFORGE_DECLARE_STAT_NS(Group, StatName)\
@@ -43,7 +40,7 @@ public:
{\
namespace Group\
{\
extern const FName StatName;\
extern STATFORGE_STAT_API const FName StatName;\
}\
}\

View File

@@ -1,6 +1,4 @@
#pragma once
#include "StatDefinitions.h"
STATFORGE_MAKE_STAT(Health)

View File

@@ -1,6 +1,13 @@
#pragma once
#include "CoreMinimal.h"
// These FNames are defined in this module but are meant to be usable from other modules/plugins. Without an
// export specifier they end up hidden on macOS, so any cross-module reference would break there while
// Windows kept working. PROJECTELERI_API is supplied by this module's generated Definitions header.
#define STATFORGE_STAT_API PROJECTELERI_API
#include "Data/StatForgeRegistry.h"
// -Flat Stats-

View File

@@ -42,9 +42,14 @@ ABaseCharacter::ABaseCharacter()
// Resident wherever the player is, because a schedule is driven by the clock rather than by
// proximity, and the whole point is that the NPC is already in the right place by the time the
// player arrives. Being frozen in place by the schedule component while out of range is what
// makes that affordable. Assigned as a plain member because SetIsSpatiallyLoaded() only exists in
// editor builds and this has to hold in a packaged one too.
// makes that affordable.
// In UE 5.8 this flag (and SetIsSpatiallyLoaded with it) is editor-only data on AActor, which is
// why it can only be set here and why the guard is needed for a packaged build to compile at
// all. That is enough for the intent to survive cooking: the flag is not read by the packaged
// game, the cook bakes this value into the actor descriptor that World Partition streams from.
#if WITH_EDITORONLY_DATA
bIsSpatiallyLoaded = false;
#endif
WidgetComponent = CreateDefaultSubobject<UInteractionWidgetComponent>(TEXT("WidgetComponent"));
WidgetComponent->SetupAttachment(RootComponent);