Step 1: the Salty project, two modules, tests and the gym

Salty.uproject (UE 5.8) from the Third Person template with class redirects, the SaltyCore and Salty modules,
the three build targets, USaltyAssetManager calling InitGlobalData, Config/Tags with the 23 root namespaces,
Scripts/run-tests.sh, build.sh and Authoring/create_gym.py, L_Gym, Git LFS attributes and the placeholder test.
Template variants kept as reference (D-41). The four Fab packs stay out of the repository for now (~10 GB).
The editor serves the engine MCP plugin on 127.0.0.1:8000 through DefaultEditorPerProjectUserSettings.ini.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Rainer Leit
2026-09-16 20:10:43 +03:00
co-authored by Claude Fable 5.1
parent 0e61a77346
commit 4f2c55cd2a
1434 changed files with 11358 additions and 27 deletions
@@ -0,0 +1,46 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "SideScrollingJumpPad.h"
#include "Components/BoxComponent.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Components/SceneComponent.h"
ASideScrollingJumpPad::ASideScrollingJumpPad()
{
PrimaryActorTick.bCanEverTick = false;
// create the root comp
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
// create the bounding box
Box = CreateDefaultSubobject<UBoxComponent>(TEXT("Box"));
Box->SetupAttachment(RootComponent);
// configure the bounding box
Box->SetBoxExtent(FVector(115.0f, 90.0f, 20.0f), false);
Box->SetRelativeLocation(FVector(0.0f, 0.0f, 16.0f));
Box->SetCollisionObjectType(ECC_WorldDynamic);
Box->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
Box->SetCollisionResponseToAllChannels(ECR_Ignore);
Box->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
// add the overlap handler
OnActorBeginOverlap.AddDynamic(this, &ASideScrollingJumpPad::BeginOverlap);
}
void ASideScrollingJumpPad::BeginOverlap(AActor* OverlappedActor, AActor* OtherActor)
{
// were we overlapped by a character?
if (ACharacter* OverlappingCharacter = Cast<ACharacter>(OtherActor))
{
// force the character to jump
OverlappingCharacter->Jump();
// launch the character to override its vertical velocity
FVector LaunchVelocity = FVector::UpVector * ZStrength;
OverlappingCharacter->LaunchCharacter(LaunchVelocity, false, true);
}
}
@@ -0,0 +1,39 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SideScrollingJumpPad.generated.h"
class UBoxComponent;
/**
* A simple jump pad that launches characters into the air
*/
UCLASS(abstract)
class ASideScrollingJumpPad : public AActor
{
GENERATED_BODY()
/** Jump pad bounding box */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
UBoxComponent* Box;
protected:
/** Vertical velocity to set the character to when they use the jump pad */
UPROPERTY(EditAnywhere, Category="Jump Pad", meta = (ClampMin=0, ClampMax=10000, Units="cm/s"))
float ZStrength = 1000.0f;
public:
/** Constructor */
ASideScrollingJumpPad();
protected:
UFUNCTION()
void BeginOverlap(AActor* OverlappedActor, AActor* OtherActor);
};
@@ -0,0 +1,40 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "SideScrollingMovingPlatform.h"
#include "Components/SceneComponent.h"
ASideScrollingMovingPlatform::ASideScrollingMovingPlatform()
{
PrimaryActorTick.bCanEverTick = false;
// create the root comp
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
}
void ASideScrollingMovingPlatform::Interaction(AActor* Interactor)
{
// ignore interactions if we're already moving
if (bMoving)
{
return;
}
// raise the movement flag
bMoving = true;
// pass control to BP for the actual movement
BP_MoveToTarget();
}
void ASideScrollingMovingPlatform::ResetInteraction()
{
// ignore if this is a one-shot platform
if (bOneShot)
{
return;
}
// reset the movement flag
bMoving = false;
}
@@ -0,0 +1,60 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SideScrollingInteractable.h"
#include "SideScrollingMovingPlatform.generated.h"
/**
* Simple moving platform that can be triggered through interactions by other actors.
* The actual movement is performed by Blueprint code through latent execution nodes.
*/
UCLASS(abstract)
class ASideScrollingMovingPlatform : public AActor, public ISideScrollingInteractable
{
GENERATED_BODY()
public:
/** Constructor */
ASideScrollingMovingPlatform();
protected:
/** If this is true, the platform is mid-movement and will ignore further interactions */
bool bMoving = false;
/** Destination of the platform in world space */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Moving Platform")
FVector PlatformTarget;
/** Time for the platform to move to the destination */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Moving Platform", meta = (ClampMin = 0, ClampMax = 10, Units="s"))
float MoveDuration = 5.0f;
/** If this is true, the platform will only move once. */
UPROPERTY(EditAnywhere, Category="Moving Platform")
bool bOneShot = false;
public:
// ~begin IInteractable interface
/** Performs an interaction triggered by another actor */
virtual void Interaction(AActor* Interactor) override;
// ~end IInteractable interface
/** Resets the interaction state. Must be called from BP code to reset the platform */
UFUNCTION(BlueprintCallable, Category="Moving Platform")
virtual void ResetInteraction();
protected:
/** Allows Blueprint code to do the actual platform movement */
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category="Moving Platform", meta = (DisplayName="Move to Target"))
void BP_MoveToTarget();
};
@@ -0,0 +1,55 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "SideScrollingPickup.h"
#include "GameFramework/Character.h"
#include "SideScrollingGameMode.h"
#include "Components/SphereComponent.h"
#include "Components/SceneComponent.h"
#include "Engine/World.h"
ASideScrollingPickup::ASideScrollingPickup()
{
PrimaryActorTick.bCanEverTick = false;
// create the root comp
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
// create the bounding sphere
Sphere = CreateDefaultSubobject<USphereComponent>(TEXT("Collision"));
Sphere->SetupAttachment(RootComponent);
Sphere->SetSphereRadius(100.0f);
Sphere->SetCollisionObjectType(ECC_WorldDynamic);
Sphere->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
Sphere->SetCollisionResponseToAllChannels(ECR_Ignore);
Sphere->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
// add the overlap handler
OnActorBeginOverlap.AddDynamic(this, &ASideScrollingPickup::BeginOverlap);
}
void ASideScrollingPickup::BeginOverlap(AActor* OverlappedActor, AActor* OtherActor)
{
// have we collided against a character?
if (ACharacter* OverlappedCharacter = Cast<ACharacter>(OtherActor))
{
// is this the player character?
if (OverlappedCharacter->IsPlayerControlled())
{
// get the game mode
if (ASideScrollingGameMode* GM = Cast<ASideScrollingGameMode>(GetWorld()->GetAuthGameMode()))
{
// tell the game mode to process a pickup
GM->ProcessPickup();
// disable collision so we don't get picked up again
SetActorEnableCollision(false);
// Call the BP handler. It will be responsible for destroying the pickup
BP_OnPickedUp();
}
}
}
}
@@ -0,0 +1,38 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SideScrollingPickup.generated.h"
class USphereComponent;
/**
* A simple side scrolling game pickup
* Increments a counter on the GameMode
*/
UCLASS(abstract)
class ASideScrollingPickup : public AActor
{
GENERATED_BODY()
/** Pickup bounding sphere */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category ="Components", meta = (AllowPrivateAccess = "true"))
USphereComponent* Sphere;
public:
/** Constructor */
ASideScrollingPickup();
protected:
/** Handles pickup collision */
UFUNCTION()
void BeginOverlap(AActor* OverlappedActor, AActor* OtherActor);
/** Passes control to BP to play effects on pickup */
UFUNCTION(BlueprintImplementableEvent, Category="Pickup", meta = (DisplayName = "On Picked Up"))
void BP_OnPickedUp();
};
@@ -0,0 +1,59 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "SideScrollingSoftPlatform.h"
#include "Components/SceneComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Components/BoxComponent.h"
#include "SideScrollingCharacter.h"
ASideScrollingSoftPlatform::ASideScrollingSoftPlatform()
{
PrimaryActorTick.bCanEverTick = true;
// create the root component
RootComponent = Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
// create the mesh
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
Mesh->SetupAttachment(Root);
Mesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
Mesh->SetCollisionObjectType(ECC_WorldStatic);
Mesh->SetCollisionResponseToAllChannels(ECR_Block);
// create the collision check box
CollisionCheckBox = CreateDefaultSubobject<UBoxComponent>(TEXT("Collision Check Box"));
CollisionCheckBox->SetupAttachment(Mesh);
CollisionCheckBox->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
CollisionCheckBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
CollisionCheckBox->SetCollisionObjectType(ECC_WorldDynamic);
CollisionCheckBox->SetCollisionResponseToAllChannels(ECR_Ignore);
CollisionCheckBox->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
// subscribe to the overlap events
CollisionCheckBox->OnComponentBeginOverlap.AddDynamic(this, &ASideScrollingSoftPlatform::OnSoftCollisionOverlap);
}
void ASideScrollingSoftPlatform::OnSoftCollisionOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
// have we overlapped a character?
if (ASideScrollingCharacter* Char = Cast<ASideScrollingCharacter>(OtherActor))
{
// disable the soft collision channel
Char->SetSoftCollision(true);
}
}
void ASideScrollingSoftPlatform::NotifyActorEndOverlap(AActor* OtherActor)
{
Super::NotifyActorEndOverlap(OtherActor);
// have we overlapped a character?
if (ASideScrollingCharacter* Char = Cast<ASideScrollingCharacter>(OtherActor))
{
// enable the soft collision channel
Char->SetSoftCollision(false);
}
}
@@ -0,0 +1,46 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SideScrollingSoftPlatform.generated.h"
class USceneComponent;
class UStaticMeshComponent;
class UBoxComponent;
/**
* A side scrolling game platform that the character can jump or drop through.
*/
UCLASS(abstract)
class ASideScrollingSoftPlatform : public AActor
{
GENERATED_BODY()
/** Root component */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category ="Components", meta = (AllowPrivateAccess = "true"))
USceneComponent* Root;
/** Platform mesh. The part we collide against and see */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category ="Components", meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* Mesh;
/** Collision volume that toggles soft collision on the character when they're below the platform. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category ="Components", meta = (AllowPrivateAccess = "true"))
UBoxComponent* CollisionCheckBox;
public:
/** Constructor */
ASideScrollingSoftPlatform();
protected:
/** Handles soft collision check box overlaps */
UFUNCTION()
void OnSoftCollisionOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
/** Restores soft collision state when overlap ends */
virtual void NotifyActorEndOverlap(AActor* OtherActor) override;
};