Stats & Damage
Entity Stats
Section titled “Entity Stats”Entity stats (health, stamina, mana, etc.) are managed by the EntityStatMap ECS component. Each stat has a current value, min/max bounds, optional regeneration, and named modifiers.
Package: com.hypixel.hytale.server.core.modules.entitystats
Built-in Stats
Section titled “Built-in Stats”| Stat | Index Getter | Description |
|---|---|---|
| Health | DefaultEntityStatTypes.getHealth() | Hit points — entity dies when this reaches min |
| Oxygen | DefaultEntityStatTypes.getOxygen() | Breath — drains underwater, triggers drowning damage at min |
| Stamina | DefaultEntityStatTypes.getStamina() | Used by blocking, dodging, sprinting |
| Mana | DefaultEntityStatTypes.getMana() | Used by spell abilities |
| Signature Energy | DefaultEntityStatTypes.getSignatureEnergy() | Used by weapon signature abilities |
| Ammo | DefaultEntityStatTypes.getAmmo() | Projectile ammunition |
Stats are indexed by int — use DefaultEntityStatTypes to get the index for a specific stat.
Reading Stats
Section titled “Reading Stats”EntityStatMap statMap = store.getComponent(ref, EntityStatMap.getComponentType());
int healthIndex = DefaultEntityStatTypes.getHealth();EntityStatValue health = statMap.get(healthIndex);
float current = health.get(); // current valuefloat min = health.getMin(); // minimum (usually 0)float max = health.getMax(); // maximum (e.g. 100)float percent = health.asPercentage(); // (current - min) / (max - min)Writing Stats
Section titled “Writing Stats”int healthIndex = DefaultEntityStatTypes.getHealth();
// set to exact value (clamped to [min, max])statMap.setStatValue(healthIndex, 50.0f);
// add (heal) or subtract (damage directly, bypassing the damage pipeline)statMap.addStatValue(healthIndex, 10.0f);statMap.subtractStatValue(healthIndex, 5.0f);
// set to min (e.g. kill) or max (e.g. full heal)statMap.minimizeStatValue(healthIndex);statMap.maximizeStatValue(healthIndex);
// reset to initial value (configured per stat type asset)statMap.resetStatValue(healthIndex);Stat Modifiers
Section titled “Stat Modifiers”Modifiers adjust a stat’s min or max bound. The value is then clamped to the modified range.
import com.hypixel.hytale.server.core.modules.entitystats.modifier.Modifier;import com.hypixel.hytale.server.core.modules.entitystats.modifier.StaticModifier;
int healthIndex = DefaultEntityStatTypes.getHealth();
// +20 max health (additive)statMap.putModifier(healthIndex, "my_mod_health_bonus", new StaticModifier(Modifier.ModifierTarget.MAX, StaticModifier.CalculationType.ADDITIVE, 20.0f));
// double max health (multiplicative)statMap.putModifier(healthIndex, "my_mod_health_double", new StaticModifier(Modifier.ModifierTarget.MAX, StaticModifier.CalculationType.MULTIPLICATIVE, 2.0f));
// remove a modifier by keystatMap.removeModifier(healthIndex, "my_mod_health_bonus");
// check if modifier existsModifier existing = statMap.getModifier(healthIndex, "my_mod_health_bonus");Each modifier has:
| Property | Options | Description |
|---|---|---|
| Target | MIN, MAX | Which bound to modify |
| Calculation type | ADDITIVE, MULTIPLICATIVE | Flat addition or percentage multiplier |
| Amount | float | The modifier value |
Modifiers are keyed by string — use a unique prefix (e.g. "my_mod_") to avoid collisions. When multiple modifiers exist on the same target, additive modifiers are applied first, then multiplicative.
Effects can also apply stat modifiers via JSON — see EffectControllerComponent - Stat Modifiers.
Damage
Section titled “Damage”Dealing Damage
Section titled “Dealing Damage”Use DamageSystems.executeDamage() to deal damage through the full pipeline (armor, invulnerability, effects, death):
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause;import com.hypixel.hytale.server.core.modules.entity.damage.DamageSystems;
// damage from an entity (melee attack, etc.)Damage damage = new Damage( new Damage.EntitySource(attackerRef), DamageCause.PHYSICAL, 25.0f);DamageSystems.executeDamage(targetRef, componentAccessor, damage);
// damage from the environmentDamage envDamage = new Damage( new Damage.EnvironmentSource("lava"), DamageCause.ENVIRONMENT, 10.0f);DamageSystems.executeDamage(targetRef, componentAccessor, envDamage);
// damage from a commandDamage cmdDamage = new Damage( new Damage.CommandSource(sender, "mycommand"), DamageCause.COMMAND, 50.0f);DamageSystems.executeDamage(targetRef, componentAccessor, cmdDamage);After executeDamage returns, the damage has been processed through all pipeline stages. Check the event object to see the result:
DamageSystems.executeDamage(targetRef, componentAccessor, damage);
if (damage.isCancelled()) { // damage was blocked (invulnerable, armor absorbed all, etc.) return;}float finalDamage = damage.getAmount(); // may differ from initial amountDamage Sources
Section titled “Damage Sources”Every Damage event has a source identifying where the damage came from:
| Source | Constructor | Description |
|---|---|---|
Damage.EntitySource | new EntitySource(attackerRef) | Melee or direct entity damage |
Damage.ProjectileSource | new ProjectileSource(shooterRef, projectileRef) | Projectile damage (extends EntitySource) |
Damage.EnvironmentSource | new EnvironmentSource("type") | Environmental damage (lava, traps, etc.) |
Damage.CommandSource | new CommandSource(sender, command) | Damage from a command |
Damage.NULL_SOURCE | (constant) | No source (used as fallback) |
The source determines the death message shown to players.
Damage Causes
Section titled “Damage Causes”DamageCause is a JSON asset loaded from Entity/DamageCauses/. Each cause defines properties like whether it triggers durability loss or bypasses resistances.
Built-in causes:
| Cause | Constant | Description |
|---|---|---|
| Physical | DamageCause.PHYSICAL | Melee and physical attacks |
| Projectile | DamageCause.PROJECTILE | Ranged projectile damage |
| Command | DamageCause.COMMAND | Damage from commands |
| Drowning | DamageCause.DROWNING | Underwater suffocation |
| Environment | DamageCause.ENVIRONMENT | Lava, traps, etc. |
| Fall | DamageCause.FALL | Fall damage |
| Out of World | DamageCause.OUT_OF_WORLD | Below the world boundary |
| Suffocation | DamageCause.SUFFOCATION | Inside solid blocks |
Damage causes support inheritance — armor resistance against a parent cause also applies to child causes.
The Damage Pipeline
Section titled “The Damage Pipeline”DamageSystems.executeDamage() dispatches the Damage event through the ECS, where registered DamageEventSystem handlers process it in three ordered groups:
Gather — systems that generate damage (drowning, fall damage, out-of-world). These create and dispatch Damage events.
Filter — systems that modify or cancel damage before it’s applied:
FilterUnkillable— cancels if entity has Invulnerable or Intangible componentPlayerDamageFilterSystem— cancels if player has spawn protection or if PvP is disabledArmorDamageReduction— reduces damage based on equipped armor (flat reduction first, then multiplier)WieldingDamageReduction— reduces damage when blocking
Apply — subtracts the final damage from health. If health reaches min, adds a DeathComponent triggering death processing.
Inspect — post-damage effects: impact particles, sounds, hurt animation, armor durability loss, combat text, hit indicators, kill feed.
Intercepting Damage
Section titled “Intercepting Damage”To modify or cancel damage, register a custom DamageEventSystem on the entity store:
public class MyDamageFilter extends DamageEventSystem { @Override public void handle(int index, ArchetypeChunk<EntityStore> archetypeChunk, Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer, Damage damage) { Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
// halve all environmental damage if (damage.getSource() instanceof Damage.EnvironmentSource) { damage.setAmount(damage.getAmount() * 0.5f); }
// cancel damage entirely for entities with a custom component MyShield shield = archetypeChunk.getComponent(index, myShieldType); if (shield != null && shield.isActive()) { damage.setCancelled(true); } }
@Override public Query<EntityStore> getQuery() { return Query.any(); // run for all entities }}Register in setup():
getEntityStoreRegistry().registerSystem(new MyDamageFilter());When a handler calls setCancelled(true), all subsequent handlers skip the event — damage is not applied and no inspect systems run.
For the full Damage event API (meta keys, inner classes, all methods), see ECS Events - Damage.
When ApplyDamage reduces health to min, it adds a DeathComponent to the entity. This triggers a sequence of death systems:
- Health cleared — health set to 0
- Effects cleared — all active effects removed
- Interactions cleared — ongoing combat chains and abilities cancelled
- Death animation — plays the death animation from the
DamageCause - Item loss — items dropped based on the world’s DeathConfig (modes:
NONE,ALL,CONFIGURED) - Kill feed — KillFeedEvent dispatched to broadcast death/kill messages
- Death screen — RespawnPage opened for players (if
showDeathMenuis true) - Death marker — position recorded as a world map marker
- Corpse removal — non-player entities removed after death animation
Death Screen
Section titled “Death Screen”When a player dies, the server opens a RespawnPage — a full-screen UI the player cannot close. It shows:
- A “YOU DIED” title with skull icon
- The death reason (e.g. “Killed by Skeleton”)
- Dropped items and durability loss details (if applicable)
- A Respawn button the player must click to respawn
The player stays dead (with DeathComponent attached) until they click the button. There is no auto-respawn timer or immediate respawn setting.
Skipping the Death Screen
Section titled “Skipping the Death Screen”DeathComponent has a showDeathMenu field (defaults to true). When false, the death screen is not shown — but the player is not auto-respawned either. Use this when your mod handles respawn timing itself:
// skip the screen and respawn immediatelyDeathComponent death = store.getComponent(ref, DeathComponent.getComponentType());if (death != null) { death.setShowDeathMenu(false); DeathComponent.respawn(componentAccessor, ref);}Checking if Dead
Section titled “Checking if Dead”DeathComponent death = store.getComponent(ref, DeathComponent.getComponentType());if (death != null) { // entity is dead, waiting for respawn Message deathMessage = death.getDeathMessage();}Killing Programmatically
Section titled “Killing Programmatically”Through the damage pipeline — respects invulnerability, armor, and filter systems. Can be cancelled:
Damage damage = new Damage(Damage.NULL_SOURCE, DamageCause.COMMAND, Float.MAX_VALUE);DamageSystems.executeDamage(ref, componentAccessor, damage);Directly adding a DeathComponent — bypasses the entire pipeline. Kills unconditionally:
DeathComponent.tryAddComponent(commandBuffer, ref, damage);Respawn
Section titled “Respawn”Respawn is triggered by calling DeathComponent.respawn(), which teleports the player to the location chosen by the world’s RespawnController and then removes the DeathComponent. The removal triggers respawn systems:
- Teleport — the respawn controller selects the destination and teleports the player
- Stats reset — all stats restored to their reset value (initial or max, per stat configuration)
- Effects cleared — all status effects removed
- Spawn protection — brief invulnerability period starts
// trigger respawn programmatically (async — teleport + cleanup happens in the future)CompletableFuture<Void> future = DeathComponent.respawn(componentAccessor, ref);Regeneration
Section titled “Regeneration”Stats can regenerate automatically based on the stat type’s asset configuration. Regeneration is defined per EntityStatType in JSON — not in mod code. Each stat can have multiple regen entries with conditions:
- Interval — seconds between regen ticks
- Amount — value per tick (additive or percentage of max)
- Conditions — must be met for regen to occur (e.g.
OutOfCombat,Alive,NoDamageTaken)
For programmatic healing, use addStatValue directly:
int healthIndex = DefaultEntityStatTypes.getHealth();statMap.addStatValue(healthIndex, 20.0f); // heal 20 HPOr apply a healing effect for gradual regeneration:
EntityEffect healRegen = EntityEffect.getAssetMap().getAsset("Food_Health_Regen_Medium");effects.addEffect(ref, healRegen, 10.0f, OverlapBehavior.OVERWRITE, store);