Skip to content

Stats & Damage

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

StatIndex GetterDescription
HealthDefaultEntityStatTypes.getHealth()Hit points — entity dies when this reaches min
OxygenDefaultEntityStatTypes.getOxygen()Breath — drains underwater, triggers drowning damage at min
StaminaDefaultEntityStatTypes.getStamina()Used by blocking, dodging, sprinting
ManaDefaultEntityStatTypes.getMana()Used by spell abilities
Signature EnergyDefaultEntityStatTypes.getSignatureEnergy()Used by weapon signature abilities
AmmoDefaultEntityStatTypes.getAmmo()Projectile ammunition

Stats are indexed by int — use DefaultEntityStatTypes to get the index for a specific stat.

EntityStatMap statMap = store.getComponent(ref, EntityStatMap.getComponentType());
int healthIndex = DefaultEntityStatTypes.getHealth();
EntityStatValue health = statMap.get(healthIndex);
float current = health.get(); // current value
float min = health.getMin(); // minimum (usually 0)
float max = health.getMax(); // maximum (e.g. 100)
float percent = health.asPercentage(); // (current - min) / (max - min)
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);

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 key
statMap.removeModifier(healthIndex, "my_mod_health_bonus");
// check if modifier exists
Modifier existing = statMap.getModifier(healthIndex, "my_mod_health_bonus");

Each modifier has:

PropertyOptionsDescription
TargetMIN, MAXWhich bound to modify
Calculation typeADDITIVE, MULTIPLICATIVEFlat addition or percentage multiplier
AmountfloatThe 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.

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 environment
Damage envDamage = new Damage(
new Damage.EnvironmentSource("lava"),
DamageCause.ENVIRONMENT,
10.0f
);
DamageSystems.executeDamage(targetRef, componentAccessor, envDamage);
// damage from a command
Damage 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 amount

Every Damage event has a source identifying where the damage came from:

SourceConstructorDescription
Damage.EntitySourcenew EntitySource(attackerRef)Melee or direct entity damage
Damage.ProjectileSourcenew ProjectileSource(shooterRef, projectileRef)Projectile damage (extends EntitySource)
Damage.EnvironmentSourcenew EnvironmentSource("type")Environmental damage (lava, traps, etc.)
Damage.CommandSourcenew 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.

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:

CauseConstantDescription
PhysicalDamageCause.PHYSICALMelee and physical attacks
ProjectileDamageCause.PROJECTILERanged projectile damage
CommandDamageCause.COMMANDDamage from commands
DrowningDamageCause.DROWNINGUnderwater suffocation
EnvironmentDamageCause.ENVIRONMENTLava, traps, etc.
FallDamageCause.FALLFall damage
Out of WorldDamageCause.OUT_OF_WORLDBelow the world boundary
SuffocationDamageCause.SUFFOCATIONInside solid blocks

Damage causes support inheritance — armor resistance against a parent cause also applies to child causes.

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 component
  • PlayerDamageFilterSystem — cancels if player has spawn protection or if PvP is disabled
  • ArmorDamageReduction — 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.

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:

  1. Health cleared — health set to 0
  2. Effects cleared — all active effects removed
  3. Interactions cleared — ongoing combat chains and abilities cancelled
  4. Death animation — plays the death animation from the DamageCause
  5. Item loss — items dropped based on the world’s DeathConfig (modes: NONE, ALL, CONFIGURED)
  6. Kill feedKillFeedEvent dispatched to broadcast death/kill messages
  7. Death screenRespawnPage opened for players (if showDeathMenu is true)
  8. Death marker — position recorded as a world map marker
  9. Corpse removal — non-player entities removed after death animation

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.

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 immediately
DeathComponent death = store.getComponent(ref, DeathComponent.getComponentType());
if (death != null) {
death.setShowDeathMenu(false);
DeathComponent.respawn(componentAccessor, ref);
}
DeathComponent death = store.getComponent(ref, DeathComponent.getComponentType());
if (death != null) {
// entity is dead, waiting for respawn
Message deathMessage = death.getDeathMessage();
}

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 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:

  1. Teleport — the respawn controller selects the destination and teleports the player
  2. Stats reset — all stats restored to their reset value (initial or max, per stat configuration)
  3. Effects cleared — all status effects removed
  4. Spawn protection — brief invulnerability period starts
// trigger respawn programmatically (async — teleport + cleanup happens in the future)
CompletableFuture<Void> future = DeathComponent.respawn(componentAccessor, ref);

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 HP

Or 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);