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

  1. 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).
  2. Create a Resource Node in the Pulse window (Create) or with Add > Pulse > Core > Resource Node.
  3. Give it a Yield (a loot table), a Full Mesh and the tools it needs.
  4. Place Pulse Resource Node actors in the level and set their Definition. Nodes can also be spawned on the server (SetDefinition before BeginPlay, or the cheat PulseGatheringSpawn).
  5. 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:

  1. checks the node (harvests left) and the tool (CanHarvest),
  2. rolls the Yield table, scaled by the tool tier: 1 + (Tier - MinToolTier) * YieldBonusPerTier, rounded, at least 1 per drop,
  3. gives the items to the harvester’s inventory; what does not fit drops next to the node (bDropWhatDoesNotFit, DropScatterRadius),
  4. wears the tool by DurabilityLoss (negative: the tool’s LossPerUse, through FPulseItemTrait_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),
  5. takes one harvest and sends Pulse.Message.Gathering.Harvested (see below) and OnHarvested.

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 only RemainingHarvests replicates (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.


PulseCore plugin documentation.

This site uses Just the Docs, a documentation theme for Jekyll.