Skip to main content

Player Party

Guide

Run several complete player units in one battle while every card and effect remains bound to the correct character

A GameEncounter can create one primary player and additional party members. Every member keeps independent HP, armor, statuses, hooks, content definition, and presentation, while the default policy gives the party one player phase and one shared set of card piles and energy.

The diagram connects encounter authoring to the final runtime result: the encounter establishes stable unit identities and slots, turn and resource policies decide how those members act, and card ownership carries the correct source through targeting, FlowGraph execution, events, and presentation.

Scope

Party support covers multiple complete local player-side units. ControllerId, resource policies, activation policies, and card-play policies provide integration boundaries, but GCS does not implement networking, replication, rollback, rooms, or accounts.

Build the initial party

Player Unit remains the primary compatibility entry. Player Capacity limits initial members and runtime reinforcements, Party Members adds the rest of the starting roster, and Resource Mode chooses how card zones and energy are owned.

SettingRuntime result
Player UnitCreates the primary member in slot 0; legacy PlayerUnit and GCSApi.Player() continue to resolve this member
Player CapacityLimits the player-side roster; the Demo board exposes three player slots by default
Party Members[].UnitCreates an independent PlayerUnitState from that definition
ControllerIdIdentifies the logical controller responsible for the member
SlotIndexSelects a stable presentation slot; -1 chooses a free slot
Resource ModeUses shared-party, per-controller, or per-unit card zones and energy

Workbench validation reports missing members, capacity overflow, out-of-range slots, and occupied slots before Play Mode.

Keep identity explicit

Every live unit carries UnitId, Team, TeamId, ControllerId, SlotIndex, and JoinOrder. GCSApi.Player() is the compatibility primary, ActivePlayer() is the selected member, PlayerParty() preserves roster order including dead members, and AlivePlayers() and DeadPlayers() separate action and revival queries.

The default shared party turn prepares armor, status lifecycle, resources, and draws for the full party, then opens one player action window. A skipped member is excluded without skipping other members, and the default loss condition requires every player-side member to be dead. IPartyActivationPolicy controls free or ordered member activation, IActionWindowPolicy controls when cards may be played, and ICardPlayPolicy adds project-specific play permission without replacing the battle state machine.

Choose a resource policy

ModeOwnership
SharedPartyAll members share one hand, draw pile, discard pile, exhaust pile, and energy pool
PerControllerMembers with the same ControllerId share a pool
PerUnitEvery member owns independent card zones and energy

Cards created from a member's Starting Deck retain that member's OwnerUnitId, even when a shared resource policy merges every deck into one physical pool. The owner is the default execution source, so Self, hooks, status interactions, controller identity, and card events remain character-correct. Ownerless cards use the selected active member instead.

PlayerUnitState member = GCSApi.AlivePlayers()[1] as PlayerUnitState;
GCSApi.TrySetActivePlayerUnit(member);

foreach (CardInstance card in GCSApi.Hand(member))
{
if (GCSApi.GetEffectiveEnergyCost(card) <= GCSApi.Energy(member)
&& GCSApi.CanPlayCard(card))
{
// Supply a target when GCSApi.RequiresTarget(card.GetActiveCard()) is true.
}
}

Card-play and lifecycle events expose OwnerUnitId, SourceUnitId, ControllerId, and ResourceOwner, allowing UI and external systems to distinguish original ownership, actual execution source, and the pool that paid.

Target allies and relative teams

Cards can use Single Ally, Other Ally, All Allies, and Random Ally. Single-target ally modes use the Demo targeting arrow and legal-unit highlight, while all and random modes resolve automatically. FlowGraph All Allies and All Opponents resolve relative to the current source team, Active Player returns the selected living member, and All Dead Allies supplies revival targets.

Change the roster at runtime

Spawn Unit can create player or enemy units by Team. Player reinforcements respect capacity, take a stable slot, initialize their resource pool, and publish OnPlayerUnitAdded. Despawn Unit removes either team and publishes OnUnitRemoved; ordinary death keeps a member in the roster for Revive Unit and All Dead Allies.

PlayerUnitState joined = GCSApi.SummonPlayer(playerDefinition, "local-2", 2, GCSApi.ActivePlayer());
if (joined != null)
GCSApi.SetEnergy(joined, GCSApi.MaxEnergyFor(joined));

GCSApi.RemoveUnit(joined);

The Demo BattleBoard follows add, remove, death, revival, and active-member changes. Legacy scenes with one PlayerArea expand to three reusable slots, and Dashboard Initialize System installs the same layout into the active scene.

Open Demo/Scenes/Party/Party Showcase to run Party Squire, Party Apprentice, and Party Acolyte against two enemies through the complete battle UI. Their shared Party Tactics deck contains Field Mend, Interpose, Shared Resolve, and Emergency Aid, which verify direct, other-only, all-party, and random ally targets without removing the cards needed to finish the battle. Game Card Monitor exposes each member's UnitId, slot, controller, resource owner, and status state; single-character encounters remain compatible by leaving Party Members empty.