Files
Documentation/content/advanced/effects/entity-effects.en.md
2026-01-20 20:33:59 +01:00

370 lines
9.8 KiB
Markdown

---
title: Entity Effects
type: docs
weight: 3
---
Entity effects are status effects that can include visual components, stat modifiers, and duration-based behaviors.
## EntityEffect Configuration
```java
public class EntityEffect {
protected String id;
protected String name; // Localization key
protected ApplicationEffects applicationEffects; // Visual/audio effects
protected String modelChange; // Change entity model
protected float duration; // Default duration (seconds)
protected boolean infinite; // Never expires?
protected boolean debuff; // Is a negative effect?
protected String statusEffectIcon; // UI icon
// Damage and stats
protected Int2FloatMap entityStats; // Stat modifiers
protected ValueType valueType; // Absolute or Percent
// Behavior
protected OverlapBehavior overlapBehavior; // EXTEND, OVERWRITE, IGNORE
protected RemovalBehavior removalBehavior; // COMPLETE, DURATION, INFINITE
protected boolean invulnerable; // Grant invulnerability
}
```
## Accessing Effect Assets
```java
// Get effect by ID
EntityEffect effect = EntityEffect.getAssetMap().getAsset("fire_resistance");
// Get effect index for operations
int effectIndex = EntityEffect.getAssetMap().getIndex(effect.getId());
```
## EffectControllerComponent
The `EffectControllerComponent` manages active effects on an entity:
```java
import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent;
// Get effect controller from entity
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
```
## Adding Effects
### Basic Addition
```java
EntityEffect effect = EntityEffect.getAssetMap().getAsset("speed_boost");
// Add with default parameters
controller.addEffect(
entityRef,
effect,
componentAccessor
);
```
### With Custom Duration
```java
// Add with custom duration and overlap behavior
controller.addEffect(
entityRef,
effect,
100.0f, // Duration in seconds
OverlapBehavior.EXTEND, // How to handle overlap
componentAccessor
);
```
### Infinite Effects
```java
// Add an effect that never expires
controller.addInfiniteEffect(
entityRef,
EntityEffect.getAssetMap().getIndex(effect.getId()),
effect,
componentAccessor
);
```
## Removing Effects
```java
// Remove specific effect
controller.removeEffect(
entityRef,
EntityEffect.getAssetMap().getIndex(effect.getId()),
componentAccessor
);
```
## Overlap Behaviors
```java
public enum OverlapBehavior {
EXTEND, // Add duration to existing effect
OVERWRITE, // Replace existing effect
IGNORE // Don't apply if already active
}
```
### Usage Examples
```java
// Extend duration if already applied
controller.addEffect(
entityRef,
effect,
30.0f,
OverlapBehavior.EXTEND,
componentAccessor
);
// Replace with fresh duration
controller.addEffect(
entityRef,
effect,
30.0f,
OverlapBehavior.OVERWRITE,
componentAccessor
);
// Only apply if not already active
controller.addEffect(
entityRef,
effect,
30.0f,
OverlapBehavior.IGNORE,
componentAccessor
);
```
## Removal Behaviors
```java
public enum RemovalBehavior {
COMPLETE, // Remove when complete
DURATION, // Remove after duration
INFINITE // Never remove automatically
}
```
## Value Types for Stats
```java
public enum ValueType {
Absolute, // Add/subtract flat value
Percent // Multiply by percentage
}
```
## Practical Examples
### Buff on Command
```java
public class BuffCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
private final RequiredArg<String> effectArg;
private final RequiredArg<Integer> durationArg;
// Would be set during plugin initialization
private Store<EntityStore> store;
private ComponentAccessor<EntityStore> componentAccessor;
public BuffCommand() {
super("buff", "effects.command.buff.description");
this.playerArg = withRequiredArg("player", "Target player", ArgTypes.PLAYER_REF);
this.effectArg = withRequiredArg("effect", "Effect ID", ArgTypes.STRING);
this.durationArg = withRequiredArg("duration", "Duration in seconds", ArgTypes.INTEGER);
}
@Override
protected CompletableFuture<Void> execute(CommandContext ctx) {
PlayerRef target = playerArg.get(ctx);
String effectId = effectArg.get(ctx);
int duration = durationArg.get(ctx);
Ref<EntityStore> entityRef = target.getReference();
if (entityRef == null || !entityRef.isValid()) {
ctx.sender().sendMessage(Message.raw("Player not found"));
return null;
}
EntityEffect effect = EntityEffect.getAssetMap().getAsset(effectId);
if (effect == null) {
ctx.sender().sendMessage(Message.raw("Unknown effect: " + effectId));
return null;
}
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null) {
controller.addEffect(
entityRef,
effect,
(float) duration,
OverlapBehavior.EXTEND,
componentAccessor
);
}
ctx.sender().sendMessage(Message.raw("Applied " + effectId + " to " + target.getUsername()));
return null;
}
}
```
{{< callout type="warning" >}}
**API Notes:**
- Use `withRequiredArg()` to define arguments, not `addArgument()`
- Use `arg.get(ctx)` to retrieve values, not `ctx.getArg()`
- Use `ctx.sender()` not `ctx.getSender()`
- Use `target.getUsername()` not `target.getName()`
- Use `target.getReference()` to get entity reference (PlayerRef doesn't have `getPlayer()`)
{{< /callout >}}
### Damage Over Time Effect
```java
public void applyPoison(Player player, float duration, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
EntityEffect poison = EntityEffect.getAssetMap().getAsset("poison");
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && poison != null) {
controller.addEffect(
entityRef,
poison,
duration,
OverlapBehavior.EXTEND,
componentAccessor
);
}
player.sendMessage(Message.raw("You have been poisoned!"));
}
```
### Temporary Invulnerability
```java
public void grantInvulnerability(Player player, float seconds, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
EntityEffect invuln = EntityEffect.getAssetMap().getAsset("invulnerability");
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && invuln != null) {
controller.addEffect(
entityRef,
invuln,
seconds,
OverlapBehavior.OVERWRITE, // Fresh duration
componentAccessor
);
}
}
```
### Clear All Effects
```java
public void clearAllEffects(Player player, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null) {
// Use the built-in clearEffects method
controller.clearEffects(
entityRef,
componentAccessor
);
}
player.sendMessage(Message.raw("All effects cleared!"));
}
```
## Combining with Other Effects
Entity effects work well with particles and dynamic lights:
```java
public void applyMagicBuff(Player player, Store<EntityStore> store,
ComponentAccessor<EntityStore> componentAccessor) {
Ref<EntityStore> entityRef = player.getReference();
// Apply status effect
EntityEffect buff = EntityEffect.getAssetMap().getAsset("magic_power");
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && buff != null) {
controller.addEffect(entityRef, buff, componentAccessor);
}
// Add visual glow
ColorLight glow = new ColorLight((byte) 10, (byte) 200, (byte) 50, (byte) 255);
DynamicLight light = new DynamicLight(glow);
componentAccessor.putComponent(
entityRef,
DynamicLight.getComponentType(),
light
);
// Get position from TransformComponent for particle spawn
TransformComponent transform = store.getComponent(entityRef, TransformComponent.getComponentType());
if (transform != null) {
ParticleUtil.spawnParticleEffect(
"magic_aura",
transform.getPosition(),
componentAccessor
);
}
}
```
{{< callout type="info" >}}
**Note:** Use `player.getReference()` to get the entity reference. Position must be obtained from `TransformComponent`, not directly from Player.
{{< /callout >}}
## Best Practices
{{< callout type="info" >}}
**Effect Guidelines:**
- Use `EXTEND` for stackable buffs to reward repeated application
- Use `OVERWRITE` for effects that should reset duration
- Use `IGNORE` to prevent effect stacking when undesired
- Always check if effect exists before applying
- Consider performance with many simultaneous effects
{{< /callout >}}