From 76d4b5055455e5fab1a4e87b6cbb697d8d4e5be6 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Wed, 27 Dec 2023 19:20:32 -0800 Subject: [PATCH] Refactor ricochet code, don't reflect shallow angles --- Changelog.txt | 2 + .../datatypes/skills/SubSkillType.java | 2 +- .../nossr50/listeners/EntityListener.java | 24 ++----- .../nossr50/skills/crossbows/Crossbows.java | 35 ++++++++++ .../skills/crossbows/CrossbowsManager.java | 40 +++++++++++ .../nossr50/util/skills/CombatUtils.java | 66 ++----------------- .../nossr50/util/skills/ProjectileUtils.java | 48 ++++++++------ 7 files changed, 119 insertions(+), 98 deletions(-) create mode 100644 src/main/java/com/gmail/nossr50/skills/crossbows/Crossbows.java diff --git a/Changelog.txt b/Changelog.txt index d2873c6bf..310194a2f 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,4 +1,6 @@ Version 2.2.000 + TODO: Trickshot: locale, multiple bounces, does not hurt yourself or allies, reduced damage per bounce? + TODO: Add metadata cleanup unit tests TODO: Cleanup new arrow metadatas TODO: SQL DB update TODO: SQL unit tests diff --git a/src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java b/src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java index 0eaf303a3..3dc587eae 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java +++ b/src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java @@ -35,7 +35,7 @@ public enum SubSkillType { /* CROSSBOWS */ CROSSBOWS_SUPER_SHOTGUN(1), CROSSBOWS_CROSSBOWS_LIMIT_BREAK(10), - CROSSBOWS_TRICK_SHOT(5), + CROSSBOWS_TRICK_SHOT(3), /* Excavation */ EXCAVATION_ARCHAEOLOGY(8), diff --git a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java index 8f1ac4432..0e4d50b51 100644 --- a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java @@ -15,6 +15,7 @@ import com.gmail.nossr50.metadata.MobMetadataService; import com.gmail.nossr50.party.PartyManager; import com.gmail.nossr50.runnables.TravelingBlockMetaCleanup; import com.gmail.nossr50.skills.archery.Archery; +import com.gmail.nossr50.skills.crossbows.Crossbows; import com.gmail.nossr50.skills.mining.BlastMining; import com.gmail.nossr50.skills.mining.MiningManager; import com.gmail.nossr50.skills.taming.Taming; @@ -28,7 +29,10 @@ import com.gmail.nossr50.util.skills.CombatUtils; import com.gmail.nossr50.util.skills.ProjectileUtils; import com.gmail.nossr50.worldguard.WorldGuardManager; import com.gmail.nossr50.worldguard.WorldGuardUtils; -import org.bukkit.*; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.OfflinePlayer; import org.bukkit.attribute.Attribute; import org.bukkit.block.Block; import org.bukkit.enchantments.Enchantment; @@ -48,9 +52,6 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.projectiles.ProjectileSource; import org.jetbrains.annotations.NotNull; -import static com.gmail.nossr50.util.skills.ProjectileUtils.getNormal; -import static com.gmail.nossr50.util.skills.ProjectileUtils.spawnReflectedArrow; - public class EntityListener implements Listener { private final mcMMO pluginRef; private final @NotNull MobMetadataService mobMetadataService; @@ -411,7 +412,7 @@ public class EntityListener implements Listener { } if(entityDamageEvent.getDamager() instanceof Projectile) { - CombatUtils.cleanupArrowMetadata((Projectile) entityDamageEvent.getDamager()); + ProjectileUtils.cleanupProjectileMetadata((Projectile) entityDamageEvent.getDamager()); } if(entityDamageEvent.getEntity() instanceof Player player && entityDamageEvent.getDamager() instanceof Player) { @@ -1118,17 +1119,6 @@ public class EntityListener implements Listener { if (WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld())) return; - if(event.getEntity() instanceof Arrow originalArrow && event.getHitBlock() != null && event.getHitBlockFace() != null) { - if (originalArrow.getShooter() instanceof Player) { - // Avoid infinite spawning of arrows - if (originalArrow.hasMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW)) { - return; - } - - // Spawn a new arrow shooting in the reflected direction - spawnReflectedArrow(pluginRef, originalArrow, originalArrow.getLocation(), - getNormal(event.getHitBlockFace())); - } - } + Crossbows.processCrossbows(event, pluginRef); } } diff --git a/src/main/java/com/gmail/nossr50/skills/crossbows/Crossbows.java b/src/main/java/com/gmail/nossr50/skills/crossbows/Crossbows.java new file mode 100644 index 000000000..02ea5b24a --- /dev/null +++ b/src/main/java/com/gmail/nossr50/skills/crossbows/Crossbows.java @@ -0,0 +1,35 @@ +package com.gmail.nossr50.skills.crossbows; + +import com.gmail.nossr50.datatypes.player.McMMOPlayer; +import com.gmail.nossr50.util.MetadataConstants; +import com.gmail.nossr50.util.player.UserManager; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.plugin.Plugin; + +import static com.gmail.nossr50.util.skills.ProjectileUtils.getNormal; + +/** + * Util class for crossbows. + */ +public class Crossbows { + public static void processCrossbows(ProjectileHitEvent event, Plugin pluginRef) { + if(event.getEntity() instanceof Arrow originalArrow && event.getHitBlock() != null && event.getHitBlockFace() != null) { + if (originalArrow.getShooter() instanceof Player) { + // Avoid infinite spawning of arrows + if (originalArrow.hasMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW)) { + return; + } + + McMMOPlayer mmoPlayer = UserManager.getPlayer((Player) originalArrow.getShooter()); + if (mmoPlayer != null) { + mmoPlayer.getCrossbowsManager().handleRicochet( + pluginRef, + originalArrow, + getNormal(event.getHitBlockFace())); + } + } + } + } +} diff --git a/src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowsManager.java b/src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowsManager.java index 29eb39d71..8cb3337eb 100644 --- a/src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowsManager.java +++ b/src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowsManager.java @@ -3,9 +3,49 @@ package com.gmail.nossr50.skills.crossbows; import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.skills.PrimarySkillType; import com.gmail.nossr50.skills.SkillManager; +import com.gmail.nossr50.util.MetadataConstants; +import org.bukkit.Location; +import org.bukkit.entity.Arrow; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.plugin.Plugin; +import org.bukkit.projectiles.ProjectileSource; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; public class CrossbowsManager extends SkillManager { public CrossbowsManager(McMMOPlayer mmoPlayer) { super(mmoPlayer, PrimarySkillType.CROSSBOWS); } + + public void handleRicochet(@NotNull Plugin pluginRef, @NotNull Arrow originalArrow, @NotNull Vector hitBlockNormal) { + // Reflect arrow in new direction + // cleanup metadata on original arrow + // TODO: Add an event for this for plugins to hook into + spawnReflectedArrow(pluginRef, originalArrow, originalArrow.getLocation(), hitBlockNormal); + } + + public void spawnReflectedArrow(@NotNull Plugin pluginRef, @NotNull Arrow originalArrow, @NotNull Location origin, @NotNull Vector normal) { + final ProjectileSource originalArrowShooter = originalArrow.getShooter(); + final Vector arrowInBlockVector = originalArrow.getVelocity(); + final Vector reflectedDirection = arrowInBlockVector.subtract(normal.multiply(2 * arrowInBlockVector.dot(normal))); + final Vector inverseNormal = normal.multiply(-1); + + + // check the angle of the arrow against the inverse normal to see if the angle was too shallow + if (arrowInBlockVector.angle(inverseNormal) < Math.PI / 4) { + return; + } + + // Spawn new arrow with the reflected direction + Arrow arrow = originalArrow.getWorld().spawnArrow(origin, + reflectedDirection, 1, 1); + arrow.setShooter(originalArrowShooter); + arrow.setMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW, + new FixedMetadataValue(pluginRef, originalArrowShooter)); + + // TODO: This metadata needs to get cleaned up at some point + arrow.setMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE, + new FixedMetadataValue(pluginRef, originalArrow.getMetadata( + MetadataConstants.METADATA_KEY_BOW_TYPE).get(0))); + } } diff --git a/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java b/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java index 399a875ec..7b1d07d86 100644 --- a/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java +++ b/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java @@ -161,16 +161,10 @@ public final class CombatUtils { //Make sure the profiles been loaded if(mcMMOPlayer == null) { - cleanupArrowMetadata(arrow); + ProjectileUtils.cleanupProjectileMetadata(arrow); return; } - // CrossbowsManager crossbowsManager = mcMMOPlayer.getCrossbowsManager(); - -// if (crossbowsManager.canActivateAbility()) { -// mcMMOPlayer.checkAbilityActivation(PrimarySkillType.CROSSBOWS); -// } - double boostedDamage = event.getDamage(); if(canUseLimitBreak(player, target, SubSkillType.CROSSBOWS_CROSSBOWS_LIMIT_BREAK)) { @@ -190,7 +184,7 @@ public final class CombatUtils { "Final Damage: "+boostedDamage); //Clean data - cleanupArrowMetadata(arrow); + ProjectileUtils.cleanupProjectileMetadata(arrow); } private static void processAxeCombat(@NotNull LivingEntity target, @NotNull Player player, @NotNull EntityDamageByEntityEvent event) { @@ -327,7 +321,7 @@ public final class CombatUtils { //Make sure the profiles been loaded if(mcMMOPlayer == null) { - cleanupArrowMetadata(arrow); + ProjectileUtils.cleanupProjectileMetadata(arrow); return; } @@ -368,7 +362,7 @@ public final class CombatUtils { "Initial Damage: "+initialDamage, "Final Damage: "+boostedDamage); //Clean data - cleanupArrowMetadata(arrow); + ProjectileUtils.cleanupProjectileMetadata(arrow); } /** @@ -500,7 +494,7 @@ public final class CombatUtils { } } else { //Cleanup Arrow - cleanupArrowMetadata(arrow); + ProjectileUtils.cleanupProjectileMetadata(arrow); } if (target.getType() != EntityType.CREEPER @@ -733,35 +727,6 @@ public final class CombatUtils { } dealNoInvulnerabilityTickDamage(target, damage, attacker); - -// //IFrame storage -//// int noDamageTicks = target.getNoDamageTicks(); -// -//// String debug = "BLEED DMG RESULT: INC DMG:"+damage+", HP-Before:"+target.getHealth()+", HP-After:"; -// -//// double incDmg = getFakeDamageFinalResult(attacker, target, DamageCause.ENTITY_ATTACK, damage); -// -//// double newHealth = Math.max(0, target.getHealth() - incDmg); -// -// //Don't kill things with a stone or wooden weapon -//// if(toolTier < 3 && newHealth == 0) -//// return; -// -// target.setMetadata(mcMMO.CUSTOM_DAMAGE_METAKEY, mcMMO.metadataValue); -// -// if(newHealth == 0 && !(target instanceof Player)) -// { -// target.damage(99999, attacker); -// } -// else -// { -//// Vector beforeRuptureVec = new Vector(target.getVelocity().getX(), target.getVelocity().getY(), target.getVelocity().getZ()); ; -// target.damage(damage, attacker); -//// debug+=target.getHealth(); -// Bukkit.broadcastMessage(debug); -//// target.setNoDamageTicks(noDamageTicks); //Do not add additional IFrames -//// target.setVelocity(beforeRuptureVec); -// } } /** @@ -1062,31 +1027,12 @@ public final class CombatUtils { } } - /** - * Clean up metadata from a projectile - * - * @param entity projectile - */ - public static void cleanupArrowMetadata(@NotNull Projectile entity) { - if(entity.hasMetadata(MetadataConstants.METADATA_KEY_INF_ARROW)) { - entity.removeMetadata(MetadataConstants.METADATA_KEY_INF_ARROW, mcMMO.p); - } - - if(entity.hasMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE)) { - entity.removeMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE, mcMMO.p); - } - - if(entity.hasMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE)) { - entity.removeMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, mcMMO.p); - } - } - /** * Clean up metadata from a projectile after a minute has passed * * @param entity the projectile */ public static void delayArrowMetaCleanup(@NotNull Projectile entity) { - mcMMO.p.getFoliaLib().getImpl().runLater(() -> cleanupArrowMetadata(entity), 20*60); + mcMMO.p.getFoliaLib().getImpl().runLater(() -> ProjectileUtils.cleanupProjectileMetadata(entity), 20*60); } } diff --git a/src/main/java/com/gmail/nossr50/util/skills/ProjectileUtils.java b/src/main/java/com/gmail/nossr50/util/skills/ProjectileUtils.java index 6212b8139..0ef2ccd46 100644 --- a/src/main/java/com/gmail/nossr50/util/skills/ProjectileUtils.java +++ b/src/main/java/com/gmail/nossr50/util/skills/ProjectileUtils.java @@ -1,13 +1,11 @@ package com.gmail.nossr50.util.skills; +import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.util.MetadataConstants; -import org.bukkit.Location; import org.bukkit.block.BlockFace; -import org.bukkit.entity.Arrow; -import org.bukkit.metadata.FixedMetadataValue; -import org.bukkit.plugin.Plugin; -import org.bukkit.projectiles.ProjectileSource; +import org.bukkit.entity.Projectile; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; public class ProjectileUtils { public static Vector getNormal(BlockFace blockFace) { @@ -22,21 +20,31 @@ public class ProjectileUtils { }; } - public static void spawnReflectedArrow(Plugin pluginRef, Arrow originalArrow, Location origin, Vector normal) { - // TODO: Add an event for this for plugins to hook into - ProjectileSource originalArrowShooter = originalArrow.getShooter(); - Vector incomingDirection = originalArrow.getVelocity(); - Vector reflectedDirection = incomingDirection.subtract(normal.multiply(2 * incomingDirection.dot(normal))); + /** + * Clean up all possible mcMMO related metadata for a projectile + * + * @param entity projectile + */ + // TODO: Add test + public static void cleanupProjectileMetadata(@NotNull Projectile entity) { + if(entity.hasMetadata(MetadataConstants.METADATA_KEY_INF_ARROW)) { + entity.removeMetadata(MetadataConstants.METADATA_KEY_INF_ARROW, mcMMO.p); + } - // Spawn new arrow with the reflected direction - Arrow arrow = originalArrow.getWorld().spawnArrow(origin, - reflectedDirection, 1, 1); - arrow.setShooter(originalArrowShooter); - arrow.setMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW, - new FixedMetadataValue(pluginRef, originalArrowShooter)); - // TODO: This metadata needs to get cleaned up at some point - arrow.setMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE, - new FixedMetadataValue(pluginRef, originalArrow.getMetadata( - MetadataConstants.METADATA_KEY_BOW_TYPE).get(0))); + if(entity.hasMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE)) { + entity.removeMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE, mcMMO.p); + } + + if(entity.hasMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE)) { + entity.removeMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, mcMMO.p); + } + + if(entity.hasMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE)) { + entity.removeMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE, mcMMO.p); + } + + if(entity.hasMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW)) { + entity.removeMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW, mcMMO.p); + } } }