Doors
ACrimsonCoreDoor is a replicated, world-saved door that puts the whole interaction stack to work: the prompt, the context menu, interaction conditions, a lockpick channel and the save system. It swings or slides, has one or two leaves, and can optionally take a key (consumed on unlock if you choose), be lockpicked, carry a bolt that works from one side only, be knocked on, and close itself after a delay. One rule set drives the prompt, the menu and the server, so what a player is offered is always what the server will accept.
ICrimsonInteractableTarget (CrimsonInteraction) and ICrimsonContextActionProvider (CrimsonUI) the same way (see Interaction Integration and Context Menu).What's included
| Type or asset | Purpose |
|---|---|
ACrimsonCoreDoor (shows as Crimson Door) | The door actor. Derives from ACrimsonPersistentActor and implements ICrimsonInteractableTarget (prompt) and ICrimsonContextActionProvider (menu). Lays out its frame and leaves from the meshes you assign. |
FCrimsonDoorState | The whole gameplay state: bOpen, bLocked, bBolted, bSwingTowardBack. Replicated and SaveGame; editable per placed instance so a door can start open, locked or bolted. |
ECrimsonDoorAction | Open, Close, Unlock, Lock, Lockpick, Bolt, Unbolt, Knock (plus None). |
UCrimsonCoreContextAction_Door | One context menu entry per verb, gathered fresh in a fixed order; every rule defers to the door. Lock / Unlock / Bolt / Unbolt are hold-to-confirm rows. |
UCrimsonCoreGameplayAbility_DoorInteract | The one handler ability every door option grants. Reads the verb from the trigger event and calls RequestDoorAction on the server. |
B_CrimsonCore_Door | Ready-to-place door Blueprint in /CrimsonCore/Interaction/Door/, with the meshes SM_CrimsonDoor_Leaf, SM_CrimsonDoor_FrameEdge and SM_CrimsonDoor_FrameCorner (/CrimsonCore/Interaction/Door/Meshes/). |
GA_Door_Lockpick | The shipped lockpick channel (/CrimsonCore/Interaction/Door/): a Blueprint of UCrimsonCoreGameplayAbility_InteractionDuration, 4 s, MaxDistanceFromTarget 250, bCancelWhenInputReleased on. |
ItemDef_Key_Brass, ItemDef_Key_Iron, ItemDef_Lockpick | Showcase items in /CrimsonCore/Inventory/Items/. The keys carry the Item.Type.Key tag. |
NavArea_Default while open, ClosedNavArea / LockedNavArea while shut, both NavArea_Null by default). That only reaches the navmesh if the navmesh rebuilds at runtime: set Runtime Generation to Dynamic Modifiers Only (or Dynamic) on your level's RecastNavMesh actor, or in Project Settings -> Navigation Mesh. With Static, the doorway keeps whatever area it had at build time. The plugin never changes this setting for you.1. Place a door
Place B_CrimsonCore_Door (tick Show Plugin Content in the Content Browser settings to see it), or make your own Blueprint child of Crimson Door and assign its meshes. The blue editor arrow points at the door's Front side.
B_CrimsonCore_Door (/CrimsonCore/Interaction/Door/). In Class Defaults study Crimson|Door|Meshes (LeafMesh, FrameEdgeMesh, FrameCornerMesh), Crimson|Door|Motion and Crimson|Door|Abilities (LockpickAbilityClass = GA_Door_Lockpick). Duplicate it, or create a Blueprint Class with parent Crimson Door and fill the same three meshes.On the placed door pick Motion (Swing or Slide), tick Double Leaf for a pair, and set Auto Close Delay in seconds (0 keeps it open).
2. Lock it with a key
Set KeyItem to an item definition and tick Door State -> Locked on the placed instance. A player carrying the key gets Unlock; one without it sees Locked with the key on the prompt's checklist and no key glyph. Tick bConsumeKeyOnUnlock to take one key out of the bag on unlock. Add more requirements to ExtraKeyConditions; they are checked with the key for both Lock and Unlock.
Images/CrimsonCore/door_prompt_locked_checklist.pngDetails panel of the placed door: Crimson|Door|Lock -> Key Item = ItemDef_Key_Brass, Crimson|Door -> Door State -> Locked ticked. To unlock it from script instead, on the server: Has Authority (Branch, True) -> Set Door Locked (New Locked unticked).
KeyHoldDuration), then Open. While the door is open, the menu's Lock is grayed out with "Close the door first".3. Make it lockpickable (optional)
Tick bLockpickable, then add the requirements to LockpickConditions (or LockpickConditionSets), for example Has Item with a lockpick and Attribute Compare Health >= 50. Set LockpickAbilityClass to a duration ability (the shipped GA_Door_Lockpick, or UCrimsonCoreGameplayAbility_InteractionDuration itself for the default 3 s channel).
Images/CrimsonCore/door_lockpick_progress_ring.pngDetails panel: Crimson|Door|Lock -> Lockpickable ticked; Lockpick Conditions -> + add Has Item (ItemDefinition = ItemDef_Lockpick) and Attribute Compare (Health, >=, 50); Crimson|Door|Abilities -> Lockpick Ability Class = GA_Door_Lockpick. To start the channel from your own UI on the owning client: Is Locally Controlled (Branch, True) -> Try Begin Lockpick Local (Pawn = the player's pawn).
4. Add a one-sided bolt (optional)
Tick bHasBolt and set BoltSide (Back by default). Only a player on that side can bolt or unbolt; from the other side the prompt reads Barred ("Barred from the other side") and the menu's Unbolt is grayed out. Bolting needs the door closed. Bolt and Unbolt are held for 3 s (BoltHoldDuration), on the prompt and in the menu. Tick Door State -> Bolted to start it barred.
How the prompt and the menu decide
Three functions on the door hold every rule; the prompt, the menu and the server all call them.
Listing - `ShouldListAction`. Whether the menu lists a verb at all. It reads only replicated state, so client and server build the same list.
| Verb | Listed when |
|---|---|
| Open | the door is closed |
| Close | the door is open |
| Unlock | locked and KeyItem is set |
| Lock | unlocked and KeyItem is set |
| Pick Lock | locked and bLockpickable |
| Bolt | bHasBolt and not bolted |
| Unbolt | bolted |
| Knock | always |
Availability and reasons - `GetActionAvailability`. Returns Hidden, Disabled with a reason, or Enabled. A disabled row is grayed out with its reason, and the server refuses anything that is not Enabled. Side checks live here, never in the listing, so a client and server that disagree about a pawn standing on the door plane produce a refusal rather than a mismatched menu.
| Verb | Disabled when | Reason shown |
|---|---|---|
| Open | bolted | "Unbolt it first" (bolt side) or "Barred from the other side" |
| Open | locked | "Locked" |
| Lock, Bolt | the door is open | "Close the door first" |
| Unlock, Lock | the key or ExtraKeyConditions fail | the first failure, e.g. "Requires Brass Key" |
| Pick Lock | LockpickConditions / LockpickConditionSets fail | the first failure |
| Bolt | used from the side opposite BoltSide | "The bolt is on the other side" |
| Unbolt | used from the side opposite BoltSide | "Barred from the other side" |
| Knock | during KnockCooldown | no text; the row shows a cooldown sweep |
Images/CrimsonCore/door_context_menu_disabled_reasons.pngPrimary action - `GetPrimaryAction`. What one press of the interaction prompt does, checked in order. The menu marks the same verb as its default row.
| Order | Door state | Prompt |
|---|---|---|
| 1 | open | Close |
| 2 | bolted | Unbolt from BoltSide; otherwise an informational Barred |
| 3 | locked | Unlock if the key passes; else Pick Lock if the lockpick requirements pass; else Locked with the key checklist (with no KeyItem: Locked, "It won't budge") |
| 4 | otherwise | Open |
Hold times - `GetActionHoldDuration`. Lock and Unlock take a KeyHoldDuration hold (1 s by default), Bolt and Unbolt a BoltHoldDuration hold (3 s); every other verb is instant. The same duration applies on both surfaces: the prompt offers an available Unlock or Unbolt as a Hold option and fills the progress ring around the key glyph, and the context menu marks those four rows HoldToConfirm with the door's duration in HoldToConfirmSeconds, so the row fills while it is held and fires when full. Releasing early cancels. The informational Locked and Barred prompts stay instant. Set either duration to 0 to make those verbs instant.
| Property (Crimson|Door|Behavior) | Default | Verbs |
|---|---|---|
KeyHoldDuration | 1 s | Lock, Unlock |
BoltHoldDuration | 3 s | Bolt, Unbolt |
Multiplayer and save
- Server owns the state.
RequestDoorAction(server,BlueprintAuthorityOnly) is the one validated entry point: reach (MaxUseDistance, 550 by default), thenGetActionAvailability, then apply. Prompt presses and menu clicks both end there. It refusesLockpick; a lock is only picked by a completed channel. - Prompt presses carry the verb. On the pressing client,
CustomizeInteractionEventDatawritesGetPrimaryActioninto the trigger event'sEventMagnitude;UCrimsonCoreGameplayAbility_DoorInteractdecodes it on the server. Two players pressing Open at once both get an open door, never a toggle back to shut. - Holds are a UX delay, not an authority check. The hold is timed on the owning client, on the prompt and in the menu; nothing reaches the server until it completes, and the server then validates the verb through
RequestDoorActionlike any other. - The menu runs on the server, except Pick Lock, which runs on the owning client because its channel is a locally predicted ability.
- Motion is local on every machine, the dedicated server included, so leaf collision is real; transforms never replicate. A late-joining client, or one receiving a restored save, snaps the door into place with no sound.
- Blocking. A leaf that would move into a pawn holds; after a moment the server reverses it (
bReverseWhenBlocked) and fires On Door Blocked. Auto-close waits while the doorway is occupied. - Events.
OnDoorStateChanged(Previous, Current), On Door State Changed, On Door Motion Finished and On Knocked fire on every machine. Knock and the locked-door rattle are unreliable multicasts. - Save.
DoorStateisSaveGameand saves with the world: open, locked and bolted state and the swing direction come back on load. A consumed key stays consumed through the player's inventory save.
Mesh pivot rules
The layout reads mesh bounds, not pivots, so most door meshes work unchanged.
- Leaf. Width is the Y extent, height the Z extent; the thickness is centered on the door plane and the bottom sits on the floor. The hinge is the vertical edge nearest the mesh origin, so a leaf with its pivot on the hinge works whichever way it extends (+Y or -Y). A centered pivot hinges on the min-Y edge.
- Double door. The right leaf is a Y mirror of the left one (negative Y scale, not a 180-degree turn), so both keep their front faces on the Front side. Model one leaf only.
- Frame.
FrameEdgeMeshis a vertical post, stretched to the leaf height on each side; the same mesh rolled 90 degrees is the lintel.FrameCornerMeshfills the two top corners. Any of them may be left empty. - The interaction volume and the nav modifier are sized to the doorway in the same pass.
Images/CrimsonCore/door_double_sliding.pngBuild a door showcase
Place six B_CrimsonCore_Doors in a level of your own and configure each as below, plus three B_CrimsonItemSpawnerPads (/CrimsonCore/GameplayTesting/) for ItemDef_Key_Brass, ItemDef_Key_Iron and ItemDef_Lockpick. Set the level's RecastNavMesh to Dynamic Modifiers Only first.
| Door | Configuration | Try |
|---|---|---|
| Unlocked | Swing, AutoCloseDelay 5 | It swings away from you on either side; a second player in its path makes it reverse; auto-close waits while the doorway is occupied. |
| Key locked | Locked, KeyItem = Brass Key | Locked with a checklist without the key; Unlock (a 1 s hold) -> Open with it; Lock grayed while open. |
| Consume key | Locked, KeyItem = Iron Key, bConsumeKeyOnUnlock | The Iron Key leaves the bag on unlock. |
| One-sided bolt | bHasBolt, BoltSide = Back, starts bolted | Back side can Unbolt with a 3 s hold; front side reads Barred, Unbolt grayed. |
| Double sliding | Motion = Slide, bDoubleLeaf, AutoCloseDelay 4 | Two mirrored leaves slide into the wall. |
| Lockpickable | Locked, Brass Key, bLockpickable; Has Item (Lockpick) + Attribute Compare (Health >= 50) | Checklist shows Lockpick + Health; a 4 s ring; walking away or releasing the key cancels; menu Pick Lock works. |
P), the doorway area changes as the door opens and closes.API reference
Function (on ACrimsonCoreDoor) | Runs on | Purpose |
|---|---|---|
RequestDoorAction(Instigator, Action) | Server | Validated player action: reach, availability, apply. True when applied. Refuses Lockpick. |
GetActionAvailability(Instigator, Action, OutReason) | Any | Hidden / Disabled (with reason) / Enabled. |
GetPrimaryAction(Instigator) | Any | The verb a prompt press performs; None when the prompt is only a message. |
ShouldListAction(Action) | Any | Whether the menu lists the verb (replicated state only). |
GetActionHoldDuration(Action) | Any | Seconds the verb is held on the prompt and in the menu: KeyHoldDuration for Lock / Unlock, BoltHoldDuration for Bolt / Unbolt, 0 (instant) otherwise. |
SetDoorOpen(bNewOpen, Instigator) / SetDoorLocked(bNewLocked) / SetDoorBolted(bNewBolted) | Server | Scripting setters with no instigator checks. The optional instigator picks the swing direction. |
TryBeginLockpickLocal(Pawn) | Owning client | Starts the lockpick channel on the pawn's ability system. |
IsOpen / IsLocked / IsBolted / GetDoorState | Any | Replicated state. |
GetSideOf(Actor) / IsWithinReach(Instigator) | Any | Front (+X) or Back; within MaxUseDistance. |
GetKnockCooldown(OutRemaining01, OutRemainingSeconds) | Any | True while knocking is cooling down. |
GetKeyDisplayName | Any | The key item's display name, empty with no KeyItem. |
EncodeAction / DecodeAction / GetActionDisplayName | Any | Verb to event magnitude and back; the verb's display text. |
OnDoorStateChanged | All | Delegate (Previous, Current) after every state change, restores included. |
See also
- Interaction Integration - the interact ability, prompt widget and progress ring the door relies on.
- Context Menu - how the menu gathers, validates and executes the door's entries.
- Inventory Showcases - spawner pads and the item definitions the keys are built from.