Fixed vanilla bow/trident damage

This commit is contained in:
Indyuce 2021-10-03 12:32:46 +02:00
parent 90dc0bfa9c
commit 97e50b5dc6
3 changed files with 125 additions and 108 deletions

View File

@ -271,17 +271,22 @@ public class ItemUse implements Listener {
}
}
/**
* This handler listens to ALL bow shootings, including both
* custom bows from MMOItems AND vanilla bows, since MMOItems needs to
* apply on-hit effects like crits, elemental damage... even if the
* player is using a vanilla bow.
* <p>
* Fixing commit 4aec1433
*/
@EventHandler
public void handleCustomBows(EntityShootBowEvent event) {
if (!(event.getProjectile() instanceof Arrow) || !(event.getEntity() instanceof Player))
return;
NBTItem item = NBTItem.get(event.getBow());
if (!item.hasType())
return;
Type type = Type.get(item.getType());
PlayerData playerData = PlayerData.get((Player) event.getEntity());
if (type != null) {
Weapon weapon = new Weapon(playerData, item);

View File

@ -144,6 +144,14 @@ public class PlayerListener implements Listener {
event.setCancelled(true);
}
/**
* This handler listens to ALL trident shootings, including both
* custom tridents from MMOItems AND vanilla tridents, since MMOItems
* needs to apply on-hit effects like crits, elemental damage... even
* if the player is using a vanilla trident.
* <p>
* Fixing commit 6cf6f741
*/
@EventHandler(ignoreCancelled = true)
public void registerTridents(ProjectileLaunchEvent event) {
if (!(event.getEntity() instanceof Trident) || !(event.getEntity().getShooter() instanceof Player))
@ -154,9 +162,6 @@ public class PlayerListener implements Listener {
return;
NBTItem nbtItem = MythicLib.plugin.getVersion().getWrapper().getNBTItem(item.getItem());
if (!nbtItem.hasType())
return;
Type type = Type.get(nbtItem.getType());
PlayerData playerData = PlayerData.get((Player) event.getEntity().getShooter());

View File

@ -27,127 +27,134 @@ import java.util.WeakHashMap;
public class EntityManager implements Listener {
/**
* Entity data used by abilities or staff attacks that utilize entities like
* evoker fangs or shulker missiles. It can correspond to the damage the
* entity is supposed to deal, etc
*/
private final Map<Integer, EntityData> entities = new HashMap<>();
/**
* Entity data used by abilities or staff attacks that utilize entities like
* evoker fangs or shulker missiles. It can correspond to the damage the
* entity is supposed to deal, etc
*/
private final Map<Integer, EntityData> entities = new HashMap<>();
private final WeakHashMap<Integer, ProjectileData> projectiles = new WeakHashMap<>();
private final WeakHashMap<Integer, ProjectileData> projectiles = new WeakHashMap<>();
public void registerCustomProjectile(NBTItem sourceItem, StatMap.CachedStatMap stats, Entity entity, boolean customWeapon) {
registerCustomProjectile(sourceItem, stats, entity, customWeapon, 1);
}
public void registerCustomProjectile(NBTItem sourceItem, StatMap.CachedStatMap stats, Entity entity, boolean customWeapon) {
registerCustomProjectile(sourceItem, stats, entity, customWeapon, 1);
}
/**
* Registers a custom projectile
*
* @param sourceItem Item used to shoot the projectile
* @param stats Cached stats of the player shooting the projectile
* @param entity The custom entity
* @param customWeapon Is the source weapon is a custom item
* @param damageCoefficient The damage coefficient. For bows, this is basically the pull force.
* For tridents or anything else this is always set to 1
*/
public void registerCustomProjectile(NBTItem sourceItem, StatMap.CachedStatMap stats, Entity entity, boolean customWeapon, double damageCoefficient) {
/**
* Registers a custom projectile. This is used for bows, crossbows and tridents.
* <p>
* Default bow/trident damage is set to 7 just like vanilla Minecraft.
*
* @param sourceItem Item used to shoot the projectile
* @param stats Cached stats of the player shooting the projectile
* @param entity The custom entity
* @param customWeapon Is the source weapon is a custom item
* @param damageMultiplicator The damage coefficient. For bows, this is basically the pull force.
* For tridents or anything else this is always set to 1
*/
public void registerCustomProjectile(NBTItem sourceItem, StatMap.CachedStatMap stats, Entity entity, boolean customWeapon, double damageMultiplicator) {
/*
* By default damage is set to minecraft's default 7. It is then
* multiplied by the coefficient proportionnal to the pull force.
* 1 corresponds to a fully pulled bow just like in vanilla MC.
*
* For tridents or crossbows, damage coefficient is always 1
*/
double damage = stats.getStat("ATTACK_DAMAGE");
damage = damage == 0 ? 7 : damage * damageCoefficient;
ItemAttackMetadata attackMeta = new ItemAttackMetadata(new DamageMetadata(damage, DamageType.WEAPON, DamageType.PHYSICAL, DamageType.PROJECTILE), stats);
stats.setStat("ATTACK_DAMAGE", damage);
/*
* For bows, MC default value is 7. When using custom bows, the attack
* damage stats returns the correct amount of damage. When using a vanilla
* bow, attack damage is set to 1 because it's the default fist damage value.
* Therefore MMOItems adds 6 to match the vanilla bow damage which is 7.
*
* Damage coefficient is how much you pull the bow. It's something between 0
* and 1 for bows, and it's always 1 for tridents or crossbows.
*/
double damage = stats.getStat("ATTACK_DAMAGE");
damage = (customWeapon ? damage : 6 + damage) * damageMultiplicator;
/*
* Load arrow particles if the entity is an arrow and if the item has
* arrow particles. Currently projectiles are only arrows so there is no
* problem with other projectiles like snowballs etc.
*/
if (entity instanceof Arrow && sourceItem.hasTag("MMOITEMS_ARROW_PARTICLES"))
new ArrowParticles((Arrow) entity, sourceItem);
ItemAttackMetadata attackMeta = new ItemAttackMetadata(new DamageMetadata(damage, DamageType.WEAPON, DamageType.PHYSICAL, DamageType.PROJECTILE), stats);
stats.setStat("ATTACK_DAMAGE", damage);
projectiles.put(entity.getEntityId(), new ProjectileData(sourceItem, attackMeta, customWeapon));
}
/*
* Load arrow particles if the entity is an arrow and if the item has
* arrow particles. Currently projectiles are only arrows so there is no
* problem with other projectiles like snowballs etc.
*/
if (entity instanceof Arrow && sourceItem.hasTag("MMOITEMS_ARROW_PARTICLES"))
new ArrowParticles((Arrow) entity, sourceItem);
public void registerCustomEntity(Entity entity, EntityData data) {
entities.put(entity.getEntityId(), data);
}
projectiles.put(entity.getEntityId(), new ProjectileData(sourceItem, attackMeta, customWeapon));
}
public boolean isCustomProjectile(Projectile projectile) {
return projectiles.containsKey(projectile.getEntityId());
}
public void registerCustomEntity(Entity entity, EntityData data) {
entities.put(entity.getEntityId(), data);
}
public boolean isCustomEntity(Entity entity) {
return entities.containsKey(entity.getEntityId());
}
public boolean isCustomProjectile(Projectile projectile) {
return projectiles.containsKey(projectile.getEntityId());
}
public ProjectileData getProjectileData(Projectile projectile) {
return projectiles.get(projectile.getEntityId());
}
public boolean isCustomEntity(Entity entity) {
return entities.containsKey(entity.getEntityId());
}
public EntityData getEntityData(Entity entity) {
return entities.get(entity.getEntityId());
}
public ProjectileData getProjectileData(Projectile projectile) {
return projectiles.get(projectile.getEntityId());
}
public void unregisterCustomProjectile(Projectile projectile) {
projectiles.remove(projectile.getEntityId());
}
public EntityData getEntityData(Entity entity) {
return entities.get(entity.getEntityId());
}
public void unregisterCustomEntity(Entity entity) {
entities.remove(entity.getEntityId());
}
public void unregisterCustomProjectile(Projectile projectile) {
projectiles.remove(projectile.getEntityId());
}
@EventHandler(priority = EventPriority.HIGHEST)
public void a(EntityDeathEvent event) {
Bukkit.getScheduler().scheduleSyncDelayedTask(MMOItems.plugin, () -> unregisterCustomEntity(event.getEntity()));
}
public void unregisterCustomEntity(Entity entity) {
entities.remove(entity.getEntityId());
}
/*
* Projectile Damage and Effects
*
* TODO when throwing a trident, on hit abilities dont cast half
* of the time because you don't hold the item anymore, therefore
* the ability does not register.
* To fix that, not only cache the player statistics using CachedStats
* but also the player abilities as well as elemental stats. In fact
* a lot of extra stats need to be cached when a ranged attack is delivered.
*
* TODO This bug could also be exploited using a bow, by holding another item
* after shooting an arrow!!
*/
@EventHandler(ignoreCancelled = true)
public void b(EntityDamageByEntityEvent event) {
if (!(event.getDamager() instanceof Projectile) || !(event.getEntity() instanceof LivingEntity) || event.getEntity().hasMetadata("NPC"))
return;
@EventHandler(priority = EventPriority.HIGHEST)
public void a(EntityDeathEvent event) {
Bukkit.getScheduler().scheduleSyncDelayedTask(MMOItems.plugin, () -> unregisterCustomEntity(event.getEntity()));
}
Projectile projectile = (Projectile) event.getDamager();
if (!isCustomProjectile(projectile))
return;
/*
* Projectile Damage and Effects
*
* TODO
* When throwing a trident, on hit abilities dont cast half
* of the time because you don't hold the item anymore, therefore
* the ability does not register.
* To fix that, not only cache the player statistics using CachedStats
* but also the player abilities as well as elemental stats. In fact
* a lot of extra stats need to be cached when a ranged attack is delivered.
*
* TODO
* This bug could also be exploited using a bow, by holding another item
* after shooting an arrow!!
*/
@EventHandler(ignoreCancelled = true)
public void b(EntityDamageByEntityEvent event) {
if (!(event.getDamager() instanceof Projectile) || !(event.getEntity() instanceof LivingEntity) || event.getEntity().hasMetadata("NPC"))
return;
ProjectileData data = getProjectileData(projectile);
LivingEntity target = (LivingEntity) event.getEntity();
Projectile projectile = (Projectile) event.getDamager();
if (!isCustomProjectile(projectile))
return;
// Apply on hit effects
data.getAttackMetadata().applyOnHitEffects(target);
ProjectileData data = getProjectileData(projectile);
LivingEntity target = (LivingEntity) event.getEntity();
// Apply power vanilla enchant
if (projectile instanceof Arrow && data.getSourceItem().getItem().hasItemMeta()
&& data.getSourceItem().getItem().getItemMeta().getEnchants().containsKey(Enchantment.ARROW_DAMAGE))
data.getAttackMetadata().getDamage().multiply(1.25 + (.25 * data.getSourceItem().getItem().getItemMeta().getEnchantLevel(Enchantment.ARROW_DAMAGE)), DamageType.WEAPON);
// Apply on hit effects
data.getAttackMetadata().applyOnHitEffects(target);
// Apply MMOItems specific modifications
if (data.isCustomWeapon()) {
data.applyPotionEffects(target);
data.getAttackMetadata().applyElementalEffects(data.getSourceItem(), target);
}
// Apply power vanilla enchant
if (projectile instanceof Arrow && data.getSourceItem().getItem().hasItemMeta()
&& data.getSourceItem().getItem().getItemMeta().getEnchants().containsKey(Enchantment.ARROW_DAMAGE))
data.getAttackMetadata().getDamage().multiply(1.25 + (.25 * data.getSourceItem().getItem().getItemMeta().getEnchantLevel(Enchantment.ARROW_DAMAGE)), DamageType.WEAPON);
event.setDamage(data.getAttackMetadata().getDamage().getDamage());
unregisterCustomProjectile(projectile);
}
// Apply MMOItems specific modifications
if (data.isCustomWeapon()) {
data.applyPotionEffects(target);
data.getAttackMetadata().applyElementalEffects(data.getSourceItem(), target);
}
event.setDamage(data.getAttackMetadata().getDamage().getDamage());
unregisterCustomProjectile(projectile);
}
}