Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example level improvements #311

Merged
merged 17 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified Mods/ExampleMod/Content/Maps/ExampleLevel/ExampleLevel.umap
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12 changes: 11 additions & 1 deletion Source/FactoryGame/Private/FGDropPod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,17 @@ void AFGDropPod::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifeti
}
void AFGDropPod::BeginPlay(){ Super::BeginPlay(); }
void AFGDropPod::EndPlay(const EEndPlayReason::Type EndPlayReason){ Super::EndPlay(EndPlayReason); }
void AFGDropPod::PreSave(FObjectPreSaveContext SaveContext){ Super::PreSave(SaveContext); }
void AFGDropPod::PreSave(FObjectPreSaveContext SaveContext)
{
Super::PreSave(SaveContext);
#if WITH_EDITOR
// Cache scannable data from the world during the cooking process
// Avoid attempting to cache the data on the CDOs and Archetypes, and objects without a world context
if (SaveContext.IsCooking() && !HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject) && GetWorld() != nullptr) {
mDropPodGuid = GetActorGuid();
}
#endif
}
void AFGDropPod::GainedSignificance_Implementation(){ }
void AFGDropPod::LostSignificance_Implementation(){ }
void AFGDropPod::GetConditionalReplicatedProps(TArray<FFGCondReplicatedProperty>& outProps) const{ }
Expand Down
55 changes: 51 additions & 4 deletions Source/FactoryGame/Private/FGGasPillarCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,57 @@
#include "Net/UnrealNetwork.h"

#if WITH_EDITOR
void AFGGasPillarCloud::GatherNearbyPillarLocations(){ }
void AFGGasPillarCloud::NotifyGasPillarRemovedFromInfluence( AFGGasPillar* gasPillar){ }
void AFGGasPillarCloud::DebugDrawCurrentPillarLocations(){ }
#endif
#include "FGGasPillar.h"
#include "EngineUtils.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Framework/Notifications/NotificationManager.h"
#include "Widgets/Notifications/SNotificationList.h"
#endif

#if WITH_EDITOR
static void ShowPostEditNotification(const FString& Message, SNotificationItem::ECompletionState State)
{
FNotificationInfo Info(FText::FromString(Message));
Info.ExpireDuration = 5.0f;
Info.bUseLargeFont = false;
TSharedPtr<SNotificationItem> Notification = FSlateNotificationManager::Get().AddNotification(Info);
if (Notification.IsValid()) {
Notification->SetCompletionState(State);
}
}

void AFGGasPillarCloud::GatherNearbyPillarLocations()
{
mProximityPillarWorldLocations.Empty();
for (TActorIterator<AFGGasPillar> ActorItr(GetWorld()); ActorItr; ++ActorItr) {
if (AFGGasPillar* gasPillar = *ActorItr) {
if (GetDistanceTo(gasPillar) <= mOverlapRadius) {
mProximityPillarWorldLocations.Add(gasPillar->GetActorLocation() + FVector(0, 0, gasPillar->GetEffectHeightOffset()));
gasPillar->SetNearbyGasCloud(this);
gasPillar->MarkPackageDirty();
}
}
}
const FString Message = FString::Printf(TEXT("Found %d gas pillars around gas cloud '%s'"), mProximityPillarWorldLocations.Num(), *GetName());
ShowPostEditNotification(Message, SNotificationItem::CS_Success);
this->MarkPackageDirty();
}

void AFGGasPillarCloud::NotifyGasPillarRemovedFromInfluence(AFGGasPillar* gasPillar)
{
if (gasPillar) {
mProximityPillarWorldLocations.Remove(gasPillar->GetActorLocation() + FVector(0, 0, gasPillar->GetEffectHeightOffset()));
this->MarkPackageDirty();
}
}

void AFGGasPillarCloud::DebugDrawCurrentPillarLocations()
{
for (const FVector& Vec : mProximityPillarWorldLocations) {
UKismetSystemLibrary::DrawDebugArrow(this, FVector(0, 0, 600) + Vec, Vec, 10000, FLinearColor::Red, 10, 20);
}
}
#endif
void AFGGasPillarCloud::GainedSignificance_Implementation(){ }
void AFGGasPillarCloud::LostSignificance_Implementation(){ }
AFGGasPillarCloud::AFGGasPillarCloud() : Super() {
Expand Down
12 changes: 11 additions & 1 deletion Source/FactoryGame/Private/FGItemPickup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,17 @@ void AFGItemPickup::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLif
void AFGItemPickup::Serialize(FArchive& ar){ Super::Serialize(ar); }
void AFGItemPickup::BeginPlay(){ Super::BeginPlay(); }
void AFGItemPickup::EndPlay(const EEndPlayReason::Type EndPlayReason){ Super::EndPlay(EndPlayReason); }
void AFGItemPickup::PreSave(FObjectPreSaveContext SaveContext){ Super::PreSave(SaveContext); }
void AFGItemPickup::PreSave(FObjectPreSaveContext SaveContext)
{
Super::PreSave(SaveContext);
#if WITH_EDITOR
// Cache scannable data from the world during the cooking process
// Avoid attempting to cache the data on the CDOs and Archetypes, and objects without a world context
if (SaveContext.IsCooking() && !HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject) && GetWorld() != nullptr) {
mItemPickupGuid = GetActorGuid();
}
#endif
}
void AFGItemPickup::PreSaveGame_Implementation(int32 saveVersion, int32 gameVersion){ }
void AFGItemPickup::PostSaveGame_Implementation(int32 saveVersion, int32 gameVersion){ }
void AFGItemPickup::PreLoadGame_Implementation(int32 saveVersion, int32 gameVersion){ }
Expand Down
158 changes: 150 additions & 8 deletions Source/FactoryGame/Private/FGWorldScannableData.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,159 @@
// This file has been automatically generated by the Unreal Header Implementation tool
// Copyright Coffee Stain Studios. All Rights Reserved.

#include "FGWorldScannableData.h"
#include "UObject/ObjectSaveContext.h"
#include "EngineUtils.h"
#include "Buildables/FGBuildableFrackingActivator.h"
#include "FGScannableSubsystem.h"
#include "FGItemPickup.h"
#include "FGDropPod.h"
#include "WorldPartition/WorldPartition.h"
#if WITH_EDITOR
#include "WorldPartition/WorldPartitionActorDesc.h"
#endif

Th3Fanbus marked this conversation as resolved.
Show resolved Hide resolved
#if WITH_EDITOR
FWorldScannableData::FWorldScannableData(const AActor* actor) { }
FWorldScannableData::FWorldScannableData(const class FWorldPartitionActorDesc* ActorDesc, int32 PIEInstanceIndex) { }

FWorldScannableData::FWorldScannableData(const AActor* actor)
{
if (actor) {
Actor = TSoftObjectPtr<AActor>(actor);
ActorGuid = actor->GetActorGuid();
ActorClass = actor->GetClass();
ActorLocation = actor->GetStreamingBounds().GetCenter();
}
}

FWorldScannableData::FWorldScannableData(const FWorldPartitionActorDesc* ActorDesc, int32 PIEInstanceIndex)
{
check(ActorDesc);
Actor = ActorDesc->GetActorSoftPath();
ActorGuid = ActorDesc->GetGuid();

// If we have a base class path, e.g. class is not native, load it
const FTopLevelAssetPath BaseClassPath = ActorDesc->GetBaseClass();
if (!BaseClassPath.IsNull()) {
ActorClass = LoadClass<AActor>(nullptr, *ActorDesc->GetBaseClass().ToString());
} else {
ActorClass = ActorDesc->GetActorNativeClass();
}
// Should always have either a native class or a blueprint loaded class at this point
check(ActorClass);

ActorLocation = ActorDesc->GetRuntimeBounds().GetCenter();

// We need to remap the actor soft path to the correct PIE instance, since actor desc will most certainly come from the original, non-duplicated world package
if (PIEInstanceIndex != INDEX_NONE) {
FSoftObjectPath SoftActorPath = Actor.ToSoftObjectPath();
SoftActorPath.FixupForPIE(PIEInstanceIndex);
Actor = SoftActorPath;
}
}

#endif
AFGWorldScannableDataGenerator::AFGWorldScannableDataGenerator() : Super() {

AFGWorldScannableDataGenerator::AFGWorldScannableDataGenerator()
{
PrimaryActorTick.bCanEverTick = false;

#if WITH_EDITORONLY_DATA
bIsSpatiallyLoaded = false;
#endif
}
void AFGWorldScannableDataGenerator::BeginPlay(){ }
void AFGWorldScannableDataGenerator::PreSave(FObjectPreSaveContext SaveContext){ Super::PreSave(SaveContext); }

void AFGWorldScannableDataGenerator::BeginPlay()
{
Super::BeginPlay();

#if WITH_EDITOR
void AFGWorldScannableDataGenerator::CacheWorldScannableData(){ }
// Cache scannable data from the world if we have editor only data
if (!GetPackage()->HasAnyPackageFlags(PKG_Cooked | PKG_FilterEditorOnly)) {
CacheWorldScannableData();
}
#endif

if (AFGScannableSubsystem* scannableSubsystem = AFGScannableSubsystem::Get(GetWorld())) {
scannableSubsystem->AssignScannableData(mItemPickups, mDropPods);
}
}

void AFGWorldScannableDataGenerator::PreSave(FObjectPreSaveContext SaveContext)
{
Super::PreSave(SaveContext);
#if WITH_EDITOR
// Cache scannable data from the world during the cooking process
// Avoid attempting to cache the data on the CDOs and Archetypes, and objects without a world context
if (SaveContext.IsCooking() && !HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject) && GetWorld() != nullptr) {
CacheWorldScannableData();
}
#endif
}

#if WITH_EDITOR

void AFGWorldScannableDataGenerator::CacheWorldScannableData()
{
mItemPickups.Empty();
mDropPods.Empty();

const UWorldPartition* WorldPartition = GetWorld()->GetWorldPartition();

// If there is no world partition, use TActorIterator to find all loaded actors and cache them immediately
if (WorldPartition == nullptr) {
for (TActorIterator<AFGItemPickup> It(GetWorld()); It; ++It) {
mItemPickups.Add(FWorldScannableData(*It));
}
for (TActorIterator<AFGDropPod> It(GetWorld()); It; ++It) {
mDropPods.Add(FWorldScannableData(*It));
}
return;
}

// This is a world partitioned level, so we need to use world partition actor iterator to find all actors. We do not actually need to load them to retrieve their data
check(WorldPartition);
UActorDescContainer* ActorDescContainer = WorldPartition->GetActorDescContainer();

// Determine the path to the source asset in case we need to remap paths to PIE
FString SourceAssetPath, UnusedRemappedPath;
const bool bWorldHasBeenInstanced = GetWorld()->GetSoftObjectPathMapping(SourceAssetPath, UnusedRemappedPath);
const FTopLevelAssetPath SourceAssetTopLevelPath(SourceAssetPath);

// In PIE, if the world has been duplicated, it is likely that it's ActorDescContainer will not be initialized
// Attempt to look up our actor desc container on our source world asset
if (ActorDescContainer == nullptr && bWorldHasBeenInstanced) {
if (const UWorld* OriginalWorldAsset = FindObject<UWorld>(SourceAssetTopLevelPath)) {
const UWorldPartition* OriginalWorldPartition = OriginalWorldAsset->GetWorldPartition();
if (OriginalWorldPartition != nullptr) {
ActorDescContainer = OriginalWorldPartition->GetActorDescContainer();
}
}
}
// If we were unable to find the original world (it could have been garbage collected by now, or never had it's actor desc initialized),
// create our own actor desc container using the original asset path
if (ActorDescContainer == nullptr) {
ActorDescContainer = NewObject<UActorDescContainer>(GetTransientPackage());
ActorDescContainer->Initialize(UActorDescContainer::FInitializeParams(GetWorld(), SourceAssetTopLevelPath.GetPackageName()));
}

// At this point we should always have a valid actor desc container, but ensure and not crash if we do not
if (!ensure(ActorDescContainer)) {
UE_LOG(LogGame, Error, TEXT("Failed to find ActorDescContainer for World Partitioned world '%s' to gather scannables. Game features relying on the WorldScannableData will not work."), *GetWorld()->GetPathName());
return;
}

// We have to fix up the path in case this is a PIE instance
const int32 PIEInstanceIndex = GetWorld()->GetPackage()->GetPIEInstanceID();

for (FActorDescList::TConstIterator<AActor> It(ActorDescContainer); It; ++It) {
const FWorldPartitionActorDesc* ActorDescriptor = *It;
const UClass* ActorNativeClass = ActorDescriptor->GetActorNativeClass();

if (ActorNativeClass->IsChildOf(AFGItemPickup::StaticClass())) {
mItemPickups.Add(FWorldScannableData(*It, PIEInstanceIndex));
}
if (ActorNativeClass->IsChildOf(AFGDropPod::StaticClass())) {
mDropPods.Add(FWorldScannableData(*It, PIEInstanceIndex));
}
}
}

#endif
Loading