Skip to content

Effect components

Every verb other than IS lives as a URuneEffectComponent subclass. The strategy creates one, attaches it to the target actor, calls OnEffectApplied, and removes it on revert. Components own their own state; the rune system never reaches inside them.

URuneEffectComponent (abstract base)

Lives at RuneSystem/Components/RuneEffectComponent.h. Marked Blueprintable so Blueprint subclasses are first-class.

Hooks (BlueprintNativeEvent):

  • OnEffectApplied(VerbRune, ObjectNoun) — the verb activates here. Spawn particles, start timers, register listeners. ObjectNoun is null for 2-word sentences.
  • OnEffectRemoved() — clean up. Destroy particles, cancel timers, restore state.
  • CanHandleVerb(VerbRune) — gate. Default returns true; override if a component should be picky.

State:

  • bEffectActive — true between Apply and Removed. Read from your tick if you want to skip work when inactive.
  • bBidirectional (default true) — controls whether 3-word sentences attach this component to the Object targets too. Set to false for "subject only" verbs (MoveToward).
  • SourceRune — the verb that caused this effect.
  • ObjectNoun — for 3-word relational verbs (MoveToward, Penetration, Hidden), the other noun in the sentence.

Helpers (protected):

  • GetEventBus()URuneEventBus accessor.
  • GetRuneableOwner() — owner cast to ARuneableObject, or null.

How attach/revert works

UComponentVerbStrategy::AttachComponentToTargets runs once per dispatch:

  1. Look up which component class to use:
  2. If the target's noun has the verb in VerbComponentOverrides, that subclass.
  3. Otherwise the verb's TwoWordEffectComponent or ThreeWordEffectComponent (selected by sentence length).
  4. NewObject<URuneEffectComponent>(Target, ComponentClass).
  5. RegisterComponent().
  6. Call OnEffectApplied(Verb, ObjectNoun).
  7. Record the effect in URuneEffectHistory with AddedComponentClass set.

Revert reads the history, finds each component on its owner, calls OnEffectRemoved, and destroys it.

Verb roster

Defined in Source/Rephrased_Demo/RuneSystem/Components/. Each component is one .h/.cpp pair.

Verb Component Notes
FloatUp UFloatUpComponent disables gravity, springs to a target height
Rotate URotateComponent continuous Z rotation
MoveToward UMoveTowardComponent bidirectional false; only subject moves
Penetration UPenetrationComponent overlap-only collision; ground is preserved via the ECC_Ground channel (RuneSystemTypes.h)
Hidden UHiddenComponent hides on collision with ObjectNoun
Burn UFireComponent spreads via the event bus to flammables
Flammable UFlammableComponent marker; does not ignite, marks "can be ignited"
Dangerous UDangerousComponent hurts the player on contact
Slow USlowComponent RuneSpeedScale *= 0.5
Accelerate UAccelerateComponent RuneSpeedScale *= 2.0
Reverse UReverseComponent RuneDirectionScale *= -1
Enlarge UEnlargeComponent uniform scale up
Shrink UShrinkComponent uniform scale down
Mature UMatureComponent growth-stage transform
Regress URegressComponent reverse maturation
Stop UStopComponent disables physics, special cases campfire extinguishment
Passing UPassingComponent timed passage; tied to the Star Observatory device
Shattered UShatteredRune (actor) dropped runes that auto-merge — utility actor, not a verb component

Assigning components on data assets

The roster lists C++ class names (UFireComponent, etc.). In a rune data asset Details panel, pick the Blueprint subclass — typically BP_<Verb>Component under Content/Blueprints/Components/ (for example BP_FireComponent for Burn). See Adding a verb and Content pipeline.

Listener and marker components used by the system but not by direct verb dispatch:

  • UNounListenerComponent — registers its owner as a listener for a foreign noun. The URuneableObjectRegistry returns listeners alongside primary noun matches when ComponentVerbStrategy queries. ListenerVerbOverrides lets the listener provide its own component class per verb without modifying the verb's data asset.
  • UFlammableComponent — owner can be ignited by a fire spread event. Doesn't burn on its own.
  • UFireSpawnPointComponent — designer-placed scene component on burnable actors. UFireComponent spawns one Niagara instance per spawn point at ignition. See VFX.

UFireComponent (worked example)

Most representative non-trivial component.

Behavior:

  • On apply: spawn fire Niagara, start SpreadTimerHandle to broadcast FFireSpreadEvent periodically, optionally schedule ConsumeTimerHandle if the owner has a UFlammableComponent with bConsumeWhenBurned.
  • The event bus broadcasts to every other UFireComponent listener; the receiver checks distance and ignites the target if it has a UFlammableComponent.
  • Fire Niagara: one instance per UFireSpawnPointComponent, or a single instance at the owner's root if no spawn points exist (legacy fallback).

Designer fields:

  • FireIntensity (default 1.0)
  • SpreadRadius (default 300)
  • SpreadDelay (default 2)
  • FireParticleSystem — the Niagara asset.

The Blueprint subclass BP_FireComponent overrides SpawnFireParticles to assign the project's Niagara system; the C++ default does the spawn, BP just configures.

Bidirectional verbs

bBidirectional = true (default) means a 3-word sentence applies to both Subject and Object targets. Penetration and Hidden are bidirectional — "TREE PENETRATION ROCK" puts the component on both trees and rocks because the relationship is symmetric.

MoveToward overrides to false because the rock should not move toward the tree just because the tree is moving toward the rock. Make this decision per component in the constructor:

UMoveTowardComponent::UMoveTowardComponent()
{
    bBidirectional = false;
}

Stack-aware effects

Slow / Accelerate / Reverse mutate RuneSpeedScale and RuneDirectionScale on the runeable. They multiply rather than overwrite, so two Slows stack to 0.25x. Revert undoes the multiply (the strategy records the multiplier in ChangeDescription for debug; the component's OnEffectRemoved divides it back out).

Source files

  • Source/Rephrased_Demo/RuneSystem/Components/RuneEffectComponent.h — base
  • Source/Rephrased_Demo/RuneSystem/Components/FireComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/FlammableComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/FireSpawnPointComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/NounListenerComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/MoveTowardComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/FloatUpComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/RotateComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/PenetrationComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/HiddenComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/DangerousComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/SlowComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/AccelerateComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/ReverseComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/EnlargeComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/ShrinkComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/MatureComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/RegressComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/StopComponent.h
  • Source/Rephrased_Demo/RuneSystem/Components/PassingComponent.h

Known constraints

  • The TDD's verb list mentions "Burn" but the component is UFireComponent. Preserved for parity with existing Blueprints; rename is cosmetic.
  • New verbs require a C++ component until the Blueprint authoring layer ships. Subclassing URuneEffectComponent in BP works for tuning but not for new tick logic.
  • Stack arithmetic for Slow/Accelerate/Reverse is multiplicative. Designers stacking many of these should be aware of the floating-point drift after revert.

Change history

  • 2026-05-23 — Gabriel Li — BP subclass assignment note for data asset Details panel.