138 lines
4.7 KiB
Java
138 lines
4.7 KiB
Java
package net.minecraft.world.item;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.function.Predicate;
|
|
import net.minecraft.core.component.DataComponents;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.tags.ItemTags;
|
|
import net.minecraft.util.Unit;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.entity.projectile.AbstractArrow;
|
|
import net.minecraft.world.entity.projectile.Projectile;
|
|
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
|
import net.minecraft.world.level.Level;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public abstract class ProjectileWeaponItem extends Item {
|
|
public static final Predicate<ItemStack> ARROW_ONLY = itemStack -> itemStack.is(ItemTags.ARROWS);
|
|
public static final Predicate<ItemStack> ARROW_OR_FIREWORK = ARROW_ONLY.or(itemStack -> itemStack.is(Items.FIREWORK_ROCKET));
|
|
|
|
public ProjectileWeaponItem(Item.Properties properties) {
|
|
super(properties);
|
|
}
|
|
|
|
public Predicate<ItemStack> getSupportedHeldProjectiles() {
|
|
return this.getAllSupportedProjectiles();
|
|
}
|
|
|
|
/**
|
|
* Get the predicate to match ammunition when searching the player's inventory, not their main/offhand
|
|
*/
|
|
public abstract Predicate<ItemStack> getAllSupportedProjectiles();
|
|
|
|
public static ItemStack getHeldProjectile(LivingEntity shooter, Predicate<ItemStack> isAmmo) {
|
|
if (isAmmo.test(shooter.getItemInHand(InteractionHand.OFF_HAND))) {
|
|
return shooter.getItemInHand(InteractionHand.OFF_HAND);
|
|
} else {
|
|
return isAmmo.test(shooter.getItemInHand(InteractionHand.MAIN_HAND)) ? shooter.getItemInHand(InteractionHand.MAIN_HAND) : ItemStack.EMPTY;
|
|
}
|
|
}
|
|
|
|
public abstract int getDefaultProjectileRange();
|
|
|
|
protected void shoot(
|
|
ServerLevel level,
|
|
LivingEntity shooter,
|
|
InteractionHand hand,
|
|
ItemStack weapon,
|
|
List<ItemStack> projectileItems,
|
|
float velocity,
|
|
float inaccuracy,
|
|
boolean isCrit,
|
|
@Nullable LivingEntity target
|
|
) {
|
|
float f = EnchantmentHelper.processProjectileSpread(level, weapon, shooter, 0.0F);
|
|
float g = projectileItems.size() == 1 ? 0.0F : 2.0F * f / (projectileItems.size() - 1);
|
|
float h = (projectileItems.size() - 1) % 2 * g / 2.0F;
|
|
float i = 1.0F;
|
|
|
|
for (int j = 0; j < projectileItems.size(); j++) {
|
|
ItemStack itemStack = (ItemStack)projectileItems.get(j);
|
|
if (!itemStack.isEmpty()) {
|
|
float k = h + i * ((j + 1) / 2) * g;
|
|
i = -i;
|
|
int l = j;
|
|
Projectile.spawnProjectile(
|
|
this.createProjectile(level, shooter, weapon, itemStack, isCrit),
|
|
level,
|
|
itemStack,
|
|
projectile -> this.shootProjectile(shooter, projectile, l, velocity, inaccuracy, k, target)
|
|
);
|
|
weapon.hurtAndBreak(this.getDurabilityUse(itemStack), shooter, LivingEntity.getSlotForHand(hand));
|
|
if (weapon.isEmpty()) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected int getDurabilityUse(ItemStack stack) {
|
|
return 1;
|
|
}
|
|
|
|
protected abstract void shootProjectile(
|
|
LivingEntity shooter, Projectile projectile, int index, float velocity, float inaccuracy, float angle, @Nullable LivingEntity target
|
|
);
|
|
|
|
protected Projectile createProjectile(Level level, LivingEntity shooter, ItemStack weapon, ItemStack ammo, boolean isCrit) {
|
|
ArrowItem arrowItem2 = ammo.getItem() instanceof ArrowItem arrowItem ? arrowItem : (ArrowItem)Items.ARROW;
|
|
AbstractArrow abstractArrow = arrowItem2.createArrow(level, ammo, shooter, weapon);
|
|
if (isCrit) {
|
|
abstractArrow.setCritArrow(true);
|
|
}
|
|
|
|
return abstractArrow;
|
|
}
|
|
|
|
protected static List<ItemStack> draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter) {
|
|
if (ammo.isEmpty()) {
|
|
return List.of();
|
|
} else {
|
|
int i = shooter.level() instanceof ServerLevel serverLevel ? EnchantmentHelper.processProjectileCount(serverLevel, weapon, shooter, 1) : 1;
|
|
List<ItemStack> list = new ArrayList(i);
|
|
ItemStack itemStack = ammo.copy();
|
|
|
|
for (int j = 0; j < i; j++) {
|
|
ItemStack itemStack2 = useAmmo(weapon, j == 0 ? ammo : itemStack, shooter, j > 0);
|
|
if (!itemStack2.isEmpty()) {
|
|
list.add(itemStack2);
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
}
|
|
|
|
protected static ItemStack useAmmo(ItemStack weapon, ItemStack ammo, LivingEntity shooter, boolean intangable) {
|
|
int i = !intangable && !shooter.hasInfiniteMaterials() && shooter.level() instanceof ServerLevel serverLevel
|
|
? EnchantmentHelper.processAmmoUse(serverLevel, weapon, ammo, 1)
|
|
: 0;
|
|
if (i > ammo.getCount()) {
|
|
return ItemStack.EMPTY;
|
|
} else if (i == 0) {
|
|
ItemStack itemStack = ammo.copyWithCount(1);
|
|
itemStack.set(DataComponents.INTANGIBLE_PROJECTILE, Unit.INSTANCE);
|
|
return itemStack;
|
|
} else {
|
|
ItemStack itemStack = ammo.split(i);
|
|
if (ammo.isEmpty() && shooter instanceof Player player) {
|
|
player.getInventory().removeItem(ammo);
|
|
}
|
|
|
|
return itemStack;
|
|
}
|
|
}
|
|
}
|