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 block sound expression and test #7040

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

import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.expressions.base.SimplePropertyExpression;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.util.Kleenean;
import org.bukkit.Sound;
import org.bukkit.SoundGroup;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;

@Name("Block Sound")
@Description({
"Gets the sound that a given block, blockdata, or itemtype will use in a specific scenario.",
"This will return a string in the form of \"SOUND_EXAMPLE\", which can be used in the play sound syntax.",
"",
"Check out <a href=\"https://minecraft.wiki/w/Sounds.json\">this website</a> for a list of sounds in Minecraft, " +
"or <a href=\"https://minecraft.wiki/w/Sound\">this one</a> to go to the Sounds wiki page."
})
@Examples({
"play sound (break sound of dirt) at all players",
"set {_sounds::*} to place sounds of dirt, grass block, blue wool and stone"
})
@Since("INSERT VERSION")
public class ExprBlockSound extends SimpleExpression<String> {

public enum SoundType {
BREAK {
@Override
public Sound getSound(SoundGroup group) {
return group.getBreakSound();
}
},

FALL {
@Override
public Sound getSound(SoundGroup group) {
return group.getFallSound();
}
},

HIT {
@Override
public Sound getSound(SoundGroup group) {
return group.getHitSound();
}
},

PLACE {
@Override
public Sound getSound(SoundGroup group) {
return group.getPlaceSound();
}
},

STEP {
@Override
public Sound getSound(SoundGroup group) {
return group.getStepSound();
}
};

public abstract @Nullable Sound getSound(SoundGroup group);
}

static {
SimplePropertyExpression.register(ExprBlockSound.class, String.class, "(1:break|2:fall|3:hit|4:place|5:step) sound[s]", "blocks/blockdatas/itemtypes");
}

@SuppressWarnings("NotNullFieldNotInitialized")
private SoundType soundType;
@SuppressWarnings("NotNullFieldNotInitialized")
private Expression<?> objects;

@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
soundType = SoundType.values()[parseResult.mark - 1];
objects = exprs[0];
return true;
}

@Override
protected String @Nullable [] get(Event event) {
return objects.stream(event)
.map(this::convertAndGetSound)
.filter(Objects::nonNull)
.distinct()
.map(Sound::name)
.toArray(String[]::new);
}

private @Nullable SoundGroup getSoundGroup(Object object) {
if (object instanceof Block block) {
return block.getBlockData().getSoundGroup();
} else if (object instanceof BlockData data) {
return data.getSoundGroup();
} else if (object instanceof ItemType item) {
if (item.hasBlock())
return item.getMaterial().createBlockData().getSoundGroup();
}
return null;
}

private @Nullable Sound convertAndGetSound(Object object) {
SoundGroup group = getSoundGroup(object);
if (group == null)
return null;
return this.soundType.getSound(group);
}

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

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

@Override
public @NotNull String toString(@Nullable Event event, boolean debug) {
return this.soundType.name().toLowerCase() + " sound of " + objects.toString(event, debug);
}

}
46 changes: 46 additions & 0 deletions src/test/skript/tests/syntaxes/expressions/ExprBlockSound.sk
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
test "block sounds (1.20+)":

# === SETUP ===

set {_before} to blockdata of (block at (spawn of world "world"))
set {_20} to whether running minecraft "1.20"
set {_empty} to "INTENTIONALLY_EMPTY" if running minecraft "1.20" else "BLOCK_STONE_PLACE"

# === TESTS ===

set {_stone::*} to getObjects(stone)
assert break sound of {_stone::*} is "BLOCK_STONE_BREAK" with "break sound of stone wasn't BLOCK_STONE_BREAK"
assert fall sound of {_stone::*} is "BLOCK_STONE_FALL" with "fall sound of stone wasn't BLOCK_STONE_FALL"
assert hit sound of {_stone::*} is "BLOCK_STONE_HIT" with "hit sound of stone wasn't BLOCK_STONE_HIT"
assert place sound of {_stone::*} is "BLOCK_STONE_PLACE" with "place sound of stone wasn't BLOCK_STONE_PLACE"
assert step sound of {_stone::*} is "BLOCK_STONE_STEP" with "step sound of stone wasn't BLOCK_STONE_STEP"

set {_wool::*} to getObjects(wool)
assert break sound of {_wool::*} is "BLOCK_WOOL_BREAK" with "break sound of stone wasn't BLOCK_WOOL_BREAK"
assert fall sound of {_wool::*} is "BLOCK_WOOL_FALL" with "fall sound of stone wasn't BLOCK_WOOL_FALL"
assert hit sound of {_wool::*} is "BLOCK_WOOL_HIT" with "hit sound of stone wasn't BLOCK_WOOL_HIT"
assert place sound of {_wool::*} is "BLOCK_WOOL_PLACE" with "place sound of stone wasn't BLOCK_WOOL_PLACE"
assert step sound of {_wool::*} is "BLOCK_WOOL_STEP" with "step sound of stone wasn't BLOCK_WOOL_STEP"

assert break sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK_STONE_BREAK") with "break sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY"
assert fall sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK_STONE_FALL") with "fall sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY"
assert hit sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK_STONE_HIT") with "hit sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY"
assert place sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK_STONE_PLACE") with "place sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY"
assert step sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK_STONE_STEP") with "step sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY"

assert break sound of (diamond, diamond sword and {_none}) is not set with "break sound of non-block item shouldn't be set"
assert fall sound of (diamond, diamond sword and {_none}) is not set with "fall sound of non-block item shouldn't be set"
assert hit sound of (diamond, diamond sword and {_none}) is not set with "hit sound of non-block item shouldn't be set"
assert place sound of (diamond, diamond sword and {_none}) is not set with "place sound of non-block item shouldn't be set"
assert step sound of (diamond, diamond sword and {_none}) is not set with "step sound of non-block item shouldn't be set"

cheeezburga marked this conversation as resolved.
Show resolved Hide resolved
# === CLEANUP ===

set blockdata of {_block} to {_before}

local function getObjects(i: itemtype) :: objects:
if (block at (spawn of world "world")) is not {_i}:
set block at (spawn of world "world") to {_i}
set {_block} to block at (spawn of world "world")
set {_data} to blockdata of {_block}
return {_block}, {_data} and {_i}