Skip to content

Cow Variants #7866

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

Open
wants to merge 4 commits into
base: dev/feature
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/main/java/ch/njol/skript/classes/data/BukkitClasses.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import ch.njol.skript.bukkitutil.SkriptTeleportFlag;
import ch.njol.skript.classes.*;
import ch.njol.skript.classes.registry.RegistryClassInfo;
import ch.njol.skript.entity.CowData.CowVariantDummy;
import ch.njol.skript.entity.EntityData;
import ch.njol.skript.entity.PigData.PigVariantDummy;
import ch.njol.skript.entity.WolfData.WolfVariantDummy;
Expand Down Expand Up @@ -1576,6 +1577,26 @@ public String toVariableNameString(WorldBorder border) {
.requiredPlugins("Minecraft 1.21.5+")
.documentationId("PigVariant"));

ClassInfo<?> cowVariantClassInfo = getRegistryClassInfo(
"org.bukkit.entity.Cow$Variant",
"COW_VARIANT",
"cowvariant",
"cow variants"
);
if (cowVariantClassInfo == null) {
// Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Cow.Variant' (1.21.4-)
cowVariantClassInfo = new ClassInfo<>(CowVariantDummy.class, "cowvariant");
}
Classes.registerClass(cowVariantClassInfo
.user("cow ?variants?")
.name("Cow Variant")
.description("Represents the variant of a cow entity.",
"NOTE: Minecraft namespaces are supported, ex: 'minecraft:warm'.")
.since("INSERT VERSION")
.requiredPlugins("Minecraft 1.21.5+")
.documentationId("CowVariant")
);

}

/**
Expand Down
184 changes: 184 additions & 0 deletions src/main/java/ch/njol/skript/entity/CowData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package ch.njol.skript.entity;

import ch.njol.skript.Skript;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.registrations.Classes;
import ch.njol.util.coll.CollectionUtils;
import com.google.common.collect.Iterators;
import org.bukkit.entity.Cow;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;

public class CowData extends EntityData<Cow> {

private static final boolean variantsEnabled;
private static final Object[] variants;
private static final Class<Cow> cowClass;
private static final @Nullable Method getVariantMethod;
private static final @Nullable Method setVariantMethod;

static {
Class<Cow> cowClass1 = null;

try {
//noinspection unchecked
cowClass1 = (Class<Cow>) Class.forName("org.bukkit.entity.Cow");
} catch (Exception ignored) {}

cowClass = cowClass1;
register(CowData.class, "cow", cowClass, 0, "cow");
if (Skript.classExists("org.bukkit.entity.Cow$Variant")) {
variantsEnabled = true;
variants = Iterators.toArray(Classes.getExactClassInfo(Cow.Variant.class).getSupplier().get(), Cow.Variant.class);
try {
getVariantMethod = cowClass.getDeclaredMethod("getVariant");
setVariantMethod = cowClass.getDeclaredMethod("setVariant", Cow.Variant.class);
} catch (Exception e) {
throw new RuntimeException("Could not retrieve get/set variant methods for Cow.", e);
}
} else {
variantsEnabled = false;
variants = null;
getVariantMethod = null;
setVariantMethod = null;
}
}

private @Nullable Object variant = null;

public CowData() {}

// TODO: When safe, 'variant' should have the type changed to 'Cow.Variant'
public CowData(@Nullable Object variant) {
this.variant = variant;
}

@Override
protected boolean init(Literal<?>[] exprs, int matchedPattern, ParseResult parseResult) {
if (variantsEnabled) {
Literal<?> expr = null;
if (exprs[0] != null) { // cow
expr = exprs[0];
} else if (exprs[1] != null) { // calf
expr = exprs[1];
}
if (expr != null)
//noinspection unchecked
variant = ((Literal<Cow.Variant>) expr).getSingle();
}
return true;
}

@Override
protected boolean init(@Nullable Class<? extends Cow> entityClass, @Nullable Cow entity) {
if (entity != null) {
if (variantsEnabled) {
variant = getVariant(entity);
}
}
return true;
}

@Override
public void set(Cow entity) {
if (variantsEnabled) {
Object finalVariant = variant != null ? variant : CollectionUtils.getRandom(variants);
assert finalVariant != null;
setVariant(entity, finalVariant);
}
}

@Override
protected boolean match(Cow entity) {
if (!variantsEnabled)
return true;
return variant == null || getVariant(entity) == variant;
}

@Override
public Class<Cow> getType() {
return cowClass;
}

@Override
public EntityData<Cow> getSuperType() {
return new CowData();
}

@Override
protected int hashCode_i() {
return Objects.hashCode(variant);
}

@Override
protected boolean equals_i(EntityData<?> obj) {
if (!(obj instanceof CowData other))
return false;
if (variantsEnabled && variant != other.variant)
return false;
return true;
}

@Override
public boolean isSupertypeOf(EntityData<?> entityData) {
if (!(entityData instanceof CowData other))
return false;
if (variantsEnabled)
return variant == other.variant;
return true;
}

/**
* Due to the addition of 'AbstractCow' and 'api-version' being '1.19'
* This helper method is required in order to set the {@link #variant} of the {@link Cow}
* @param cow The {@link Cow} to set the variant
*/
public void setVariant(Cow cow) {
setVariant(cow, variant);
}

/**
* Due to the addition of 'AbstractCow' and 'api-version' being '1.19'
* This helper method is required in order to set the {@code object} of the {@link Cow}
* @param cow The {@link Cow} to set the variant
* @param object The 'Cow.Variant'
*/
public void setVariant(Cow cow, Object object) {
if (!variantsEnabled || setVariantMethod == null)
return;
Entity entity = cowClass.cast(cow);
try {
setVariantMethod.invoke(entity, (Cow.Variant) object);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}

/**
* Due to the addition of 'AbstractCow' and 'api-version' being '1.19'
* This helper method is required in order to get the 'Cow.Variant' of the {@link Cow}
* @param cow The {@link Cow} to get the variant
* @return The 'Cow.Variant'
*/
public @Nullable Object getVariant(Cow cow) {
if (!variantsEnabled || getVariantMethod == null)
return null;
Entity entity = cowClass.cast(cow);
try {
return getVariantMethod.invoke(entity);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}

/**
* A dummy/placeholder class to ensure working operation on MC versions that do not have 'Cow.Variant'
*/
public static class CowVariantDummy {}

}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ private static void addSuperEntity(String codeName, Class<? extends Entity> enti
addSimpleEntity("blaze", Blaze.class);
addSimpleEntity("chicken", Chicken.class);
addSimpleEntity("mooshroom", MushroomCow.class);
addSimpleEntity("cow", Cow.class);
addSimpleEntity("cave spider", CaveSpider.class);
addSimpleEntity("dragon fireball", DragonFireball.class);
addSimpleEntity("egg", Egg.class);
Expand Down
9 changes: 8 additions & 1 deletion src/main/resources/lang/default.lang
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ entities:
pattern: <age> chicken(|1¦s)|(4¦)chick(|1¦s)
cow:
name: cow¦s
pattern: <age> cow(|1¦s)|(4¦)cal(f|1¦ves)
pattern: <age> [%-cowvariant%] cow[1:s]|(4:)[%-cowvariant%] cal(f|1:ves)
cave spider:
name: cave spider¦s
pattern: cave[ ]spider(|1¦s)
Expand Down Expand Up @@ -2592,6 +2592,12 @@ pig variants:
temperate: temperate
warm: warm

# -- Cow Variants --
cow variants:
cold: cold
temperate: temperate
warm: warm

# -- Boolean --
boolean:
true:
Expand Down Expand Up @@ -2696,6 +2702,7 @@ types:
fishingstate: fishing state¦s @a
equipmentslot: equipment slot¦s @an
pigvariant: pig variant¦s @a
cowvariant: cow variant¦s @a

# Skript
weathertype: weather type¦s @a
Expand Down
21 changes: 21 additions & 0 deletions src/test/skript/tests/syntaxes/sections/EffSecSpawn.sk
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,24 @@ test "spawn pig by variant" when running minecraft "1.21.5":
assert size of all pigs is 5 with "Only temperate pigs should have been deleted"
delete all warm pigs
assert size of all warm pigs is 0 with "Warm pigs were not deleted"

test "spawn cow by variant" when running minecraft "1.21.5":
delete all cows
set {_l} to test-location
spawn 5 warm cows at {_l}
assert size of all warm cows is 5 with "Size of all warm cows is not 5"
assert size of all cows is 5 with "Size of all cows is not 5"
spawn 3 temperate cows at {_l}
assert size of all temperate cows is 3 with "Size of all temperate cows is not 3"
assert size of all cows is 8 with "Size of all cows is not 8"
spawn 2 cold cows at {_l}
assert size of all cold cows is 2 with "Size of all cold cows is not 2"
assert size of all cows is 10 with "Size of all cows is not 10"
delete all cold cows
assert size of all cold cows is 0 with "Cold cows were not deleted"
assert size of all cows is 8 with "Only cold cows should have been deleted"
delete all temperate cows
assert size of all temperate cows is 0 with "Temperate cows were not deleted"
assert size of all cows is 5 with "Only temperate cows should have been deleted"
delete all warm cows
assert size of all warm cows is 0 with "Warm cows were not deleted"