Refactor ricochet code, don't reflect shallow angles

This commit is contained in:
nossr50 2023-12-27 19:20:32 -08:00
parent afff3b4c50
commit 76d4b50554
7 changed files with 119 additions and 98 deletions

View File

@ -1,4 +1,6 @@
Version 2.2.000 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: Cleanup new arrow metadatas
TODO: SQL DB update TODO: SQL DB update
TODO: SQL unit tests TODO: SQL unit tests

View File

@ -35,7 +35,7 @@ public enum SubSkillType {
/* CROSSBOWS */ /* CROSSBOWS */
CROSSBOWS_SUPER_SHOTGUN(1), CROSSBOWS_SUPER_SHOTGUN(1),
CROSSBOWS_CROSSBOWS_LIMIT_BREAK(10), CROSSBOWS_CROSSBOWS_LIMIT_BREAK(10),
CROSSBOWS_TRICK_SHOT(5), CROSSBOWS_TRICK_SHOT(3),
/* Excavation */ /* Excavation */
EXCAVATION_ARCHAEOLOGY(8), EXCAVATION_ARCHAEOLOGY(8),

View File

@ -15,6 +15,7 @@ import com.gmail.nossr50.metadata.MobMetadataService;
import com.gmail.nossr50.party.PartyManager; import com.gmail.nossr50.party.PartyManager;
import com.gmail.nossr50.runnables.TravelingBlockMetaCleanup; import com.gmail.nossr50.runnables.TravelingBlockMetaCleanup;
import com.gmail.nossr50.skills.archery.Archery; 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.BlastMining;
import com.gmail.nossr50.skills.mining.MiningManager; import com.gmail.nossr50.skills.mining.MiningManager;
import com.gmail.nossr50.skills.taming.Taming; 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.util.skills.ProjectileUtils;
import com.gmail.nossr50.worldguard.WorldGuardManager; import com.gmail.nossr50.worldguard.WorldGuardManager;
import com.gmail.nossr50.worldguard.WorldGuardUtils; 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.attribute.Attribute;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
@ -48,9 +52,6 @@ import org.bukkit.potion.PotionEffectType;
import org.bukkit.projectiles.ProjectileSource; import org.bukkit.projectiles.ProjectileSource;
import org.jetbrains.annotations.NotNull; 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 { public class EntityListener implements Listener {
private final mcMMO pluginRef; private final mcMMO pluginRef;
private final @NotNull MobMetadataService mobMetadataService; private final @NotNull MobMetadataService mobMetadataService;
@ -411,7 +412,7 @@ public class EntityListener implements Listener {
} }
if(entityDamageEvent.getDamager() instanceof Projectile) { 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) { 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())) if (WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
return; return;
if(event.getEntity() instanceof Arrow originalArrow && event.getHitBlock() != null && event.getHitBlockFace() != null) { Crossbows.processCrossbows(event, pluginRef);
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()));
}
}
} }
} }

View File

@ -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()));
}
}
}
}
}

View File

@ -3,9 +3,49 @@ package com.gmail.nossr50.skills.crossbows;
import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType; import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.skills.SkillManager; 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 class CrossbowsManager extends SkillManager {
public CrossbowsManager(McMMOPlayer mmoPlayer) { public CrossbowsManager(McMMOPlayer mmoPlayer) {
super(mmoPlayer, PrimarySkillType.CROSSBOWS); 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)));
}
} }

View File

@ -161,16 +161,10 @@ public final class CombatUtils {
//Make sure the profiles been loaded //Make sure the profiles been loaded
if(mcMMOPlayer == null) { if(mcMMOPlayer == null) {
cleanupArrowMetadata(arrow); ProjectileUtils.cleanupProjectileMetadata(arrow);
return; return;
} }
// CrossbowsManager crossbowsManager = mcMMOPlayer.getCrossbowsManager();
// if (crossbowsManager.canActivateAbility()) {
// mcMMOPlayer.checkAbilityActivation(PrimarySkillType.CROSSBOWS);
// }
double boostedDamage = event.getDamage(); double boostedDamage = event.getDamage();
if(canUseLimitBreak(player, target, SubSkillType.CROSSBOWS_CROSSBOWS_LIMIT_BREAK)) { if(canUseLimitBreak(player, target, SubSkillType.CROSSBOWS_CROSSBOWS_LIMIT_BREAK)) {
@ -190,7 +184,7 @@ public final class CombatUtils {
"Final Damage: "+boostedDamage); "Final Damage: "+boostedDamage);
//Clean data //Clean data
cleanupArrowMetadata(arrow); ProjectileUtils.cleanupProjectileMetadata(arrow);
} }
private static void processAxeCombat(@NotNull LivingEntity target, @NotNull Player player, @NotNull EntityDamageByEntityEvent event) { 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 //Make sure the profiles been loaded
if(mcMMOPlayer == null) { if(mcMMOPlayer == null) {
cleanupArrowMetadata(arrow); ProjectileUtils.cleanupProjectileMetadata(arrow);
return; return;
} }
@ -368,7 +362,7 @@ public final class CombatUtils {
"Initial Damage: "+initialDamage, "Initial Damage: "+initialDamage,
"Final Damage: "+boostedDamage); "Final Damage: "+boostedDamage);
//Clean data //Clean data
cleanupArrowMetadata(arrow); ProjectileUtils.cleanupProjectileMetadata(arrow);
} }
/** /**
@ -500,7 +494,7 @@ public final class CombatUtils {
} }
} else { } else {
//Cleanup Arrow //Cleanup Arrow
cleanupArrowMetadata(arrow); ProjectileUtils.cleanupProjectileMetadata(arrow);
} }
if (target.getType() != EntityType.CREEPER if (target.getType() != EntityType.CREEPER
@ -733,35 +727,6 @@ public final class CombatUtils {
} }
dealNoInvulnerabilityTickDamage(target, damage, attacker); 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 * Clean up metadata from a projectile after a minute has passed
* *
* @param entity the projectile * @param entity the projectile
*/ */
public static void delayArrowMetaCleanup(@NotNull Projectile entity) { 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);
} }
} }

View File

@ -1,13 +1,11 @@
package com.gmail.nossr50.util.skills; package com.gmail.nossr50.util.skills;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.MetadataConstants; import com.gmail.nossr50.util.MetadataConstants;
import org.bukkit.Location;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.entity.Arrow; import org.bukkit.entity.Projectile;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.projectiles.ProjectileSource;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
public class ProjectileUtils { public class ProjectileUtils {
public static Vector getNormal(BlockFace blockFace) { 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 * Clean up all possible mcMMO related metadata for a projectile
ProjectileSource originalArrowShooter = originalArrow.getShooter(); *
Vector incomingDirection = originalArrow.getVelocity(); * @param entity projectile
Vector reflectedDirection = incomingDirection.subtract(normal.multiply(2 * incomingDirection.dot(normal))); */
// 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 if(entity.hasMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE)) {
Arrow arrow = originalArrow.getWorld().spawnArrow(origin, entity.removeMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE, mcMMO.p);
reflectedDirection, 1, 1); }
arrow.setShooter(originalArrowShooter);
arrow.setMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW, if(entity.hasMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE)) {
new FixedMetadataValue(pluginRef, originalArrowShooter)); entity.removeMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, mcMMO.p);
// TODO: This metadata needs to get cleaned up at some point }
arrow.setMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE,
new FixedMetadataValue(pluginRef, originalArrow.getMetadata( if(entity.hasMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE)) {
MetadataConstants.METADATA_KEY_BOW_TYPE).get(0))); 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);
}
} }
} }