Entity Effects
The Entity Effects system manages temporary and permanent status effects applied to entities, including buffs, debuffs, damage over time, and stat modifiers.
Core Components
Section titled “Core Components”EffectControllerComponent
Section titled “EffectControllerComponent”Manages all active effects on an entity.
Location: com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent
// Get component typeComponentType<EntityStore, EffectControllerComponent> type = EffectControllerComponent.getComponentType();
// Get controller from entityEffectControllerComponent controller = store.getComponent(entityRef, type);Key Methods:
// Add an effectboolean added = controller.addEffect( entityRef, // Entity to apply to entityEffect, // Effect configuration componentAccessor // Component accessor);
// Add with custom durationcontroller.addEffect( entityRef, entityEffect, customDuration, // Duration in seconds OverlapBehavior.EXTEND, componentAccessor);
// Add infinite effectcontroller.addInfiniteEffect( entityRef, entityEffectIndex, entityEffect, componentAccessor);
// Remove an effectcontroller.removeEffect( entityRef, entityEffectIndex, componentAccessor);
// Clear all effectscontroller.clearEffects(entityRef, componentAccessor);
// Get active effectsInt2ObjectMap<ActiveEntityEffect> activeEffects = controller.getActiveEffects();int[] effectIndexes = controller.getActiveEffectIndexes();ActiveEntityEffect
Section titled “ActiveEntityEffect”Represents an active effect instance with remaining duration and state.
Location: com.hypixel.hytale.server.core.entity.effect.ActiveEntityEffect
// Effect propertiesint effectIndex = activeEffect.getEntityEffectIndex();float initialDuration = activeEffect.getInitialDuration();float remainingDuration = activeEffect.getRemainingDuration();boolean isInfinite = activeEffect.isInfinite();boolean isDebuff = activeEffect.isDebuff();boolean isInvulnerable = activeEffect.isInvulnerable();Tick Processing:
- Updates remaining duration
- Applies stat modifiers periodically
- Applies damage over time
- Handles effect expiration
Entity Effect Configuration
Section titled “Entity Effect Configuration”Effects are configured through JSON assets.
EntityEffect
Section titled “EntityEffect”Location: com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect
Asset Path: EntityEffect/{id}.json
{ "Name": "effect.poison.name", "Duration": 10.0, "Infinite": false, "Debuff": true, "StatusEffectIcon": "hytale:icon/poison", "OverlapBehavior": "Extend", "RemovalBehavior": "Complete", "Invulnerable": false, "StatModifiers": { "hytale:health": -5.0, "hytale:movement_speed": -0.2 }, "ValueType": "Percent", "DamageCalculator": { "Type": "OverTime", "DamageCauses": { "Poison": 2.0 } }, "DamageCalculatorCooldown": 1.0, "DamageEffects": { "Particles": ["hytale:poison_bubble"], "Sounds": ["hytale:damage_poison"] }, "StatModifierEffects": { "Particles": ["hytale:debuff_sparkle"] }, "ApplicationEffects": { "Particles": ["hytale:poison_cloud"], "Sounds": ["hytale:poison_apply"] }, "ModelChange": "hytale:poisoned_texture", "WorldRemovalSoundEventId": "hytale:poison_fade", "LocalRemovalSoundEventId": "hytale:poison_cure"}Configuration Fields:
Basic Properties:
Name- Localization key for display nameDuration- Default duration in secondsInfinite- Whether effect lasts forever by defaultDebuff- True if negative effect (shown as debuff)StatusEffectIcon- Icon asset ID for UILocale- Optional death message locale key
Behavior:
OverlapBehavior- How to handle applying same effect twice:Replace- Replace existing effectExtend- Add duration to existingIgnore- Keep existing, ignore new
RemovalBehavior- How effect is removed:Complete- Remove immediatelyInfinite- Stop infinite flagDuration- Set duration to 0
Invulnerable- Makes entity invulnerable while active
Stat Modifiers:
StatModifiers- Map of stat IDs to valuesValueType- How values are applied:Absolute- Direct value changePercent- Percentage of max value
StatModifierEffects- Effects when stats updateRawStatModifiers- Advanced stat modifiers with priorities
Damage Over Time:
DamageCalculator- Damage configurationDamageCalculatorCooldown- Seconds between damage ticksDamageEffects- Effects when damage is appliedDamageResistance- Resistance values by damage cause
Visual Effects:
ApplicationEffects- Effects when first appliedModelChange- Change entity model while activeModelOverride- Advanced model override settings
Audio:
WorldRemovalSoundEventId- 3D sound when removedLocalRemovalSoundEventId- Sound for affected entity
Overlap Behavior
Section titled “Overlap Behavior”Location: com.hypixel.hytale.server.core.asset.type.entityeffect.config.OverlapBehavior
public enum OverlapBehavior { Replace, // Replace existing effect Extend, // Add duration to existing Ignore // Keep existing effect}Removal Behavior
Section titled “Removal Behavior”Location: com.hypixel.hytale.server.core.asset.type.entityeffect.config.RemovalBehavior
public enum RemovalBehavior { Complete, // Remove effect completely Infinite, // Clear infinite flag Duration // Set duration to 0}Applying Effects
Section titled “Applying Effects”Basic Application
Section titled “Basic Application”// Get effect from asset storeEntityEffect effect = EntityEffect.getAssetMap().getAsset("hytale:poison");
// Get entity's effect controllerEffectControllerComponent controller = componentAccessor.getComponent( entityRef, EffectControllerComponent.getComponentType());
// Apply effectboolean applied = controller.addEffect( entityRef, effect, componentAccessor);Custom Duration
Section titled “Custom Duration”// Apply with custom duration (30 seconds)controller.addEffect( entityRef, effect, 30.0f, // Duration OverlapBehavior.Extend, // Extend if already active componentAccessor);Infinite Effects
Section titled “Infinite Effects”// Apply infinite effectint effectIndex = EntityEffect.getAssetMap().getIndex("hytale:invulnerable");EntityEffect effect = EntityEffect.getAssetMap().getAsset(effectIndex);
controller.addInfiniteEffect( entityRef, effectIndex, effect, componentAccessor);Removing Effects
Section titled “Removing Effects”Remove Specific Effect
Section titled “Remove Specific Effect”// Get effect indexint effectIndex = EntityEffect.getAssetMap().getIndex("hytale:poison");
// Remove effectcontroller.removeEffect( entityRef, effectIndex, componentAccessor);
// Remove with specific behaviorcontroller.removeEffect( entityRef, effectIndex, RemovalBehavior.Complete, componentAccessor);Clear All Effects
Section titled “Clear All Effects”// Remove all effects from entitycontroller.clearEffects(entityRef, componentAccessor);Checking Active Effects
Section titled “Checking Active Effects”Check if Effect Active
Section titled “Check if Effect Active”EffectControllerComponent controller = /* get controller */;int poisonIndex = EntityEffect.getAssetMap().getIndex("hytale:poison");
// Get active effectActiveEntityEffect activeEffect = controller.getActiveEffects().get(poisonIndex);if (activeEffect != null) { // Effect is active float remainingTime = activeEffect.getRemainingDuration();}Iterate Active Effects
Section titled “Iterate Active Effects”Int2ObjectMap<ActiveEntityEffect> activeEffects = controller.getActiveEffects();
for (Int2ObjectMap.Entry<ActiveEntityEffect> entry : activeEffects.int2ObjectEntrySet()) { int effectIndex = entry.getIntKey(); ActiveEntityEffect effect = entry.getValue();
System.out.println("Effect: " + effectIndex + ", Remaining: " + effect.getRemainingDuration() + ", Infinite: " + effect.isInfinite());}Stat Modifiers
Section titled “Stat Modifiers”Effects can modify entity stats while active.
Absolute Value Modifiers
Section titled “Absolute Value Modifiers”{ "StatModifiers": { "hytale:health": 20.0, "hytale:movement_speed": 2.0 }, "ValueType": "Absolute"}Adds fixed values to stats (e.g., +20 health, +2 speed).
Percentage Modifiers
Section titled “Percentage Modifiers”{ "StatModifiers": { "hytale:health": -50.0, "hytale:damage": 25.0 }, "ValueType": "Percent"}Modifies stats by percentage (e.g., -50% health, +25% damage).
Advanced Modifiers
Section titled “Advanced Modifiers”{ "RawStatModifiers": { "hytale:health": [ { "Value": 10.0, "Priority": 100, "Type": "Add" } ] }}Provides fine-grained control over modifier application order and type.
Damage Over Time
Section titled “Damage Over Time”Effects can deal periodic damage.
Damage Configuration
Section titled “Damage Configuration”{ "DamageCalculator": { "Type": "OverTime", "DamageCauses": { "Poison": 1.0, "Magic": 0.5 } }, "DamageCalculatorCooldown": 1.0, "DamageEffects": { "Particles": ["hytale:damage_indicator"], "Sounds": ["hytale:hurt"] }}Fields:
DamageCalculator- Damage calculation configurationDamageCalculatorCooldown- Seconds between damage ticks (0 = once on apply)DamageEffects- Visual/audio effects when damage is dealt
Damage Calculation
Section titled “Damage Calculation”Damage is calculated based on effect duration:
// In ActiveEntityEffect.tick()int cyclesToRun = calculateCyclesToRun(entityEffect, dt);if (cyclesToRun > 0) { // Apply damage for each cycle DamageCalculator calculator = entityEffect.getDamageCalculator(); Object2FloatMap<DamageCause> damages = calculator.calculateDamage(initialDuration);
// Queue damage events for (Damage damageEvent : createDamageEvents(damages)) { commandBuffer.invoke(entityRef, damageEvent); }}Model Changes
Section titled “Model Changes”Effects can change an entity’s appearance.
Simple Model Change
Section titled “Simple Model Change”{ "ModelChange": "hytale:model/poisoned"}Replaces entity model while effect is active.
Original Model Restoration
Section titled “Original Model Restoration”The controller automatically stores and restores the original model:
// In EffectControllerComponentpublic void setModelChange( Ref<EntityStore> ownerRef, EntityEffect entityEffect, int entityEffectIndex, ComponentAccessor<EntityStore> componentAccessor) { if (this.originalModel != null) return;
// Store original ModelComponent modelComponent = componentAccessor.getComponent( ownerRef, ModelComponent.getComponentType() ); this.originalModel = modelComponent.getModel();
// Apply new model ModelAsset newModel = ModelAsset.getAssetMap() .getAsset(entityEffect.getModelChange()); componentAccessor.putComponent( ownerRef, ModelComponent.getComponentType(), new ModelComponent(Model.createRandomScaleModel(newModel)) );}Invulnerability
Section titled “Invulnerability”Effects can make entities invulnerable.
{ "Invulnerable": true}While any invulnerable effect is active, the entity cannot take damage.
// Check if entity is invulnerable via effectsEffectControllerComponent controller = /* get controller */;boolean isInvulnerable = controller.isInvulnerable();Application Effects
Section titled “Application Effects”Visual and audio feedback when effect is applied.
ApplicationEffects
Section titled “ApplicationEffects”Location: com.hypixel.hytale.server.core.asset.type.entityeffect.config.ApplicationEffects
{ "ApplicationEffects": { "Particles": [ "hytale:magic_sparkle", "hytale:buff_glow" ], "Sounds": [ "hytale:buff_apply" ], "Animation": "hytale:player/receive_buff" }}Network Synchronization
Section titled “Network Synchronization”Effect changes are tracked and synchronized to clients.
// Controller tracks changesboolean isOutdated = controller.consumeNetworkOutdated();EntityEffectUpdate[] changes = controller.consumeChanges();controller.clearChanges();
// Create initial updates for new clientsEntityEffectUpdate[] initUpdates = controller.createInitUpdates();EntityEffectUpdate contains:
- Operation type (Add/Remove)
- Effect index
- Remaining duration
- Infinite flag
- Debuff flag
- Status icon
Interaction Integration
Section titled “Interaction Integration”Effects can be applied via interactions.
ApplyEffectInteraction
Section titled “ApplyEffectInteraction”{ "Type": "ApplyEffect", "EntityEffect": "hytale:poison", "Duration": 10.0, "OverlapBehavior": "Extend"}ClearEntityEffectInteraction
Section titled “ClearEntityEffectInteraction”{ "Type": "ClearEntityEffect", "EntityEffect": "hytale:poison"}EffectConditionInteraction
Section titled “EffectConditionInteraction”Check if entity has specific effect:
{ "Type": "EffectCondition", "EntityEffect": "hytale:burning", "HasEffect": true, "Interactions": ["hytale:take_fire_damage"]}Examples
Section titled “Examples”Poison Effect
Section titled “Poison Effect”{ "Name": "effect.poison.name", "Duration": 10.0, "Debuff": true, "StatusEffectIcon": "hytale:icon/poison", "OverlapBehavior": "Extend", "DamageCalculator": { "Type": "OverTime", "DamageCauses": { "Poison": 1.0 } }, "DamageCalculatorCooldown": 1.0, "DamageEffects": { "Particles": ["hytale:poison_bubble"] }, "ApplicationEffects": { "Particles": ["hytale:poison_cloud"], "Sounds": ["hytale:poison_apply"] }}Speed Boost
Section titled “Speed Boost”{ "Name": "effect.speed.name", "Duration": 30.0, "Debuff": false, "StatusEffectIcon": "hytale:icon/speed", "OverlapBehavior": "Extend", "StatModifiers": { "hytale:movement_speed": 50.0 }, "ValueType": "Percent", "ApplicationEffects": { "Particles": ["hytale:speed_lines"], "Sounds": ["hytale:buff_apply"] }}Regeneration
Section titled “Regeneration”{ "Name": "effect.regeneration.name", "Duration": 20.0, "Debuff": false, "StatusEffectIcon": "hytale:icon/regen", "OverlapBehavior": "Replace", "StatModifiers": { "hytale:health": 1.0 }, "ValueType": "Absolute", "DamageCalculatorCooldown": 0.5, "StatModifierEffects": { "Particles": ["hytale:heal_sparkle"] }}Invulnerability
Section titled “Invulnerability”{ "Name": "effect.invulnerable.name", "Duration": 5.0, "Debuff": false, "StatusEffectIcon": "hytale:icon/shield", "OverlapBehavior": "Ignore", "Invulnerable": true, "ModelChange": "hytale:player_glowing", "ApplicationEffects": { "Particles": ["hytale:golden_shield"], "Sounds": ["hytale:shield_activate"] }}Complete Example in Code
Section titled “Complete Example in Code”public class EffectManager {
// Apply poison to entity public void applyPoison( Ref<EntityStore> targetRef, ComponentAccessor<EntityStore> accessor, float duration ) { // Get effect config EntityEffect poison = EntityEffect.getAssetMap() .getAsset("hytale:poison"); if (poison == null) return;
// Get or add effect controller EffectControllerComponent controller = accessor.getComponent( targetRef, EffectControllerComponent.getComponentType() ); if (controller == null) { controller = new EffectControllerComponent(); accessor.putComponent( targetRef, EffectControllerComponent.getComponentType(), controller ); }
// Apply effect with custom duration controller.addEffect( targetRef, poison, duration, OverlapBehavior.Extend, accessor ); }
// Check if entity is poisoned public boolean isPoisoned( Ref<EntityStore> entityRef, ComponentAccessor<EntityStore> accessor ) { EffectControllerComponent controller = accessor.getComponent( entityRef, EffectControllerComponent.getComponentType() ); if (controller == null) return false;
int poisonIndex = EntityEffect.getAssetMap() .getIndex("hytale:poison"); return controller.getActiveEffects().containsKey(poisonIndex); }
// Cure all debuffs public void cureDebuffs( Ref<EntityStore> entityRef, ComponentAccessor<EntityStore> accessor ) { EffectControllerComponent controller = accessor.getComponent( entityRef, EffectControllerComponent.getComponentType() ); if (controller == null) return;
// Find all debuffs List<Integer> debuffsToRemove = new ArrayList<>(); for (Int2ObjectMap.Entry<ActiveEntityEffect> entry : controller.getActiveEffects().int2ObjectEntrySet()) { if (entry.getValue().isDebuff()) { debuffsToRemove.add(entry.getIntKey()); } }
// Remove them for (int effectIndex : debuffsToRemove) { controller.removeEffect(entityRef, effectIndex, accessor); } }}Effect Systems
Section titled “Effect Systems”Effects are processed by the LivingEntityEffectSystem from the EntityModule, which:
- Ticks all active effects each frame
- Updates durations and removes expired effects
- Applies periodic damage
- Updates stat modifiers
- Triggers effect-related interactions
The system iterates through all entities with EffectControllerComponent and calls ActiveEntityEffect.tick() on each active effect.