Overview
This genre pack targets skill-based melee combat — fighters, brawlers, and hack-and-slash in the Street Fighter / Tekken / God of War mould: a character defined by frame-data states (hitstun and blockstun), a poise → stagger system that opens punish windows, a guard / block damage channel that trades chip damage and mobility for safety, a super meter spent on specials, and combo cancels expressed through ability block/cancel tags. Its core loop is the melee exchange — strike, block, cancel, stagger, punish — rather than the projectile-and-ammo loop of a shooter.
The core specification ships no dedicated fighting case study, so this pack is designed from first
principles. It still leans entirely on core constructs: the modifier Channel mechanism
(core spec §5.3) for the guard damage reduction, a custom Execution
(§9.5) for the multi-attribute hit resolution, ActivationOwnedTags / CancelAbilitiesWithTags /
BlockAbilitiesWithTags (§8.1) for the cancel and super-freeze rules, and effect-granted tags
(§7.5, §9.7) for the entire frame-data and stagger state machine.
The 1v1 duelist is the concrete seed, but the same pieces generalize across the wider Combat family — beat 'em up, brawler, hack-and-slash — which layer crowd targeting, juggles, and arenas on top of this strike-block-stagger core. Where the Shooter pack resolves hits at range with ammo, falloff, and hitzone multipliers, this pack resolves them in melee with frame phases, a guard meter, and a stagger meter — no projectiles, no reloads.
Relationship to the core specification
This document is additive. It defines genre-specific Attributes, Tags, Abilities, and Effects on top of the core Universal Gameplay Ability System specification.
-
It MUST NOT redefine, override, or contradict any concept in the core spec.
-
All entities in
entities/validate against the sameschemas/*.jsonas the core. -
It reuses the core lifecycle tags (
State.Alive,State.Combat,State.Dead) by reference, and adds combat-specific states (Combat.State.Hitstun,Combat.State.Staggered, …). -
Combat state is mutated only through Effects (core spec §7.5) — abilities gate on tags and apply Effects, never writing tags directly — and the guard damage reduction is the core modifier
Channelmechanism (§5.3), not a new construct.
Genre Attributes
Defined in entities/attribute_set.yaml as the FighterCombatSet set. The base values describe a
default all-rounder fighter, so the set is playable as shipped.
| Attribute | Category | Role |
|---|---|---|
|
Statistic / Resource |
|
|
Statistic / Resource |
The stagger meter, clamped to |
|
Statistic / Resource |
The super gauge, clamped to |
|
Statistic |
Base offense; scaled by each move’s multiplier before mitigation. |
|
Statistic |
Flat pre-mitigation damage reduction subtracted by the hit-resolution calc. |
|
Statistic / Resource |
Block stamina, clamped to |
|
Statistic |
Ground movement speed; slowed while blocking (the |
|
Meta |
Transient per-hit magnitude handed to the calc before mitigation; the target of the |
|
Meta |
Output of the hit-resolution calc after |
The melee exchange (the core combat mechanic)
A fighting game lives or dies on the exchange: who can strike, who must block, and who gets punished. In UGAS that exchange is a small set of tag-gated abilities, a guard damage channel, and two depletable meters.
Frame-data states gate every action
Real fighting games are governed by frame data: after a move connects, the loser spends a fixed number of frames unable to act. UGAS models those windows as effect-granted states:
-
A clean hit applies
GE_Hitstun(entities/effect_hitstun.yaml), which grantsCombat.State.Hitstunfor the stun window. -
A blocked hit applies
GE_Blockstun(entities/effect_blockstun.yaml), which grantsCombat.State.Blockstun.
Both states appear in every action ability’s ActivationBlockedTags, so a stunned fighter simply
cannot act until the timed effect expires — no explicit timer lives in the ability. Whether the
defender’s blockstun expires before the attacker’s own recovery is what makes a blocked string
safe or punishable.
Cancel windows via the frame-phase tags
Combat.Move.Startup → Combat.Move.Active → Combat.Move.Recovery is the illustrative phase
vocabulary the engine sets on a fighter as a move plays out; individual abilities own only the
phase windows they actually need through ActivationOwnedTags. The combo system is then pure tag
logic:
-
GA_LightAttackownsCombat.Move.Recoveryduring its tail. -
GA_HeavyAttacklistsAbility.Type.Lightin itsCancelAbilitiesWithTags, so a heavy buffered during the light’s recovery cancels it — the canonical light → heavy combo cancel, expressed with tags alone. -
GA_Speciallists the normals in itsBlockAbilitiesWithTags, locking the super-user out of their own Light/Heavy/Block while the special is active —BlockAbilitiesWithTagsis owner-scoped, so this commits the player to the super animation rather than touching the opponent.
The guard damage channel
GA_Block (entities/ability_block.yaml) holds GE_Blocking (entities/effect_blocking.yaml)
while the block input is down — the same hold-then-WaitInputRelease shape as the shooter’s
aim-down-sights. GE_Blocking is the genre’s signature effect. Modifiers stack through named
channels — additive within a channel, multiplicative across channels (§5.3) — so GE_Blocking
puts a single Multiply in a Block channel:
-
On
IncomingDamage: aMultiplyof \(-0.6\) → factor \(1 - 0.6 = 0.40\). The hit-resolution calc reads the reduced magnitude on a blocked hit. -
On
MoveSpeed: aMultiplyof \(-0.5\) → factor \(1 - 0.5 = 0.50\). Committing to guard costs mobility.
Because the reduction lives in its own named channel, additional defensive layers (a parry, a guard
upgrade) authored in a different channel multiply across it cleanly — exactly how the shooter’s
Aim and Attachments channels compose on Spread.
Poise drains to a stagger
Poise is a depletable stat: every hit (blocked or not) deals poise damage. When Poise reaches
0, the engine’s OnAttributeChanged threshold (§5.6) applies GE_Stagger
(entities/effect_stagger.yaml), which grants Combat.State.Staggered for a long window — a
guaranteed punish. The threshold keeps the rule that states are granted by Effects, never written
directly.
Worked example (entities/gameplay_controller.yaml)
A fighter mid-block eats one heavy hit. The attacker’s AttackPower (\(20\)) scaled by the
heavy move multiplier (\(2.5\)) is the raw hit, handed to the calc as IncomingDamage. The
defender owns Combat.State.Blocking, so the Block channel and Defense both apply:
The same blocked hit drains the two meters and feeds the gauge, and holding guard slows the fighter
through the Block channel:
-
Health: \(200 - 10 = 190\) (theEffectiveDamage) -
GuardHealth: \(80 - 30 = 50\) (heavy guard damage, chipped while blocking) -
Poise: \(100 - 10 = 90\) (the heavy’s block-poise chip) -
Super: \(0 + 8 = 8\) (meter gained for taking the hit) -
MoveSpeed: \(500 \times (1 - 0.5) = 250\) (theBlockchannel)
These are exactly the CurrentValue entries recorded for the example fighter. Had the hit landed
unblocked, the Block channel would be absent (factor \(1.0\)), the calc would apply
GE_Hitstun instead of GE_Blockstun, and poise damage would be far higher — the difference
between a chipped guard and a broken one.
Hit resolution via ExecutionCalculation
Melee damage is not a single subtraction: it couples the source’s AttackPower (× the move
multiplier), the target’s flat Defense, the Block channel reduction, poise damage, guard damage,
and the super gain for both fighters. That is inherently multi-attribute and cross-actor, so
GE_MeleeDamage (entities/effect_melee_damage.yaml) delegates to a custom Execution (§9.5),
ExecCalc_MeleeResolution, which honors Team.Friendly targets (skips friendly fire) and writes
EffectiveDamage (a Meta attribute) for telemetry and UI. This is the combat analogue of the
shooter’s hit-resolution calc and the racing traction calc — the seam where the genre’s signature
math plugs into UGAS. An adopter who prefers a declarative poise-damage rule can express it as an
AttributeBased magnitude (§9.4.2) keyed on the source’s AttackPower
(\((\text{AttackPower} + \text{PreAdd}) \times \text{Coefficient} + \text{PostAdd}\)); the
worked numbers above keep poise damage inside the calc for a single source of truth.
Genre Tags
Defined in entities/tag_registry.yaml (additive only):
-
Combat states —
Combat.State.Hitstun,Combat.State.Blockstun,Combat.State.Blocking,Combat.State.Staggered(all granted by Effects). -
Move frame phases —
Combat.Move.Startup,Combat.Move.Active,Combat.Move.Recovery. -
Ability types —
Ability.Type.Light|Heavy|Block|Special(the keys for cancel/block queries). -
Hitzones —
Hitzone.High|Mid|Low(high/mid/low guard rules). -
Teams —
Team.Friendly|Hostile(friendly-fire skip in the calc).
Genre Abilities
-
entities/ability_light_attack.yaml—GA_LightAttack: fast startup, short recovery; ownsCombat.Move.Recoveryso a heavy can cancel it. -
entities/ability_heavy_attack.yaml—GA_HeavyAttack: slow, high-damage; itsCancelAbilitiesWithTagscancels the light’s recovery. -
entities/ability_block.yaml—GA_Block: holdsGE_Blockingfor theBlock-channel reduction at the cost of movement; ends on input release. -
entities/ability_special.yaml—GA_Special: costs a full bar (Cost: GE_SuperCost) and usesBlockAbilitiesWithTagsto lock the user out of their own normals during super-freeze (an owner-scoped self-lock that commits the player to the super); onGE_SpecialCooldown.
Genre Effects
-
entities/effect_melee_damage.yaml—GE_MeleeDamage: instant; runsExecCalc_MeleeResolutionto resolve a hit (damage, poise, guard, super) and writeEffectiveDamage. -
entities/effect_hitstun.yaml—GE_Hitstun:HasDuration; grantsCombat.State.Hitstun, blocking all actions. -
entities/effect_blockstun.yaml—GE_Blockstun:HasDuration; grantsCombat.State.Blockstun, blocking all actions (shorter than hitstun). -
entities/effect_blocking.yaml—GE_Blocking: infinite-while-held;Block-channelMultiplyonIncomingDamageandMoveSpeed; grantsCombat.State.Blocking. -
entities/effect_stagger.yaml—GE_Stagger:HasDuration; grantsCombat.State.Staggered, applied by the engine’sPoise == 0threshold.
The trivial cost and cooldown effects the abilities reference — GE_SuperCost (subtracts Super),
GE_LightAttackCooldown, GE_HeavyAttackCooldown, GE_SpecialCooldown — are flat resource costs
and cooldown tags, referenced by name rather than shipped as files, the same convention the Racing
and Shooter packs use for their nitro/fire cost and cooldown effects.
Using this pack
-
Copy
genres/combat/into your project (or loadentities/directly via theugas-schema-authorskill). -
Tune the fighter base values in
FighterCombatSetfirst —MaxHealth, thePoise/GuardHealthmeters, andAttackPower/Defensecarry the match pacing — then add moves as new Abilities
GE_MeleeDamagepayloads and new defensive layers as effects in their ownChannel. -
Implement the one
ExecCalc_MeleeResolutionhook in your engine (the only seam: damage, poise, guard, and super math); everything else is data. -
Author new states as
Combat.State.*tags granted by Effects (never mutate tags directly), and drive stagger from the enginePoise == 0threshold applyingGE_Stagger. -
Validate with
python scripts/validate_schema_examples.pybefore committing.