Massive changes to the npc system
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
#include "Styling/SlateColor.h"
|
||||
|
||||
class AActor;
|
||||
class IPropertyHandle;
|
||||
class SWidget;
|
||||
|
||||
/**
|
||||
* Details panel layout for one FScheduledAction, the element type of
|
||||
* UNpcScheduleAsset::ScheduledActions.
|
||||
*
|
||||
* An action's anchor is stored as a PersistentId rather than as an actor pointer, so that a re-cook or
|
||||
* a re-save cannot leave a schedule dangling. The price of that is a value nobody can type in, so this
|
||||
* customization replaces the raw Guid row with one that names the actor the id currently resolves to,
|
||||
* offers the current selection and every anchor in the level in a menu, and has a button that clears
|
||||
* it. The id therefore always comes from the actor itself and can never drift away from it. A row for
|
||||
* an id that no actor in this level claims is left as it is, because that is a real state (furniture
|
||||
* that was deleted, or furniture sitting in a sublevel that is not loaded) and not something to hide.
|
||||
*
|
||||
* Every other member of the struct keeps the row the details panel would have given it.
|
||||
*/
|
||||
class FNpcScheduleActionCustomization : public IPropertyTypeCustomization
|
||||
{
|
||||
public:
|
||||
|
||||
static TSharedRef<IPropertyTypeCustomization> MakeInstance();
|
||||
|
||||
virtual void CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override;
|
||||
virtual void CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override;
|
||||
|
||||
protected:
|
||||
|
||||
/** The anchor row: the label, the name of the actor the id resolves to, the assign menu, and the
|
||||
* unassign button. Stands in for the raw Guid field, which is never shown. */
|
||||
void AddAnchorRow(IDetailChildrenBuilder& ChildBuilder, TSharedRef<IPropertyHandle> AnchorHandle) const;
|
||||
|
||||
/** What the id points at: the actor's label, "None" when nothing is assigned, or the raw id when no
|
||||
* actor in this level claims it. */
|
||||
FText GetAnchorText(TSharedRef<IPropertyHandle> AnchorHandle) const;
|
||||
|
||||
/** The id itself and what it means, so that an id can be read off the panel and matched against a
|
||||
* log or a save without the panel offering a text box to mistype it into. */
|
||||
FText GetAnchorTooltip(TSharedRef<IPropertyHandle> AnchorHandle) const;
|
||||
|
||||
/** Subdues the row while nothing is assigned, so an action with no anchor reads differently at a
|
||||
* glance from one whose anchor the panel cannot name. */
|
||||
FSlateColor GetAnchorColor(TSharedRef<IPropertyHandle> AnchorHandle) const;
|
||||
|
||||
bool CanClearAnchor(TSharedRef<IPropertyHandle> AnchorHandle) const;
|
||||
|
||||
/** Writes one id into the action. An invalid id is what unassign does, so the button that clears
|
||||
* the anchor and the menu entries that set one both come through here. */
|
||||
void AssignAnchor(TSharedRef<IPropertyHandle> AnchorHandle, FGuid NewAnchorGuid) const;
|
||||
|
||||
/** The assign menu: the current selection first, then every anchor in the level, with the one in
|
||||
* use radioed. */
|
||||
TSharedRef<SWidget> BuildAnchorMenu(TSharedRef<IPropertyHandle> AnchorHandle) const;
|
||||
|
||||
/** The id a property handle currently holds, read as text so that it works whether the handle is
|
||||
* reached on its own, inside the array, or through a multi selection, which reports no single
|
||||
* value and so comes back as unassigned. */
|
||||
static FGuid ReadAnchorGuid(const TSharedRef<IPropertyHandle>& AnchorHandle);
|
||||
|
||||
/** The first actor in the current selection that can be an anchor, or null when there is none. */
|
||||
static AActor* GetSelectedAnchorActor();
|
||||
|
||||
/** The id of an actor, invalid when the actor cannot be an anchor. Implementing
|
||||
* IActorPersistentIdInterface is the whole of that rule, since the interface is what carries the id
|
||||
* a schedule stores. */
|
||||
static FGuid GetAnchorId(AActor* Actor);
|
||||
|
||||
/** The actor a single id belongs to, or null when no actor in the current level claims it. */
|
||||
static AActor* FindActorWithGuid(const FGuid& Guid);
|
||||
|
||||
/** How many actors in the current level claim an id. More than one is a real state - a pasted actor
|
||||
* carries its source's id - and everything that stores the id, this schedule included, resolves to
|
||||
* whichever of them a search reaches first, so the row says so rather than leaving it to be found
|
||||
* in game. Walked on the spot rather than indexed, because this is read while the panel is drawn
|
||||
* and an index would be stale the moment the author adds, deletes or pastes an anchor. */
|
||||
static int32 CountActorsWithGuid(const FGuid& Guid);
|
||||
|
||||
/** Every actor in the current level that can be an anchor, sorted by label so that the menu does not
|
||||
* reshuffle itself between two openings. */
|
||||
static void CollectAnchorActors(TArray<AActor*>& OutAnchors);
|
||||
|
||||
/** For the menu entries that only exist to explain themselves, such as "nothing is selected". */
|
||||
static bool NeverExecutable();
|
||||
|
||||
/** Past this many anchors the menu stops listing actors and says how many it left out, because a
|
||||
* level can hold hundreds and a menu is not a browser. Assigning from the selection still works. */
|
||||
static constexpr int32 MaxListedAnchors = 40;
|
||||
};
|
||||
Reference in New Issue
Block a user