Skip to content

Commit

Permalink
Interaction entity support (#2489)
Browse files Browse the repository at this point in the history
* Interaction entity support

* use duration instead of time for interactions
  • Loading branch information
tal5 committed Jul 5, 2023
1 parent a769245 commit 92fc9f7
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 106 deletions.
@@ -1,6 +1,7 @@
package com.denizenscript.denizen.objects;

import com.denizenscript.denizen.nms.NMSHandler;
import com.denizenscript.denizen.nms.NMSVersion;
import com.denizenscript.denizen.nms.abstracts.ProfileEditor;
import com.denizenscript.denizen.nms.interfaces.EntityAnimation;
import com.denizenscript.denizen.nms.interfaces.FakePlayer;
Expand All @@ -13,6 +14,7 @@
import com.denizenscript.denizen.scripts.containers.core.EntityScriptContainer;
import com.denizenscript.denizen.scripts.containers.core.EntityScriptHelper;
import com.denizenscript.denizen.utilities.BukkitImplDeprecations;
import com.denizenscript.denizen.utilities.MultiVersionHelper1_19;
import com.denizenscript.denizen.utilities.Utilities;
import com.denizenscript.denizen.utilities.VanillaTagHelper;
import com.denizenscript.denizen.utilities.depends.Depends;
Expand Down Expand Up @@ -2638,8 +2640,8 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// Returns an element indicating the minecraft key for the loot-table for the entity (if any).
// -->
registerSpawnedOnlyTag(ElementTag.class, "loot_table_id", (attribute, object) -> {
if (object.getBukkitEntity() instanceof Lootable) {
LootTable table = ((Lootable) object.getBukkitEntity()).getLootTable();
if (object.getBukkitEntity() instanceof Lootable lootable) {
LootTable table = lootable.getLootTable();
if (table != null) {
return new ElementTag(table.getKey().toString());
}
Expand All @@ -2654,11 +2656,11 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// Returns the current state of the fish hook, as any of: UNHOOKED, HOOKED_ENTITY, BOBBING (unhooked means the fishing hook is in the air or on ground).
// -->
registerSpawnedOnlyTag(ElementTag.class, "fish_hook_state", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
if (!(object.getBukkitEntity() instanceof FishHook fishHook)) {
attribute.echoError("EntityTag.fish_hook_state is only valid for fish hooks.");
return null;
}
return new ElementTag(((FishHook) object.getBukkitEntity()).getState());
return new ElementTag(fishHook.getState());
});

// <--[tag]
Expand All @@ -2669,11 +2671,11 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// Returns the remaining time before this fish hook will lure a fish.
// -->
registerSpawnedOnlyTag(DurationTag.class, "fish_hook_lure_time", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
if (!(object.getBukkitEntity() instanceof FishHook fishHook)) {
attribute.echoError("EntityTag.fish_hook_lure_time is only valid for fish hooks.");
return null;
}
return new DurationTag((long) NMSHandler.fishingHelper.getLureTime((FishHook) object.getBukkitEntity()));
return new DurationTag((long) NMSHandler.fishingHelper.getLureTime(fishHook));
});

// <--[tag]
Expand All @@ -2684,11 +2686,11 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// Returns the minimum possible time before this fish hook can lure a fish.
// -->
registerSpawnedOnlyTag(DurationTag.class, "fish_hook_min_lure_time", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
if (!(object.getBukkitEntity() instanceof FishHook fishHook)) {
attribute.echoError("EntityTag.fish_hook_min_lure_time is only valid for fish hooks.");
return null;
}
return new DurationTag((long) ((FishHook) object.getBukkitEntity()).getMinWaitTime());
return new DurationTag((long) fishHook.getMinWaitTime());
});

// <--[tag]
Expand All @@ -2699,11 +2701,11 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// Returns the maximum possible time before this fish hook will lure a fish.
// -->
registerSpawnedOnlyTag(DurationTag.class, "fish_hook_max_lure_time", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
if (!(object.getBukkitEntity() instanceof FishHook fishHook)) {
attribute.echoError("EntityTag.fish_hook_max_lure_time is only valid for fish hooks.");
return null;
}
return new DurationTag((long) ((FishHook) object.getBukkitEntity()).getMaxWaitTime());
return new DurationTag((long) fishHook.getMaxWaitTime());
});

// <--[tag]
Expand All @@ -2714,11 +2716,11 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// Returns the entity this fish hook is attached to.
// -->
registerSpawnedOnlyTag(EntityTag.class, "fish_hook_hooked_entity", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
if (!(object.getBukkitEntity() instanceof FishHook fishHook)) {
attribute.echoError("EntityTag.fish_hook_hooked_entity is only valid for fish hooks.");
return null;
}
Entity entity = ((FishHook) object.getBukkitEntity()).getHookedEntity();
Entity entity = fishHook.getHookedEntity();
return entity != null ? new EntityTag(entity) : null;
});

Expand All @@ -2731,11 +2733,11 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// Every level of lure enchantment reduces lure time by 5 seconds.
// -->
registerSpawnedOnlyTag(ElementTag.class, "fish_hook_apply_lure", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
if (!(object.getBukkitEntity() instanceof FishHook fishHook)) {
attribute.echoError("EntityTag.fish_hook_apply_lure is only valid for fish hooks.");
return null;
}
return new ElementTag(((FishHook) object.getBukkitEntity()).getApplyLure());
return new ElementTag(fishHook.getApplyLure());
});

// <--[tag]
Expand All @@ -2746,11 +2748,11 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// See <@link url https://minecraft.fandom.com/wiki/Fishing> for more info.
// -->
registerSpawnedOnlyTag(ElementTag.class, "fish_hook_in_open_water", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
if (!(object.getBukkitEntity() instanceof FishHook fishHook)) {
attribute.echoError("EntityTag.fish_hook_in_open_water is only valid for fish hooks.");
return null;
}
return new ElementTag(((FishHook) object.getBukkitEntity()).isInOpenWater());
return new ElementTag(fishHook.isInOpenWater());
});

// <--[tag]
Expand Down Expand Up @@ -2823,11 +2825,11 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// Returns the amount of time that passed since the start of the attack cooldown.
// -->
registerSpawnedOnlyTag(DurationTag.class, "attack_cooldown_duration", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof Player)) {
if (!(object.getBukkitEntity() instanceof Player player)) {
attribute.echoError("Only player-type entities can have attack_cooldowns!");
return null;
}
return new DurationTag((long) NMSHandler.playerHelper.ticksPassedDuringCooldown((Player) object.getLivingEntity()));
return new DurationTag((long) NMSHandler.playerHelper.ticksPassedDuringCooldown(player));
});

// <--[tag]
Expand All @@ -2841,11 +2843,11 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// cooldown progress.
// -->
registerSpawnedOnlyTag(DurationTag.class, "attack_cooldown_max_duration", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof Player)) {
if (!(object.getBukkitEntity() instanceof Player player)) {
attribute.echoError("Only player-type entities can have attack_cooldowns!");
return null;
}
return new DurationTag((long) NMSHandler.playerHelper.getMaxAttackCooldownTicks((Player) object.getLivingEntity()));
return new DurationTag((long) NMSHandler.playerHelper.getMaxAttackCooldownTicks(player));
});

// <--[tag]
Expand All @@ -2858,11 +2860,11 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// NOTE: This may not match exactly with the clientside attack cooldown indicator.
// -->
registerSpawnedOnlyTag(ElementTag.class, "attack_cooldown_percent", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof Player)) {
if (!(object.getBukkitEntity() instanceof Player player)) {
attribute.echoError("Only player-type entities can have attack_cooldowns!");
return null;
}
return new ElementTag(((Player) object.getLivingEntity()).getAttackCooldown() * 100);
return new ElementTag(player.getAttackCooldown() * 100);
});

// <--[tag]
Expand All @@ -2873,13 +2875,52 @@ else if (object.getBukkitEntity() instanceof Hanging) {
// A player's hand is raised when they are blocking with a shield, aiming a crossbow, looking through a spyglass, etc.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_hand_raised", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof HumanEntity)) {
if (!(object.getBukkitEntity() instanceof HumanEntity humanEntity)) {
attribute.echoError("Only player-type entities can have is_hand_raised!");
return null;
}
return new ElementTag(((HumanEntity) object.getLivingEntity()).isHandRaised());
return new ElementTag(humanEntity.isHandRaised());
});

if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_19)) {

// <--[tag]
// @attribute <EntityTag.last_attack>
// @returns MapTag
// @description
// Returns an interaction entity's last attack interaction, if any.
// The returned map contains:
// - 'player' (PlayerTag): the player who interacted
// - 'duration' (DurationTag): the amount of time since the interaction. Note that this value is calculated, and may become inaccurate if the interaction entity changes worlds or the server lags.
// - 'raw_game_time' (ElementTag(Number)): the raw game time the interaction occurred at, used to calculate the time above.
// -->
registerSpawnedOnlyTag(MapTag.class, "last_attack", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof Interaction interaction)) {
attribute.echoError("'EntityTag.last_attack' is only valid for interaction entities.");
return null;
}
return MultiVersionHelper1_19.interactionToMap(interaction.getLastAttack(), interaction.getWorld());
});

// <--[tag]
// @attribute <EntityTag.last_interaction>
// @returns MapTag
// @description
// Returns an interaction entity's last right click interaction, if any.
// The returned map contains:
// - 'player' (PlayerTag): the player who interacted
// - 'duration' (DurationTag): the amount of time since the interaction. Note that this value is calculated, and may become inaccurate if the interaction entity changes worlds or the server lags.
// - 'raw_game_time' (ElementTag(Number)): the raw game time the interaction occurred at, used to calculate the time above.
// -->
registerSpawnedOnlyTag(MapTag.class, "last_interaction", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof Interaction interaction)) {
attribute.echoError("'EntityTag.last_interaction' is only valid for interaction entities.");
return null;
}
return MultiVersionHelper1_19.interactionToMap(interaction.getLastInteraction(), interaction.getWorld());
});
}

// <--[mechanism]
// @object EntityTag
// @name alter_uuid
Expand Down
@@ -1,86 +1,62 @@
package com.denizenscript.denizen.objects.properties.entity;

import com.denizenscript.denizen.nms.NMSHandler;
import com.denizenscript.denizen.nms.NMSVersion;
import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.objects.Mechanism;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.properties.Property;
import com.denizenscript.denizencore.objects.properties.PropertyParser;
import com.denizenscript.denizencore.objects.core.ElementTag;
import org.bukkit.entity.Interaction;
import org.bukkit.entity.Mob;

public class EntityAware implements Property {
public class EntityAware extends EntityProperty<ElementTag> {

// <--[property]
// @object EntityTag
// @name is_aware
// @input ElementTag(Boolean)
// @description
// For mobs (<@link tag EntityTag.is_mob>), this is whether the entity is aware of its surroundings.
// Unaware entities will not perform any actions on their own, such as pathfinding or attacking.
// Similar to <@link property EntityTag.has_ai>, except allows the entity to be moved by gravity, being pushed or attacked, etc.
// For interaction entities, this is whether interacting with them should trigger a response (arm swings, sounds, etc.).
// -->

public static boolean describes(ObjectTag entity) {
return entity instanceof EntityTag
&& ((EntityTag) entity).isMobType();
public static boolean describes(EntityTag entity) {
return entity.getBukkitEntity() instanceof Mob || (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_19) && entity.getBukkitEntity() instanceof Interaction);
}

public static EntityAware getFrom(ObjectTag entity) {
if (!describes(entity)) {
return null;
}
else {
return new EntityAware((EntityTag) entity);
@Override
public ElementTag getPropertyValue() {
if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_19) && getEntity() instanceof Interaction interaction) {
return new ElementTag(interaction.isResponsive());
}
return new ElementTag(as(Mob.class).isAware());
}

public static final String[] handledMechs = new String[] {
"is_aware"
};

public EntityAware(EntityTag entity) {
this.entity = entity;
@Override
public boolean isDefaultValue(ElementTag value) {
// Default value is true for mobs, false for interaction entities
return value.asBoolean() == getEntity() instanceof Mob;
}

EntityTag entity;

@Override
public String getPropertyString() {
return String.valueOf(getMob().isAware());
public void setPropertyValue(ElementTag value, Mechanism mechanism) {
if (!mechanism.requireBoolean()) {
return;
}
if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_19) && getEntity() instanceof Interaction interaction) {
interaction.setResponsive(value.asBoolean());
return;
}
as(Mob.class).setAware(value.asBoolean());
}

@Override
public String getPropertyId() {
return "is_aware";
}

public Mob getMob() {
return (Mob) entity.getBukkitEntity();
}

public static void register() {

// <--[tag]
// @attribute <EntityTag.is_aware>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.is_aware
// @group attributes
// @description
// Returns whether the entity is aware of its surroundings.
// Unaware entities will not perform any actions on their own, such as pathfinding or attacking.
// Similar to <@link tag EntityTag.has_ai>, except allows the entity to be moved by gravity, being pushed or attacked, etc.
// -->
PropertyParser.registerTag(EntityAware.class, ElementTag.class, "is_aware", (attribute, entity) -> {
return new ElementTag(entity.getMob().isAware());
});
}

@Override
public void adjust(Mechanism mechanism) {

// <--[mechanism]
// @object EntityTag
// @name is_aware
// @input ElementTag(Boolean)
// @description
// Sets whether the entity is aware of its surroundings.
// Unaware entities will not perform any actions on their own, such as pathfinding or attacking.
// Similar to <@link mechanism EntityTag.has_ai>, except allows the entity to be moved by gravity, being pushed or attacked, etc.
// @tags
// <EntityTag.is_aware>
// -->
if (mechanism.matches("is_aware") && mechanism.requireBoolean()) {
getMob().setAware(mechanism.getValue().asBoolean());
}
autoRegister("is_aware", EntityAware.class, ElementTag.class, false);
}
}
Expand Up @@ -4,6 +4,7 @@
import com.denizenscript.denizencore.objects.Mechanism;
import com.denizenscript.denizencore.objects.core.ElementTag;
import org.bukkit.entity.Display;
import org.bukkit.entity.Interaction;

public class EntityHeight extends EntityProperty<ElementTag> {

Expand All @@ -12,29 +13,38 @@ public class EntityHeight extends EntityProperty<ElementTag> {
// @name height
// @input ElementTag(Decimal)
// @description
// The height of a display entity's culling box. The box will span from the entity's y to the entity's y + the height.
// The default value is 0, which disables culling entirely.
// For a display entity, this is the height of it's culling box. The box will span from the entity's y to the entity's y + the height.
// The default value for these is 0, which disables culling entirely.
// For an interaction entity, this is the height of it's bounding box (the area that can be interacted with).
// -->

public static boolean describes(EntityTag entity) {
return entity.getBukkitEntity() instanceof Display;
return entity.getBukkitEntity() instanceof Display || entity.getBukkitEntity() instanceof Interaction;
}

@Override
public ElementTag getPropertyValue() {
return new ElementTag(as(Display.class).getDisplayHeight());
if (getEntity() instanceof Display display) {
return new ElementTag(display.getDisplayHeight());
}
return new ElementTag(as(Interaction.class).getInteractionHeight());
}

@Override
public boolean isDefaultValue(ElementTag value) {
return value.asFloat() == 0f;
return value.asFloat() == (getEntity() instanceof Display ? 0f : 1f);
}

@Override
public void setPropertyValue(ElementTag value, Mechanism mechanism) {
if (mechanism.requireFloat()) {
as(Display.class).setDisplayHeight(value.asFloat());
if (!mechanism.requireFloat()) {
return;
}
if (getEntity() instanceof Display display) {
display.setDisplayHeight(value.asFloat());
return;
}
as(Interaction.class).setInteractionHeight(value.asFloat());
}

@Override
Expand Down

0 comments on commit 92fc9f7

Please sign in to comment.