Tooling
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Subsystems/WorldSubsystem.h"
|
||||
#include "WorldMapSubsystem.generated.h"
|
||||
|
||||
class UWorldMapDefinition;
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EWorldMapMarkerShape : uint8
|
||||
{
|
||||
/** A filled disc. Anything that is just somewhere. */
|
||||
Dot,
|
||||
/** An unfilled ring, so it does not hide what is under it. Objectives, regions of interest. */
|
||||
Ring,
|
||||
/** A cross. Reads at one pixel where a dot does not, which is what a waypoint needs when zoomed out. */
|
||||
Cross,
|
||||
/** A triangle pointing along Heading. The body you are in, and anything else with a facing. */
|
||||
Arrow
|
||||
};
|
||||
|
||||
/**
|
||||
* Something to draw on the map at a place in the world. Purely a UI concern - nothing here is replicated and
|
||||
* nothing gameplay does depends on a marker existing.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct SALTY_API FWorldMapMarker
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Centimetres, world space. Z is ignored; the map is a plan. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "World Map")
|
||||
FVector WorldLocation = FVector::ZeroVector;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "World Map")
|
||||
FText Label;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "World Map")
|
||||
FLinearColor Colour = FLinearColor::White;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "World Map")
|
||||
EWorldMapMarkerShape Shape = EWorldMapMarkerShape::Dot;
|
||||
|
||||
/** World yaw in degrees for Arrow: 0 points along world +X, which is to the right on the map. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "World Map")
|
||||
float HeadingDegrees = 0.f;
|
||||
|
||||
/** Drawn size in pixels, so a marker stays the same size however far the map is zoomed out. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "World Map")
|
||||
float SizePx = 7.f;
|
||||
};
|
||||
|
||||
/**
|
||||
* The map's registry: which definition this level has a map from, and what is on it.
|
||||
*
|
||||
* A world subsystem because a map belongs to a map. Markers are registered by whoever owns the thing being
|
||||
* marked - an actor in BeginPlay, unregistering in EndPlay - rather than found by the map, because a widget
|
||||
* that ran GetAllActorsOfClass to draw itself would be exactly the world search the conventions forbid.
|
||||
*
|
||||
* The local player's own body is deliberately NOT a registered marker: it moves every frame, and a registry
|
||||
* entry rewritten every frame is a registry being used as a variable. The widget asks for it instead.
|
||||
*/
|
||||
UCLASS()
|
||||
class SALTY_API UWorldMapSubsystem : public UWorldSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
/**
|
||||
* The definition whose Level is this world, loading it the first time it is asked for. Null when this level
|
||||
* has no map, which is the normal answer in L_Gym and is not an error.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "World Map")
|
||||
const UWorldMapDefinition* GetDefinition() const;
|
||||
|
||||
/** Adds a marker and returns its handle. Keep the handle; it is the only way to move or remove it. */
|
||||
UFUNCTION(BlueprintCallable, Category = "World Map")
|
||||
FGuid AddMarker(const FWorldMapMarker& Marker);
|
||||
|
||||
/** Replaces a marker wholesale. False when the handle is not one of ours. */
|
||||
UFUNCTION(BlueprintCallable, Category = "World Map")
|
||||
bool UpdateMarker(const FGuid& Handle, const FWorldMapMarker& Marker);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "World Map")
|
||||
bool RemoveMarker(const FGuid& Handle);
|
||||
|
||||
const TMap<FGuid, FWorldMapMarker>& GetMarkers() const { return Markers; }
|
||||
|
||||
/**
|
||||
* Where the local player is, as a marker, or false when there is no local body to speak of - a dedicated
|
||||
* server, or a client before possession. The heading is the control rotation rather than the body's, because
|
||||
* on a map a player expects the arrow to point where they are looking.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "World Map")
|
||||
bool GetLocalPlayerMarker(FWorldMapMarker& OutMarker) const;
|
||||
|
||||
/**
|
||||
* Emits world_map_opened. Every way of opening the map calls this - the console command today, a HUD key
|
||||
* when there is one - so the count is of maps opened and not of the ways of opening them.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "World Map")
|
||||
void NoteMapOpened(FName LayerId, float MetresPerPixel, const FString& OpenedBy);
|
||||
|
||||
/** Raised when a marker is added, moved or removed. The widget subscribes; it never polls. */
|
||||
DECLARE_MULTICAST_DELEGATE(FOnWorldMapMarkersChanged);
|
||||
FOnWorldMapMarkersChanged OnMarkersChanged;
|
||||
|
||||
private:
|
||||
/** Every WorldMap definition whose Level is this world. Searched once; the answer, including none, sticks. */
|
||||
void ResolveDefinition() const;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
mutable TObjectPtr<const UWorldMapDefinition> Definition;
|
||||
|
||||
mutable bool bResolved = false;
|
||||
|
||||
TMap<FGuid, FWorldMapMarker> Markers;
|
||||
};
|
||||
Reference in New Issue
Block a user