Gathering
Module PulseGathering (depends on PulseInventory, PulseAbility and PulseCombat). Trees, rocks and bushes the player harvests: by holding the interaction, by hitting them with a tool, or both. A harvest gives items from a loot table into the inventory, wears the tool, and the node shows how much is left until it grows back.
Overview
| Type | What it is |
|---|---|
UPulseResourceNodeDefinition | Data asset (Primary Asset Type PulseResourceNode): how a node is harvested, the tool it needs, its yield, its look per stage and its regrow time |
APulseResourceNode | The placed node: an actor with a mesh, an interactable and a hit box; replicated, dormant, no tick |
FPulseItemTrait_GatheringTool | Item trait Gathering Tool: tool tags (Pulse.Gathering.Tool.*) and a tier |
FPulseCondition_CanHarvest | Condition Can Harvest Node: harvests left and a fitting tool; the interactable of every node has it |
UPulseGatheringSettings | Project Settings > Pulse > Core > Gathering |
Setup
- The player needs an inventory (
UPulseInventoryComponent) and, for tools, equipment (UPulseEquipmentComponent) on the pawn, the controller or the player state. Harvesting by interaction needs the interact ability; harvesting by hits needs a melee weapon (see Melee). - Create a Resource Node in the Pulse window (Create) or with Add > Pulse > Core > Resource Node.
- Give it a Yield (a loot table), a Full Mesh and the tools it needs.
- Place Pulse Resource Node actors in the level and set their Definition. Nodes can also be spawned on the server (
SetDefinitionbeforeBeginPlay, or the cheatPulseGatheringSpawn). - Make tools: items with the traits Equippable, Weapon (melee, for hits), Durability and Gathering Tool with a tag such as
Pulse.Gathering.Tool.Axe.
Harvesting
| How | What happens |
|---|---|
| Holding the interaction | bHarvestByInteraction: the prompt shows ActionText and DisplayName (“Chop Tree”), the player holds for HoldDuration seconds. Clients interact through the server (bReplicateInteractionEvents). |
| Hits | bHarvestByHits: every HitsPerHarvest hits of a melee attack harvest once. Nodes take hits from every attacker, also from attacks that only hit enemies (IsHitByAnyAttacker of IPulseDamageable). |
Each harvest, on the server:
- checks the node (harvests left) and the tool (
CanHarvest), - rolls the Yield table, scaled by the tool tier:
1 + (Tier - MinToolTier) * YieldBonusPerTier, rounded, at least 1 per drop, - gives the items to the harvester’s inventory; what does not fit drops next to the node (
bDropWhatDoesNotFit,DropScatterRadius), - wears the tool by
DurabilityLoss(negative: the tool’sLossPerUse, throughFPulseItemTrait_Durability::Wear; a broken tool or no loss writes nothing) when it was harvested by interaction; a swung tool already wears per hit like every weapon (Combat with items), - takes one harvest and sends
Pulse.Message.Gathering.Harvested(see below) andOnHarvested.
Tools
RequiredTools lists the tool tags that can harvest a node (any of them); empty means by hand. MinToolTier is the lowest tier that works. The tool is found in the equipment of the harvester: the item in the active hotbar slot first, then the best fitting tool of the other active slots (slots outside the hotbar, such as the hands). Hotbar items that are not selected do not count.
Without a fitting tool the prompt shows the reason in place of the action, “Needs: Axe” (or “Needs: Axe (tier 2)”). ToolText names the tool; empty uses the last part of the first tool tag.
The plugin brings the roots Pulse.Gathering.Tool and Pulse.Gathering.Resource; add your own tags below them, e.g. Pulse.Gathering.Tool.Sickle or Pulse.Gathering.Resource.Fiber. ResourceTags of a definition travel with the harvest message, so quests and statistics can count wood or stone without knowing the node.
Stages and regrowing
| Stage | When | Look |
|---|---|---|
| Full | every harvest left | FullMesh, Material |
| Partial | after the first harvest | PartialMesh (empty: the full mesh), PartialScale, Material |
| Exhausted | nothing left | ExhaustedMesh (empty: hidden), ExhaustedScale, ExhaustedMaterial; no interaction, no hits |
bStandOnGround keeps the bottom of the mesh on the actor location in every stage, so a short stump stays on the ground. The stage is derived from the harvests left, so clients only receive that number.
Every RegrowTime seconds an exhausted or partial node gets one harvest back (0 never regrows). The node keeps the moment the next harvest grows back as a stamp on the Pulse game clock (FPulseResourceNodeState::RegrowAt), so regrowing follows the game rate and stops while the game clock is paused; a late wake-up catches up (one harvest per RegrowTime that passed). Worlds without the clock (editor previews) count world time. The server sets one world timer per waiting node as a wake-up and sets it again when the game clock changes; nothing ticks.
Saving the state
GetNodeState / SetNodeState (same session) and SaveToBytes / LoadFromBytes (versioned FPulseResourceNodeSaveData with the seconds left, across sessions) keep the wait: a node saved 20 of 60 seconds before its next harvest regrows 20 seconds after loading. SetRemainingHarvests sets the harvests left directly (cheats, simple saves) and starts a fresh wait when none runs. Nodes of the endless world (PulseWorldCoreBridge) store only the harvests left, so they regrow only while their chunk is loaded.
Multiplayer
- Harvests run on the server. The definition is sent once (
COND_InitialOnly), afterwards only the harvests left: the state is one property,NodeState, of which onlyRemainingHarvestsreplicates (hits and the regrow stamp stay on the server). - Nodes placed in the level are dormant until something changes (
DORM_Initial); spawned nodes go dormant after their first replication. Every change of the harvests left (harvest, regrow,SetRemainingHarvests,SetNodeState) wakes the node for one update. NodeNetCullDistance(default 200 m) limits how far away clients get nodes.
Collision
The mesh is WorldStatic (profile BlockAll) like other world objects: the interaction query, traces and navigation find it. Melee attacks look for Pawn and PhysicsBody objects, so the node has a second component, HitShape: a query-only box around the mesh of the stage that blocks nothing and is off while the node is exhausted.
Message
Pulse.Message.Gathering.Harvested (payload FPulseHarvestMessage: node, definition, harvester, items, source, harvests left). The context carries the harvester as player and instigator, the node as target, the definition as source, the resource tags as tags and the number of items as amount.
Blueprint and C++
| Function | What it does |
|---|---|
Harvest(Harvester, Source) | Harvests once for a harvester (authority). |
CanHarvest(Harvester, OutReason) | Whether a harvester can harvest now, and why not (any machine). |
SetRemainingHarvests(Remaining) | Sets the harvests left (authority); starts a fresh wait when none runs. |
GetNodeState, SetNodeState(State) | The whole state (harvests left, hits, regrow stamp); set it on the authority. |
GetRegrowSecondsLeft | Game seconds until the next harvest grows back (authority). |
SaveToBytes, LoadFromBytes | Versioned save bytes that keep the wait (C++, authority). |
GetRemainingHarvests, GetMaxHarvests, GetStage, GetDefinition | State of the node. |
OnHarvested / OnHarvestedNative | After a harvest, on the authority. |
OnStageChanged | On every machine when the stage changed. |
Pulse::Gathering::FindTool | The tool a harvester would use for a node (C++). |
The rules are pure functions in PulseGatheringTypes.h (CheckTool, GetYieldMultiplier, ScaleDrops, GetStage, WearTool, Regrow; for the node state GetRemaining, RegisterHit, TakeHarvest, Advance, WithRemaining, ToSaveData, FromSaveData); the specs PulseGathering.Rules and PulseGathering.Node test them.
Example
The example map LV_PulseCore_Example has three trees (left of the start) and two rocks (right of it), built from /PulseCore/Examples/Gathering: DA_PulseExample_Tree (axe, hold 1.5 s or 3 hits, 3 harvests, regrows every 30 s) and DA_PulseExample_Rock (pickaxe, hold 2 s or 4 hits, regrows every 45 s), with the items wood, stone, axe and pickaxe. PulseGatheringGrant (part of PulseCoreGrant) puts the axe on hotbar slot 4 and the pickaxe on slot 5.
Cheats
See Console.