Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds entity sound expression and test #7019

Merged
merged 11 commits into from
Oct 13, 2024
145 changes: 145 additions & 0 deletions src/main/java/ch/njol/skript/expressions/ExprEntitySound.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package ch.njol.skript.expressions;

import ch.njol.skript.Skript;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.util.Kleenean;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;

public class ExprEntitySound extends SimpleExpression<String> {

static {
if (Skript.methodExists(LivingEntity.class, "getDeathSound")) {
Skript.registerExpression(ExprEntitySound.class, String.class, ExpressionType.COMBINED,
"[the] (damage|hurt) sound[s] of %livingentities%",
"%livingentities%'[s] (damage|hurt) sound[s]",

"[the] death sound[s] of %livingentities%",
"%livingentities%'[s] death sound[s]",

"[the] [high:(tall|high)|(low|normal)] fall [damage] sound[s] [from [[a] height [of]] %-number%] of %livingentities%",
"%livingentities%'[s] [high:(tall|high)|low:(low|normal)] fall [damage] sound[s] [from [[a] height [of]] %-number%]",

"[the] swim[ming] sound[s] of %livingentities%",
"%livingentities%'[s] swim[ming] sound[s]",

"[the] [fast:(fast|speedy)] splash sound[s] of %livingentities%",
"%livingentities%'[s] [fast:(fast|speedy)] splash sound[s]",

"[the] eat[ing] sound of %livingentities% [(with|using|[while] eating [a]) %-itemtype%]",
"%livingentities%'[s] eat[ing] sound",

"[the] drink[ing] sound of %livingentities% [(with|using|[while] drinking [a]) %-itemtype%]",
"%livingentities%'[s] drink[ing] sound");
}
}

private static final int DAMAGE = 0;
private static final int DEATH = 2;
private static final int FALL = 4;
private static final int SWIM = 6;
private static final int SPLASH = 8;
private static final int EAT_WITH_ITEM = 10;
private static final int EAT = 11;
private static final int DRINK_WITH_ITEM = 12;
private static final int DRINK = 13;

private int soundPattern;
private boolean big;
@SuppressWarnings("NotNullFieldNotInitialized")
private Expression<Number> height;
@SuppressWarnings("NotNullFieldNotInitialized")
private Expression<LivingEntity> entities;
@SuppressWarnings("NotNullFieldNotInitialized")
private Expression<ItemType> item;

@Override
@SuppressWarnings("unchecked")
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
soundPattern = matchedPattern;
big = parseResult.hasTag("high") || parseResult.hasTag("fast");
if (matchedPattern == FALL || matchedPattern == FALL + 1)
height = (Expression<Number>) exprs[0];
if (matchedPattern == EAT_WITH_ITEM || matchedPattern == DRINK_WITH_ITEM)
item = (Expression<ItemType>) exprs[1];
entities = (Expression<LivingEntity>) ((matchedPattern == FALL) ? exprs[1] : exprs[0]);
return true;
}

@Override
@SuppressWarnings("ConstantValue")
cheeezburga marked this conversation as resolved.
Show resolved Hide resolved
protected String @Nullable [] get(Event event) {
int height = this.height == null ? -1 : this.height.getOptionalSingle(event).orElse(-1).intValue();

ItemStack defaultItem = new ItemStack(soundPattern == EAT_WITH_ITEM ? Material.COOKED_BEEF : Material.POTION);
ItemStack item = this.item == null ? defaultItem : this.item.getOptionalSingle(event).map(ItemType::getRandom).orElse(defaultItem);

return entities.stream(event)
.map(entity -> getEntitySound(entity, height, item))
.filter(Objects::nonNull)
.distinct()
.map(Sound::name)
.toArray(String[]::new);
}

private @Nullable Sound getEntitySound(LivingEntity entity, int height, ItemStack item) {
return switch (this.soundPattern) {
case DAMAGE, DAMAGE + 1 -> entity.getHurtSound();
case DEATH, DEATH + 1 -> entity.getDeathSound();
case FALL, FALL + 1 -> {
if (height != -1)
yield entity.getFallDamageSound(height);
else
yield big ? entity.getFallDamageSoundBig() : entity.getFallDamageSoundSmall();
}
case SWIM, SWIM + 1 -> entity.getSwimSound();
case SPLASH, SPLASH + 1 -> big ? entity.getSwimHighSpeedSplashSound() : entity.getSwimSplashSound();
case EAT, EAT_WITH_ITEM -> entity.getEatingSound(item);
case DRINK, DRINK_WITH_ITEM -> entity.getDrinkingSound(item);
default -> null;
};
}

@Override
public boolean isSingle() {
return entities.isSingle();
}

@Override
public Class<? extends String> getReturnType() {
return String.class;
}

@Override
@SuppressWarnings("ConstantValue")
public String toString(@Nullable Event event, boolean debug) {
return switch (soundPattern) {
case DAMAGE, DAMAGE + 1 -> "damage";
case DEATH, DEATH + 1 -> "death";
case FALL, FALL + 1 -> {
if (this.height == null) {
yield big ? "high fall damage" : "normal fall damage";
} else {
yield "fall damage from a height of " + this.height.toString(event, debug);
}
}
case SWIM, SWIM + 1 -> "swim";
case SPLASH, SPLASH + 1 -> big ? "speedy splash" : "splash";
case EAT_WITH_ITEM, DRINK_WITH_ITEM -> (soundPattern == EAT_WITH_ITEM ? "eating" : "drinking") +
(this.item == null ? " with default item" : " " + this.item.toString(event, debug));
case EAT, DRINK -> soundPattern == EAT ? "eating" : "drinking";
default -> "unknown";
} + " sound of " + entities.toString(event, debug);
}

}
16 changes: 16 additions & 0 deletions src/test/skript/tests/syntaxes/expressions/ExprEntitySound.sk
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
test "entity sounds":
spawn a zombie at (spawn of world "world"):
cheeezburga marked this conversation as resolved.
Show resolved Hide resolved
assert damage sound of entity is "ENTITY_ZOMBIE_HURT" with "damage sound of zombie should return ENTITY_ZOMBIE_HURT"
assert death sound of entity is "ENTITY_ZOMBIE_DEATH" with "death sound of zombie should return ENTITY_ZOMBIE_DEATH"
assert swim sound of entity is "ENTITY_HOSTILE_SWIM" with "swim sound of zombie should return ENTITY_HOSTILE_SWIM"
assert normal fall sound of entity is "ENTITY_HOSTILE_SMALL_FALL" with "fall sound of zombie should return ENTITY_HOSTILE_SMALL_FALL"
assert high fall sound of entity is "ENTITY_HOSTILE_BIG_FALL" with "high fall sound of zombie should return ENTITY_HOSTILE_BIG_FALL"
assert fall sound from a height of 10 of entity is "ENTITY_HOSTILE_BIG_FALL" with "fall sound from height of 10 sound of zombie should return ENTITY_HOSTILE_BIG_FALL"
assert fall sound from a height of {_none} of entity is "ENTITY_HOSTILE_SMALL_FALL" with "fall sound from invalid height sound of zombie should return ENTITY_HOSTILE_SMALL_FALL"
assert splash sound of entity is "ENTITY_HOSTILE_SPLASH" with "splash sound of zombie should return ENTITY_HOSTILE_SPLASH"
assert speedy splash sound of entity is "ENTITY_GENERIC_SPLASH" with "speedy splash sound of zombie should return ENTITY_GENERIC_SPLASH"
assert eating sound of entity is "ENTITY_GENERIC_EAT" with "eating sound of zombie should return ENTITY_GENERIC_EAT"
assert eating sound of entity using golden apple is "ENTITY_GENERIC_EAT" with "eating sound of zombie using golden apple should return ENTITY_GENERIC_EAT"
assert drinking sound of entity is "ENTITY_GENERIC_DRINK" with "drinking sound of zombie should return ENTITY_GENERIC_DRINK"
assert drinking sound of entity using potion is "ENTITY_GENERIC_DRINK" with "drinking sound of zombie using potion should return ENTITY_GENERIC_DRINK"
delete entity