Skip to content

Commit

Permalink
Add fish hook tags/mecs and expand projectile hit event for hooking e…
Browse files Browse the repository at this point in the history
…ntities (#2278)

* Add fish hook tags/mecs and expand projectile hit event for hooking entities

* Add cancellable tag on projectile hit events

* Split projectile event into separate classes, fix 1.17 lure time
  • Loading branch information
mergu committed Jul 6, 2021
1 parent 6a881c7 commit 9e72b38
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 9 deletions.
Expand Up @@ -94,7 +94,8 @@ public static void registerMainEvents() {
ScriptEvent.registerScriptEvent(new HangingBreaksScriptEvent());
ScriptEvent.registerScriptEvent(new HorseJumpsScriptEvent());
ScriptEvent.registerScriptEvent(new PigZappedScriptEvent());
ScriptEvent.registerScriptEvent(new ProjectileHitsScriptEvent());
ScriptEvent.registerScriptEvent(new ProjectileHitsBlockScriptEvent());
ScriptEvent.registerScriptEvent(new ProjectileHitsEntityScriptEvent());
ScriptEvent.registerScriptEvent(new ProjectileLaunchedScriptEvent());
ScriptEvent.registerScriptEvent(new SheepDyedScriptEvent());
ScriptEvent.registerScriptEvent(new SheepRegrowsScriptEvent());
Expand Down
Expand Up @@ -12,7 +12,7 @@
import org.bukkit.event.Listener;
import org.bukkit.event.entity.ProjectileHitEvent;

public class ProjectileHitsScriptEvent extends BukkitScriptEvent implements Listener {
public class ProjectileHitsBlockScriptEvent extends BukkitScriptEvent implements Listener {

// <--[event]
// @Events
Expand All @@ -27,7 +27,9 @@ public class ProjectileHitsScriptEvent extends BukkitScriptEvent implements List
//
// @Location true
//
// @Triggers when a projectile hits a block.
// @Cancellable true
//
// @Triggers when a projectile hits a block or a fish hook lands on a block.
//
// @Context
// <context.projectile> returns the EntityTag of the projectile.
Expand All @@ -52,19 +54,22 @@ public class ProjectileHitsScriptEvent extends BukkitScriptEvent implements List
//
// @Location true
//
// @Triggers when a projectile shot by an entity hits a block.
// @Cancellable true
//
// @Triggers when a projectile shot by an entity hits a block or a fish hook lands on a block.
//
// @Context
// <context.projectile> returns the EntityTag of the projectile.
// <context.shooter> returns the EntityTag of the shooter, if there is one.
// <context.location> returns the LocationTag of the block that was hit.
// <context.hit_face> returns a LocationTag vector of the hit normal (like '0,1,0' if the projectile hit the top of the block).
//
// -->
public ProjectileHitsScriptEvent() {
public ProjectileHitsBlockScriptEvent() {
instance = this;
}

public static ProjectileHitsScriptEvent instance;
public static ProjectileHitsBlockScriptEvent instance;
public EntityTag projectile;
public EntityTag shooter;
public LocationTag location;
Expand Down Expand Up @@ -119,7 +124,7 @@ else if (cmd.equals("shoots")) {

@Override
public String getName() {
return "ProjectileHits";
return "ProjectileHitsBlock";
}

@Override
Expand Down
@@ -0,0 +1,125 @@
package com.denizenscript.denizen.events.entity;

import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizen.objects.LocationTag;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
import com.denizenscript.denizen.events.BukkitScriptEvent;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.ProjectileHitEvent;

public class ProjectileHitsEntityScriptEvent extends BukkitScriptEvent implements Listener {

// <--[event]
// @Events
// projectile hits entity
// projectile hits <entity>
// <projectile> hits entity
// <projectile> hits <entity>
//
// @Switch shooter:<entity>
//
// @Regex ^on [^\s]+ hits [^\s]+$
//
// @Group Entity
//
// @Location true
//
// @Cancellable true
//
// @Triggers when a projectile hits an entity or a fish hook latches onto an entity.
//
// @Context
// <context.projectile> returns the EntityTag of the projectile.
// <context.shooter> returns the EntityTag of the shooter, if there is one.
// <context.hit_entity> returns the EntityTag of the entity that was hit.
//
// @Player when the entity that was hit is a player.
//
// @NPC when the entity that was hit is an NPC.
//
// -->
public ProjectileHitsEntityScriptEvent() {
instance = this;
}

public static ProjectileHitsEntityScriptEvent instance;
public EntityTag projectile;
public EntityTag shooter;
public EntityTag hitEntity;
public LocationTag location;
public ProjectileHitEvent event;

@Override
public boolean couldMatch(ScriptPath path) {
String cmd = path.eventArgLowerAt(1);
if (!cmd.equals("hits") && !cmd.equals("shoots")) {
return false;
}
if (!couldMatchEntity(path.eventArgLowerAt(0))) {
return false;
}
if (!couldMatchEntity(path.eventArgLowerAt(2))) {
return false;
}
return true;
}

@Override
public boolean matches(ScriptPath path) {
if (!tryEntity(projectile, path.eventArgLowerAt(0))) {
return false;
}
if (!tryEntity(hitEntity, path.eventArgLowerAt(2))) {
return false;
}
if (path.switches.containsKey("shooter") && !tryEntity(shooter, path.switches.get("shooter"))) {
return false;
}
if (!runInCheck(path, location)) {
return false;
}
return super.matches(path);
}

@Override
public String getName() {
return "ProjectileHitsEntity";
}

@Override
public ScriptEntryData getScriptEntryData() {
return new BukkitScriptEntryData(hitEntity);
}

@Override
public ObjectTag getContext(String name) {
if (name.equals("projectile")) {
return projectile.getDenizenObject();
}
else if (name.equals("hit_entity")) {
return hitEntity.getDenizenObject();
}
else if (name.equals("shooter") && shooter != null) {
return shooter.getDenizenObject();
}
return super.getContext(name);
}

@EventHandler
public void onProjectileHits(ProjectileHitEvent event) {
Entity entity = event.getHitEntity();
if (entity == null) {
return;
}
projectile = new EntityTag(event.getEntity());
shooter = projectile.getShooter();
hitEntity = new EntityTag(entity);
location = hitEntity.getLocation();
this.event = event;
fire(event);
}
}
Expand Up @@ -25,6 +25,10 @@ default void setHookTime(FishHook hook, int ticks) {
throw new UnsupportedOperationException();
}

default int getLureTime(FishHook hook) {
throw new UnsupportedOperationException();
}

default void setLureTime(FishHook hook, int ticks) {
throw new UnsupportedOperationException();
}
Expand Down
167 changes: 165 additions & 2 deletions plugin/src/main/java/com/denizenscript/denizen/objects/EntityTag.java
Expand Up @@ -1673,7 +1673,7 @@ else if (mtr.angle == BlockFace.EAST) {
// @group attributes
// @mechanism EntityTag.shooter
// @description
// Returns the entity's shooter, if any.
// Returns the entity's shooter, if any. Also works with fish hooks.
// -->
registerSpawnedOnlyTag("shooter", (attribute, object) -> {
EntityTag shooter = object.getShooter();
Expand Down Expand Up @@ -2585,6 +2585,93 @@ else if (object.getBukkitEntity() instanceof Hanging) {
}
return new ElementTag(((FishHook) object.getBukkitEntity()).getState().name());
});

// <--[tag]
// @attribute <EntityTag.fish_hook_lure_time>
// @returns DurationTag
// @description
// Returns the remaining time before this fish hook will lure a fish.
// -->
registerSpawnedOnlyTag("fish_hook_lure_time", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
attribute.echoError("EntityTag.fish_hook_lure_time is only valid for fish hooks.");
return null;
}
return new DurationTag((long) NMSHandler.getFishingHelper().getLureTime((FishHook) object.getBukkitEntity()));
});

// <--[tag]
// @attribute <EntityTag.fish_hook_min_lure_time>
// @returns DurationTag
// @description
// Returns the minimum possible time before this fish hook can lure a fish.
// -->
registerSpawnedOnlyTag("fish_hook_min_lure_time", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof 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());
});

// <--[tag]
// @attribute <EntityTag.fish_hook_max_lure_time>
// @returns DurationTag
// @description
// Returns the maximum possible time before this fish hook will lure a fish.
// -->
registerSpawnedOnlyTag("fish_hook_max_lure_time", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof 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());
});

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

// <--[tag]
// @attribute <EntityTag.fish_hook_apply_lure>
// @returns ElementTag(Boolean)
// @description
// Returns whether this fish hook should respect the lure enchantment.
// Every level of lure enchantment reduces lure time by 5 seconds.
// -->
registerSpawnedOnlyTag("fish_hook_apply_lure", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
attribute.echoError("EntityTag.fish_hook_apply_lure is only valid for fish hooks.");
return null;
}
return new ElementTag(((FishHook) object.getBukkitEntity()).getApplyLure());
});

// <--[tag]
// @attribute <EntityTag.fish_hook_in_open_water>
// @returns ElementTag(Boolean)
// @description
// Returns whether this fish hook is in open water. Fish hooks in open water can catch treasure.
// See <@link url https://minecraft.fandom.com/wiki/Fishing> for more info.
// -->
registerSpawnedOnlyTag("fish_hook_in_open_water", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
attribute.echoError("EntityTag.fish_hook_in_open_water is only valid for fish hooks.");
return null;
}
return new ElementTag(((FishHook) object.getBukkitEntity()).isInOpenWater());
});
}

public EntityTag describe(TagContext context) {
Expand Down Expand Up @@ -2687,7 +2774,7 @@ public void adjust(Mechanism mechanism) {
// @input EntityTag
// @description
// Sets the entity's shooter.
// The entity must be a projectile.
// The entity must be a projectile. Also works with fish hooks.
// @tags
// <EntityTag.shooter>
// -->
Expand Down Expand Up @@ -3470,6 +3557,82 @@ else if (getBukkitEntity() instanceof Creeper) {
NMSHandler.getFishingHelper().setLureTime((FishHook) getBukkitEntity(), mechanism.valueAsType(DurationTag.class).getTicksAsInt());
}

// <--[mechanism]
// @object EntityTag
// @name fish_hook_pull
// @input None
// @description
// Pulls the entity this fish hook is attached to towards the caster.
// -->
if (mechanism.matches("fish_hook_pull")) {
if (!(getBukkitEntity() instanceof FishHook)) {
mechanism.echoError("fish_hook_pull is only valid for FishHook entities.");
return;
}
((FishHook) getBukkitEntity()).pullHookedEntity();
}

// <--[mechanism]
// @object EntityTag
// @name fish_hook_apply_lure
// @input ElementTag(Boolean)
// @description
// Sets whether this fish hook should respect the lure enchantment.
// Every level of lure enchantment reduces lure time by 5 seconds.
// -->
if (mechanism.matches("fish_hook_apply_lure") && mechanism.requireBoolean()) {
if (!(getBukkitEntity() instanceof FishHook)) {
mechanism.echoError("fish_hook_apply_lure is only valid for FishHook entities.");
return;
}
((FishHook) getBukkitEntity()).setApplyLure(mechanism.getValue().asBoolean());
}

// <--[mechanism]
// @object EntityTag
// @name fish_hook_hooked_entity
// @input EntityTag
// @description
// Sets the entity this fish hook is attached to.
// -->
if (mechanism.matches("fish_hook_hooked_entity") && mechanism.requireObject(EntityTag.class)) {
if (!(getBukkitEntity() instanceof FishHook)) {
mechanism.echoError("fish_hook_hooked_entity is only valid for FishHook entities.");
return;
}
((FishHook) getBukkitEntity()).setHookedEntity(mechanism.valueAsType(EntityTag.class).getBukkitEntity());
}

// <--[mechanism]
// @object EntityTag
// @name fish_hook_min_lure_time
// @input DurationTag
// @description
// Returns the minimum possible time before this fish hook can lure a fish.
// -->
if (mechanism.matches("fish_hook_min_lure_time") && mechanism.requireObject(DurationTag.class)) {
if (!(getBukkitEntity() instanceof FishHook)) {
mechanism.echoError("fish_hook_min_lure_time is only valid for FishHook entities.");
return;
}
((FishHook) getBukkitEntity()).setMinWaitTime(mechanism.valueAsType(DurationTag.class).getTicksAsInt());
}

// <--[mechanism]
// @object EntityTag
// @name fish_hook_max_lure_time
// @input DurationTag
// @description
// Returns the maximum possible time before this fish hook will lure a fish.
// -->
if (mechanism.matches("fish_hook_max_lure_time") && mechanism.requireObject(DurationTag.class)) {
if (!(getBukkitEntity() instanceof FishHook)) {
mechanism.echoError("fish_hook_max_lure_time is only valid for FishHook entities.");
return;
}
((FishHook) getBukkitEntity()).setMaxWaitTime(mechanism.valueAsType(DurationTag.class).getTicksAsInt());
}

CoreUtilities.autoPropertyMechanism(this, mechanism);
}
}

0 comments on commit 9e72b38

Please sign in to comment.