Stats and damage¶
All · 20 min read · No code
A character's numbers are Gameplay Ability System attributes on its own ability system. Health
comes from Lyra's health set; everything else (Strength, Ether, Agility, defences, rates, weapon
attack, level, talent gauge) is XenoCore's UXenoCoreStatSet. Numbers get onto a character in two
layers: a statline effect that reads curve tables at the character's level, and an equipment
effect that adds the stats of what it wears. Damage is calculated by one execution,
UXenoCoreDamageExecution, set up as Battle/Attributes/BP_XenoCoreDamageExecution and used by
Battle/Attributes/GE_XenoCore_Damage. The one idea: the formula is not symmetric. A party member
hitting an enemy treats the enemy's defence as a fraction; an enemy hitting a party member
subtracts the party member's defence as flat points.
At a glance
In play: the numbers over every hit, MISS, BLOCKED and CRITICAL HIT!, and the stats on the Party and Equipment screens.
What you can change, no code:
- A character's stats at each level: Give a character its own statline
- An enemy's: Author an enemy statline
- Hit, block and critical chances, and how much a level gap matters: the settings on
Battle/Attributes/BP_XenoCoreDamageExecution(open it, click Class Defaults, see XenoCore › Damage). Listed under What you can change. - A brand-new stat needs a programmer: Add a stat to the damage formula
How it fits together¶
| Layer | Asset | What it sets |
|---|---|---|
| Attribute set | UXenoCoreStatSet (C++), granted by every character's ability set |
Which stats exist. |
| Health | Lyra's ULyraHealthSet, created with the pawn's ability system |
Health, Max Health. |
| Statline | Battle/Attributes/GE_XenoCore_BaseStats_* |
Base stats at a level, read from a curve table. Override modifiers. One per character (_Kael, _Mira ...) and one per enemy type. |
| Party curves | Battle/Attributes/CT_XenoCore_PartyStatlines |
Each character's own rows, such as Kael.Strength, one curve per stat, X = level. Each follows the XC1 character whose skill tree it uses. CT_XenoCore_BaseStats holds the older shared Shulk.* rows the colour mannequins still read. |
| Enemy curves | Battle/Attributes/CT_XenoCore_EnemyStats |
Rows such as Minion_Melee.Strength. |
| Level | UXenoCoreMMC_EffectLevel and the pawn's C++ |
Character Level is set to the level the statline was applied at. |
| Equipment | Inventory/GE_XenoCore_EquipmentStats |
Adds each worn item's stats through Set By Caller tags XenoCore.Stat.*. |
| Damage | GE_XenoCore_Damage → BP_XenoCoreDamageExecution |
Health damage per hit. |
| Level difference | Battle/Attributes/CT_XenoCore_DamageLevelDiff |
Rows Damage.LevelDiff.Physical and Damage.LevelDiff.Ether. |
The attributes¶
| Attribute | Meaning | Range |
|---|---|---|
| Strength | Physical attack stat. Does nothing for ether attacks. | 0 and up |
| Ether | Ether attack stat. Does nothing for physical attacks. | 0 and up |
| Agility | Hit chance and evasion. | 0 and up |
| Physical Defence, Ether Defence | Flat points on a party member; a fraction (0 to 0.9) on an enemy. | 0 and up |
| Critical Rate, Block Rate | Chance per hit, as a fraction. 0.1 is 10%. | 0 to 1 |
| Weight | Armour weight, subtracted from Agility for hit rolls only. | 0 and up |
| Weapon Attack Min, Weapon Attack Max | The weapon's damage range. | 0 and up |
| Character Level | The level used in every curve lookup. | 1 to Max Character Level (99) |
| Experience, Experience To Next Level | Written by the progression component for display. | |
| Talent Gauge | 0 to 100. Fills as auto-attacks land; spent by Talent Arts. | 0 to 100 |
| Talent Charge Per Hit | Gauge percent added per landed auto-attack. Set by the statline. | 0 and up |
| Auto Attack Speed | Multiplier on swing cadence; Haste and Slow change it. | 0.1 to 4 |
A party character has no defence, block or critical chance of its own: those come only from equipment. That is intended.
Max Character Level is a config value with no editor field. To raise the cap, close the
editor, open your project's Config/DefaultGame.ini in a text editor, and add:
[/Script/XenoCoreRuntime.XenoCoreStatSet]
MaxCharacterLevel=120
and extend your curve tables, and every row of DT_XenoCore_ExperienceCurves, to the new cap.
The damage formula¶
Every hit runs these steps, in order:
- Team check. If the attacker may not damage the target (same team, or no team), the hit does
nothing and a warning is logged under
LogXenoDamage. - School. Ether if the damage effect's spec carries
XenoCore.Art.School.Ether, otherwise physical. The school decides which attack stat and which defence are used. - Contact roll: miss, block or hit. Rolled by the attacking ability before the effect is applied (see below). A miss shows MISS over the target and nothing else happens: no damage, no reaction, no talent charge. A block shows BLOCKED and marks the hit as blocked for step 8.
- Weapon roll. A random value between Weapon Attack Min and Max, with Max capped at Min + 99.
- Art multiplier. The Set By Caller
XenoCore.Art.Magnitude.Damage. Auto-attacks send none, so it is 1. - Modifiers. 1, or the critical multiplier on a critical hit. Never below Minimum Modifiers.
- Level multiplier. The level-difference curve at (attacker level − target level).
- Block. If step 3 rolled a block, damage is multiplied by Block Multiplier: 0 by default, so a block takes no damage at all.
- The formula, then rounded down:
| Attacker | Damage |
|---|---|
| Party member | (stat + weapon) × art × (1 − defence) × modifiers × level × block, with defence capped at Max Defence Fraction |
| Enemy | max(stat − defence, 0) × art × modifiers × level × block |
The side is decided by what the attacker is, not by who is driving it, so a companion under AI control and the same character under your control deal the same damage. In order:
- An
AXenoCoreEnemyCharacteris always an enemy. - A character with a Member Definition (every party member) is always party.
- Otherwise, the team: on the player's team is party, on any other team is enemy. This covers a player pawn with no member definition, such as the one Lyra spawns when Starting Roster is empty.
- Last resort, for an actor with no team: whether a player is controlling it.
If the attacker and the target come out on the same side, a warning is logged under
LogXenoDamage: the target's defence is then being read in the wrong units, which means your team
setup lets a side hurt itself.
Critical hits roll against the attacker's Critical Rate. The multiplier is Critical Multiplier (1.25), or Critical Multiplier Vs Reacting (1.5) against a Toppled or Dazed target. An ether attack against a Dazed target is always critical.
A critical hit shows CRITICAL HIT! over the target, draws the damage number gold and plays the heavier critical hit sound. It also adds 10 to the Party Gauge when a party member lands it. The text, its size and punch, and an optional hit-stop are under Project Settings › Game › XenoCore Content › Critical Hit; see Tune hit feedback.
Hit rolls. The chance is 1 + (attacker − target) × Agility Hit Per Point, using Agility minus Weight on both sides, plus a level-difference modifier, clamped between Hit Chance Floor (5%) and Hit Chance Cap (100%). Equal agility and equal level always hit.
- Ether attacks ignore Agility but still take the level modifier, so they miss enemies well above you.
- A Toppled or Dazed target cannot be missed.
- The level modifier comes from Physical Hit By Level Gap or Ether Hit By Level Gap, indexed by the level gap (0 to 10, the last entry covering 10 or more). It is a bonus when the attacker is higher and a penalty when it is lower. The shipped numbers are XC1's: a physical attacker 3 levels below loses 24%, and at 6 or more below it loses 120% to 200%, which puts it on the 5% floor.
Block rolls. Physical hits only; ether is never blocked. The chance is the target's Block Rate plus Block Rate Per Level (5%) for each level the attacker is below the target (a penalty when it is above), capped at ± Block Level Modifier Cap (100%). A Toppled or Dazed target does not block.
- Party members get Block Rate only from their weapon and skills.
- Enemies have none of their own, so an enemy blocks only when it is higher-levelled than you.
- Block Level Dead Zone (0) ignores small gaps; set it to 2 for no level effect within ±2.
XenoCore departs from XC1 on blocks
In XC1 a block halves the damage. XenoCore's Block Multiplier defaults to 0, so a block
negates the hit completely. Set it to 0.5 on BP_XenoCoreDamageExecution to restore XC1.
Fails silently: a blocked opening hit does not start the fight
An enemy is drawn into a fight by losing HP. With Block Multiplier at 0, an enemy that blocks your first hit takes no damage and stays where it is. Hit it again.
Fails silently: the team gate
Damage between actors on the same team, or where either has no team, is dropped. The target still
flinches, because the hit-react event is sent separately, so "it reacted" does not prove damage
landed. Type log LogXenoDamage Verbose in the console to see every hit's breakdown.
The hit roll reads its settings (Agility Hit Per Point, Hit Chance Floor, Hit Chance
Cap, Miss Colour) from the execution class the attack's damage effect actually runs, so edits
on BP_XenoCoreDamageExecution take effect like every other setting there. A damage effect that runs
no XenoCore damage execution falls back to the C++ defaults, and a warning naming the effect is logged
once under LogXenoDamage.
Lifecycle¶
- Spawn and level-up. The character applies its Stats Effect at its level; every modifier is an Override, so re-applying replaces rather than stacks. The same effect also overrides Health, which is why a level-up fully heals. See Progression.
- Equip. Equipping runs
GA_XenoCore_EquipmentStats, which appliesGE_XenoCore_EquipmentStatswith eachXenoCore.Stat.*magnitude filled from the item's own stat tags. Unequipping removes it. See Equipment. - Statuses such as
GE_XenoCore_Status_StrengthDownor_Slowmodify attributes for their duration. See Reactions and statuses.
What you can change¶
| Setting | Where it lives | What it does |
|---|---|---|
| Base stat curves | CT_XenoCore_BaseStats, CT_XenoCore_EnemyStats |
Stat value at each level. |
| Which rows a character reads | That character's GE_XenoCore_BaseStats_* modifiers |
The curve row and a scale per stat. |
| Level Difference Curve | BP_XenoCoreDamageExecution › XenoCore › Damage |
The level-gap multiplier table. |
| Max Defence Fraction | same | Defence cap on the party formula (0.9). |
| Minimum Modifiers | same | Floor of the modifiers bucket (0.1). |
| Critical Multiplier, Critical Multiplier Vs Reacting | same | 1.25 and 1.5. |
| Block Multiplier | same | Damage a blocked hit keeps: 0 (XC1 is 0.5). |
| Agility Hit Per Point, Hit Chance Floor, Hit Chance Cap | same | The hit roll: 0.01, 5% and 100%. |
| Physical Hit By Level Gap, Ether Hit By Level Gap | same › XenoCore › Damage › Level Difference | Hit-chance change per level of gap, 0 to 10+. |
| Block Rate Per Level, Block Level Dead Zone, Block Level Modifier Cap | same | Block-chance change per level of gap: 5%, 0 and 100%. |
| Miss Colour, Critical Colour, Blocked Colour | same › XenoCore › Damage › Notifications | Battle-text colours. |
| Critical Hit Text, Critical Hit Scale, Critical Hit Punch, Critical Hit Stop Seconds, Critical Hit Stop Dilation | Project Settings › Game › XenoCore Content › Critical Hit | The critical line's wording, size and punch, and an optional hit-stop (0 s, off, by default). |
| Max Character Level | DefaultGame.ini, section above |
Level cap. |
The shipped BP_XenoCoreDamageExecution changes only Level Difference Curve; every other value
above is the class default. CT_XenoCore_DamageLevelDiff multiplies damage by the gap (attacker level
− target level):
| Gap | −6 or less | −5 to −3 | −2 | −1 to +2 | +3 | +4 to +9 | +10 or more |
|---|---|---|---|---|---|---|---|
Damage.LevelDiff.Physical |
0.5 | 0.75 | 0.875 | 1.0 | 1.125 | 1.25 | 2.0 |
Damage.LevelDiff.Ether |
0.625 | 0.875 | 0.9375 | 1.0 | 1.125 | 1.25 | 1.5 |
Every statline modifier must be Override
A statline is re-applied on every level-up. A modifier using Add stacks on the previous
application and the numbers climb with each level. Use Override on every modifier of a
GE_XenoCore_BaseStats_* effect.
Enemy types are one curve times a multiplier
The minion's rows are anchored on XC1's Hand Bunnit: Minion_Melee.MaxHealth is 900 + 100 per
level and Minion_Melee.Strength 69 + 10 per level. The other minion types read the same rows,
and their GE_XenoCore_BaseStats_Minion_* effects scale them on each modifier: Melee 1.0,
Brute 1.45, Bulwark 1.15, Skirmisher 0.9. So the number the game uses is not always the number in
the table. Check the scale on every modifier of an effect you duplicate.
Every character has its own statline
Each sample character's GE_XenoCore_BaseStats_<Name> reads its own rows of
CT_XenoCore_PartyStatlines at a scale of 1.0, so the table number is the game number. To give a
new character its own growth, add rows for it and point its effect at them. See
Give a character its own statline.
Extending it¶
- A new stat: needs C++, because attributes are reflected properties. Add it to a subclass of
UXenoCoreStatSetor a new set, grant that set in your ability sets, and read it in a damage execution subclass. See Add a stat to the damage formula. - A different formula: subclass
UXenoCoreDamageExecutionin C++ and point a duplicate ofGE_XenoCore_Damageat it. Keep the team check. - Tuning only: make a child Blueprint of
BP_XenoCoreDamageExecutioninContent/MyGame/and point your damage effect at the child, rather than editing the shipped one.
Why it's built this way¶
For programmers: the design reasons
- Curve tables, not per-level data assets. One row per stat covers every level, and a statline effect sampled at a level is plain GAS: nothing custom stores numbers.
- Level set by the pawn, not a curve row. An earlier design read level from a curve whose input was also level, which could silently disagree with the badge.
- The hit and block rolls live in the attacking ability. An execution can only output attribute changes; it cannot tell the ability "you missed" or "you were blocked". Rolling first lets a miss cancel the whole hit, and lets a block stop a Topple and the rest of a multi-hit art.
- Its own execution instead of Lyra's. Lyra's formula is a shooter's. Lyra's team check is kept, because without it arts would damage allies.