Companion System

v2.0.0

Command System

Define and execute custom commands for your companions

Overview

A CompanionCommand is a ScriptableObject that defines a command (e.g., "Sit", "Fetch", "Attack"). The actual execution logic is implemented via GC2 Triggers on the companion prefab.

Key Concept: Commands are just labels — the logic lives on the prefab. This gives you full flexibility to implement any behavior using GC2's visual scripting.

Command Workflow

Commands flow through four stages from creation to execution:

┌─────────────────────────────────────────────────────────────────────┐
│                         COMMAND WORKFLOW                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  1. CREATE A COMMAND (ScriptableObject)                             │
│     └─> CompanionCommand "Sit"                                      │
│                                                                      │
│  2. REGISTER IN DEFINITION                                          │
│     └─> CompanionDefinition.CommandSet += "Sit"                     │
│                                                                      │
│  3. IMPLEMENT ON PREFAB (GC2 Visual Scripting)                      │
│     └─> Trigger: "On Companion Command Received"                    │
│         Filter: "Sit"                                               │
│         └─> Instructions: PlayGesture("Sit"), Wait(3s)              │
│         └─> Instruction: "Complete Companion Command"               │
│                                                                      │
│  4. ISSUE THE COMMAND (GC2 Instruction or Code)                     │
│     └─> "Issue Companion Command" or brain.IssueCommand()           │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

1. Create a CompanionCommand

In the Project window:

  1. Right-click → Create → Companion System → Command
  2. Name it descriptively (e.g., "Cmd_Sit")
  3. Configure in the Inspector

Command Properties

PropertyDescription
Command IDUnique identifier string (e.g., "sit")
Display NameUI-friendly name (e.g., "Sit!")
IconOptional sprite for UI display
CooldownSeconds until command can be issued again

2. Register in CompanionDefinition

Open your CompanionDefinition and add the command to the Command Set array:

CompanionDefinition (e.g., "Dog")
├── Display Name: "Buddy"
├── Prefab: Dog_Prefab
├── Follow Profile: ...
├── Personality: ...
└── Command Set:
    ├── [0] Cmd_Sit      ← Add here
    ├── [1] Cmd_Fetch
    └── [2] Cmd_Attack
Important: Only commands in the CommandSet can be executed.SendCommand() silently ignores commands not in the set.

3. Implement Trigger on Prefab

On the Companion Prefab (the GameObject with CompanionBrain):

Step 3a: Create the Trigger

  1. Add Component → Game Creator → Visual Scripting → Trigger
  2. Add Event: On Companion Command Received
  3. Set Filter Command to your command (e.g., "Cmd_Sit")

Step 3b: Add Instructions

Trigger: On Companion Command Received
├── Filter: Cmd_Sit
└── Instructions:
    ├── Character Play Gesture: "Sit"    ← Play animation
    ├── Wait: 3 seconds                   ← Hold pose
    └── Complete Companion Command        ← REQUIRED!

Don't Forget: Complete Command

The last instruction must be Complete Companion Command. This resets ActiveCommand to null, returns the companion toFollowing state, and fires theOnCommandCompleted event.

Without it, the companion stays stuck in Commanded state!

4. Issue the Command

Trigger the command from anywhere in your game:

Option A: GC2 Visual Scripting

Use in any Trigger (e.g., UI Button, Hotkey):

Trigger: On Button Click
└── Instructions:
    └── Issue Companion Command
        ├── Definition: Dog
        └── Command: Cmd_Sit

Option B: C# Code

// Issue command via CompanionManager (recommended)
var brain = CompanionManager.Instance.FindActiveByDefinition(dogDefinition);
CompanionManager.Instance.IssueCommand(brain, sitCommand);

// Or directly on the brain
brain.IssueCommand(sitCommand);

// Group command (all companions)
CompanionManager.Instance.GroupSendCommand(sitCommand);

// Check status
bool busy = brain.ActiveCommand != null;
bool canDo = brain.Definition.HasCommand(command);

// Events
brain.OnCommandReceived += (cmd) => Debug.Log($"Received: {cmd.DisplayName}");
brain.OnCommandCompleted += (cmd) => Debug.Log($"Completed: {cmd.DisplayName}");

Complete Example: Fetch Command

A "Fetch" command where the companion runs to a target, picks something up, and returns:

1. ScriptableObject

CompanionCommand: Cmd_Fetch
├── Command ID: "fetch"
├── Display Name: "Fetch!"
└── Cooldown: 10 seconds

2. Definition

CompanionDefinition: Dog
└── Command Set: [Cmd_Sit, Cmd_Fetch]

3. Trigger on Prefab

Trigger: On Companion Command Received
├── Filter: Cmd_Fetch
└── Instructions:
    │
    ├── [1] Character Play Gesture: "AlertBark"
    │
    ├── [2] Character Move To: {Target Position}
    │       └── Wait until arrival: true
    │
    ├── [3] Character Play Gesture: "PickUp"
    │
    ├── [4] Wait: 1 second
    │
    ├── [5] Character Move To: {Owner Position}
    │       └── Wait until arrival: true
    │
    ├── [6] Character Play Gesture: "Drop"
    │
    └── [7] Complete Companion Command   ← REQUIRED!

4. UI Trigger

Trigger: On Button Click "Fetch Button"
└── Instructions:
    └── Issue Companion Command
        ├── Definition: Dog
        └── Command: Cmd_Fetch

State Flow During Execution

Understanding the state transitions helps debug command issues:

Following ──────────────────────────────────────────────────────────┐
    │                                                               │
    │ IssueCommand(Cmd_Sit)                                         │
    ▼                                                               │
Commanded ──► OnCommandReceived Event fires                         │
    │         └─► Trigger "On Companion Command Received"           │
    │             └─► Instructions execute...                       │
    │                                                               │
    │ CompleteCommand()                                             │
    ▼                                                               │
Following ◄─────────────────────────────────────────────────────────┘
Bond Bonus: Completing a command automatically grants bond points to the companion. The amount is controlled by BondPerCommand in the companion's Personality profile.

GC2 Components Reference

Instructions (Actions)

InstructionDescription
Issue Companion CommandSend a command to a specific companion
Complete Companion CommandSignal that the current command finished
Companion WaitCompanion stops and stays in place
Companion FollowCompanion resumes following
Group: Send CommandSend command to ALL active companions

Events (Triggers)

EventDescription
On Command ReceivedFires when command is received (with filter)
On Command CompletedFires when current command finishes
On State ChangedFires on any state transition
On Companion SummonedFires when companion spawns
On Companion RecalledFires when companion despawns

Conditions

ConditionDescription
Has Active CommandTrue if a command is currently running
Companion In StateCheck current companion state
Companion Is ActiveCheck if a definition is currently summoned

Troubleshooting

Command not executing

  • Is the command in the CompanionDefinition's CommandSet?
  • Does the Trigger have the correct Filter Command?
  • Is the companion actually summoned and active?

Companion stuck in "Commanded" state

Add Complete Companion Command as the last instruction in your Trigger's instruction list.

Command executes twice

  • Check for duplicate Triggers with the same filter
  • Set a cooldown on the CompanionCommand

Wrong companion executes

Use GetGameObjectInstance on the prefab trigger — it always references the exact companion that received the command.

Pro Tips
  • Use cooldowns to prevent command spam
  • Commands automatically grant bond points (+BondPerCommand from Personality)
  • Use GroupSendCommand() for party-wide commands
  • Check readiness with Has Active Command condition