minecraft-source/src/net/minecraft/world/item/CrossbowItem.java

377 lines
16 KiB
Java

package net.minecraft.world.item;
import java.util.AbstractList;
import java.util.Collection;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.network.chat.Component;
import javax.annotation.Nullable;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.stats.Stat;
import net.minecraft.stats.Stats;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.server.level.ServerPlayer;
import java.util.Random;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.entity.Entity;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
import net.minecraft.world.entity.monster.CrossbowAttackMob;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import com.google.common.collect.Lists;
import java.util.List;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import java.util.function.Predicate;
import net.minecraft.resources.ResourceLocation;
public class CrossbowItem extends ProjectileWeaponItem {
private boolean startSoundPlayed;
private boolean midLoadSoundPlayed;
public CrossbowItem(final Properties a) {
super(a);
this.startSoundPlayed = false;
this.midLoadSoundPlayed = false;
this.addProperty(new ResourceLocation("pull"), (bek, bjt, akw) -> {
if (akw == null || bek.getItem() != this) {
return 0.0f;
}
else if (isCharged(bek)) {
return 0.0f;
}
else {
return (bek.getUseDuration() - akw.getUseItemRemainingTicks()) / (float)getChargeDuration(bek);
}
});
this.addProperty(new ResourceLocation("pulling"), (bek, bjt, akw) -> (akw != null && akw.isUsingItem() && akw.getUseItem() == bek && !isCharged(bek)) ? 1.0f : 0.0f);
this.addProperty(new ResourceLocation("charged"), (bek, bjt, akw) -> (akw != null && isCharged(bek)) ? 1.0f : 0.0f);
this.addProperty(new ResourceLocation("firework"), (bek, bjt, akw) -> (akw != null && isCharged(bek) && containsChargedProjectile(bek, Items.FIREWORK_ROCKET)) ? 1.0f : 0.0f);
}
@Override
public Predicate<ItemStack> getSupportedHeldProjectiles() {
return CrossbowItem.ARROW_OR_FIREWORK;
}
@Override
public Predicate<ItemStack> getAllSupportedProjectiles() {
return CrossbowItem.ARROW_ONLY;
}
@Override
public InteractionResultHolder<ItemStack> use(final Level bjt, final Player ayg, final InteractionHand ajh) {
final ItemStack bek5 = ayg.getItemInHand(ajh);
if (isCharged(bek5)) {
performShooting(bjt, ayg, ajh, bek5, getShootingPower(bek5), 1.0f);
setCharged(bek5, false);
return InteractionResultHolder.<ItemStack>consume(bek5);
}
if (!ayg.getProjectile(bek5).isEmpty()) {
if (!isCharged(bek5)) {
this.startSoundPlayed = false;
this.midLoadSoundPlayed = false;
ayg.startUsingItem(ajh);
}
return InteractionResultHolder.<ItemStack>consume(bek5);
}
return InteractionResultHolder.<ItemStack>fail(bek5);
}
@Override
public void releaseUsing(final ItemStack bek, final Level bjt, final LivingEntity akw, final int integer) {
final int integer2 = this.getUseDuration(bek) - integer;
final float float7 = getPowerForTime(integer2, bek);
if (float7 >= 1.0f && !isCharged(bek) && tryLoadProjectiles(akw, bek)) {
setCharged(bek, true);
final SoundSource aaj8 = (akw instanceof Player) ? SoundSource.PLAYERS : SoundSource.HOSTILE;
bjt.playSound(null, akw.getX(), akw.getY(), akw.getZ(), SoundEvents.CROSSBOW_LOADING_END, aaj8, 1.0f, 1.0f / (CrossbowItem.random.nextFloat() * 0.5f + 1.0f) + 0.2f);
}
}
private static boolean tryLoadProjectiles(final LivingEntity akw, final ItemStack bek) {
final int integer3 = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.MULTISHOT, bek);
final int integer4 = (integer3 == 0) ? 1 : 3;
final boolean boolean5 = akw instanceof Player && ((Player)akw).abilities.instabuild;
ItemStack bek2 = akw.getProjectile(bek);
ItemStack bek3 = bek2.copy();
for (int integer5 = 0; integer5 < integer4; ++integer5) {
if (integer5 > 0) {
bek2 = bek3.copy();
}
if (bek2.isEmpty() && boolean5) {
bek2 = new ItemStack(Items.ARROW);
bek3 = bek2.copy();
}
if (!loadProjectile(akw, bek, bek2, integer5 > 0, boolean5)) {
return false;
}
}
return true;
}
private static boolean loadProjectile(final LivingEntity akw, final ItemStack bek2, final ItemStack bek3, final boolean boolean4, final boolean boolean5) {
if (bek3.isEmpty()) {
return false;
}
final boolean boolean6 = boolean5 && bek3.getItem() instanceof ArrowItem;
ItemStack bek4;
if (!boolean6 && !boolean5 && !boolean4) {
bek4 = bek3.split(1);
if (bek3.isEmpty() && akw instanceof Player) {
((Player)akw).inventory.removeItem(bek3);
}
}
else {
bek4 = bek3.copy();
}
addChargedProjectile(bek2, bek4);
return true;
}
public static boolean isCharged(final ItemStack bek) {
final CompoundTag jt2 = bek.getTag();
return jt2 != null && jt2.getBoolean("Charged");
}
public static void setCharged(final ItemStack bek, final boolean boolean2) {
final CompoundTag jt3 = bek.getOrCreateTag();
jt3.putBoolean("Charged", boolean2);
}
private static void addChargedProjectile(final ItemStack bek1, final ItemStack bek2) {
final CompoundTag jt3 = bek1.getOrCreateTag();
ListTag jz4;
if (jt3.contains("ChargedProjectiles", 9)) {
jz4 = jt3.getList("ChargedProjectiles", 10);
}
else {
jz4 = new ListTag();
}
final CompoundTag jt4 = new CompoundTag();
bek2.save(jt4);
((AbstractList<CompoundTag>)jz4).add(jt4);
jt3.put("ChargedProjectiles", jz4);
}
private static List<ItemStack> getChargedProjectiles(final ItemStack bek) {
final List<ItemStack> list2 = Lists.newArrayList();
final CompoundTag jt3 = bek.getTag();
if (jt3 != null && jt3.contains("ChargedProjectiles", 9)) {
final ListTag jz4 = jt3.getList("ChargedProjectiles", 10);
if (jz4 != null) {
for (int integer5 = 0; integer5 < jz4.size(); ++integer5) {
final CompoundTag jt4 = jz4.getCompound(integer5);
list2.add(ItemStack.of(jt4));
}
}
}
return list2;
}
private static void clearChargedProjectiles(final ItemStack bek) {
final CompoundTag jt2 = bek.getTag();
if (jt2 != null) {
final ListTag jz3 = jt2.getList("ChargedProjectiles", 9);
jz3.clear();
jt2.put("ChargedProjectiles", jz3);
}
}
private static boolean containsChargedProjectile(final ItemStack bek, final Item bef) {
return getChargedProjectiles(bek).stream().anyMatch(bek -> bek.getItem() == bef);
}
private static void shootProjectile(final Level bjt, final LivingEntity akw, final InteractionHand ajh, final ItemStack bek4, final ItemStack bek5, final float float6, final boolean boolean7, final float float8, final float float9, final float float10) {
if (bjt.isClientSide) {
return;
}
final boolean boolean8 = bek5.getItem() == Items.FIREWORK_ROCKET;
Projectile ayv12;
if (boolean8) {
ayv12 = new FireworkRocketEntity(bjt, bek5, akw.getX(), akw.getEyeY() - 0.15000000596046448, akw.getZ(), true);
}
else {
ayv12 = getArrow(bjt, akw, bek4, bek5);
if (boolean7 || float10 != 0.0f) {
((AbstractArrow)ayv12).pickup = AbstractArrow.Pickup.CREATIVE_ONLY;
}
}
if (akw instanceof CrossbowAttackMob) {
final CrossbowAttackMob awg13 = (CrossbowAttackMob)akw;
awg13.shootProjectile(awg13.getTarget(), bek4, ayv12, float10);
}
else {
final Vec3 cvi13 = akw.getUpVector(1.0f);
final Quaternion c14 = new Quaternion(new Vector3f(cvi13), float10, true);
final Vec3 cvi14 = akw.getViewVector(1.0f);
final Vector3f e16 = new Vector3f(cvi14);
e16.transform(c14);
ayv12.shoot(e16.x(), e16.y(), e16.z(), float8, float9);
}
bek4.<LivingEntity>hurtAndBreak(boolean8 ? 3 : 1, akw, akw -> akw.broadcastBreakEvent(ajh));
bjt.addFreshEntity((Entity)ayv12);
bjt.playSound(null, akw.getX(), akw.getY(), akw.getZ(), SoundEvents.CROSSBOW_SHOOT, SoundSource.PLAYERS, 1.0f, float6);
}
private static AbstractArrow getArrow(final Level bjt, final LivingEntity akw, final ItemStack bek3, final ItemStack bek4) {
final ArrowItem bch5 = (ArrowItem)((bek4.getItem() instanceof ArrowItem) ? bek4.getItem() : Items.ARROW);
final AbstractArrow ayk6 = bch5.createArrow(bjt, bek4, akw);
if (akw instanceof Player) {
ayk6.setCritArrow(true);
}
ayk6.setSoundEvent(SoundEvents.CROSSBOW_HIT);
ayk6.setShotFromCrossbow(true);
final int integer7 = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.PIERCING, bek3);
if (integer7 > 0) {
ayk6.setPierceLevel((byte)integer7);
}
return ayk6;
}
public static void performShooting(final Level bjt, final LivingEntity akw, final InteractionHand ajh, final ItemStack bek, final float float5, final float float6) {
final List<ItemStack> list7 = getChargedProjectiles(bek);
final float[] arr8 = getShotPitches(akw.getRandom());
for (int integer9 = 0; integer9 < list7.size(); ++integer9) {
final ItemStack bek2 = list7.get(integer9);
final boolean boolean11 = akw instanceof Player && ((Player)akw).abilities.instabuild;
if (!bek2.isEmpty()) {
if (integer9 == 0) {
shootProjectile(bjt, akw, ajh, bek, bek2, arr8[integer9], boolean11, float5, float6, 0.0f);
}
else if (integer9 == 1) {
shootProjectile(bjt, akw, ajh, bek, bek2, arr8[integer9], boolean11, float5, float6, -10.0f);
}
else if (integer9 == 2) {
shootProjectile(bjt, akw, ajh, bek, bek2, arr8[integer9], boolean11, float5, float6, 10.0f);
}
}
}
onCrossbowShot(bjt, akw, bek);
}
private static float[] getShotPitches(final Random random) {
final boolean boolean2 = random.nextBoolean();
return new float[] { 1.0f, getRandomShotPitch(boolean2), getRandomShotPitch(!boolean2) };
}
private static float getRandomShotPitch(final boolean boolean1) {
final float float2 = boolean1 ? 0.63f : 0.43f;
return 1.0f / (CrossbowItem.random.nextFloat() * 0.5f + 1.8f) + float2;
}
private static void onCrossbowShot(final Level bjt, final LivingEntity akw, final ItemStack bek) {
if (akw instanceof ServerPlayer) {
final ServerPlayer xe4 = (ServerPlayer)akw;
if (!bjt.isClientSide) {
CriteriaTriggers.SHOT_CROSSBOW.trigger(xe4, bek);
}
xe4.awardStat(Stats.ITEM_USED.get(bek.getItem()));
}
clearChargedProjectiles(bek);
}
@Override
public void onUseTick(final Level bjt, final LivingEntity akw, final ItemStack bek, final int integer) {
if (!bjt.isClientSide) {
final int integer2 = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.QUICK_CHARGE, bek);
final SoundEvent aah7 = this.getStartSound(integer2);
final SoundEvent aah8 = (integer2 == 0) ? SoundEvents.CROSSBOW_LOADING_MIDDLE : null;
final float float9 = (bek.getUseDuration() - integer) / (float)getChargeDuration(bek);
if (float9 < 0.2f) {
this.startSoundPlayed = false;
this.midLoadSoundPlayed = false;
}
if (float9 >= 0.2f && !this.startSoundPlayed) {
this.startSoundPlayed = true;
bjt.playSound(null, akw.getX(), akw.getY(), akw.getZ(), aah7, SoundSource.PLAYERS, 0.5f, 1.0f);
}
if (float9 >= 0.5f && aah8 != null && !this.midLoadSoundPlayed) {
this.midLoadSoundPlayed = true;
bjt.playSound(null, akw.getX(), akw.getY(), akw.getZ(), aah8, SoundSource.PLAYERS, 0.5f, 1.0f);
}
}
}
@Override
public int getUseDuration(final ItemStack bek) {
return getChargeDuration(bek) + 3;
}
public static int getChargeDuration(final ItemStack bek) {
final int integer2 = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.QUICK_CHARGE, bek);
return (integer2 == 0) ? 25 : (25 - 5 * integer2);
}
@Override
public UseAnim getUseAnimation(final ItemStack bek) {
return UseAnim.CROSSBOW;
}
private SoundEvent getStartSound(final int integer) {
switch (integer) {
case 1: {
return SoundEvents.CROSSBOW_QUICK_CHARGE_1;
}
case 2: {
return SoundEvents.CROSSBOW_QUICK_CHARGE_2;
}
case 3: {
return SoundEvents.CROSSBOW_QUICK_CHARGE_3;
}
default: {
return SoundEvents.CROSSBOW_LOADING_START;
}
}
}
private static float getPowerForTime(final int integer, final ItemStack bek) {
float float3 = integer / (float)getChargeDuration(bek);
if (float3 > 1.0f) {
float3 = 1.0f;
}
return float3;
}
@Override
public void appendHoverText(final ItemStack bek, @Nullable final Level bjt, final List<Component> list, final TooltipFlag bft) {
final List<ItemStack> list2 = getChargedProjectiles(bek);
if (!isCharged(bek) || list2.isEmpty()) {
return;
}
final ItemStack bek2 = list2.get(0);
list.add(new TranslatableComponent("item.minecraft.crossbow.projectile", new Object[0]).append(" ").append(bek2.getDisplayName()));
if (bft.isAdvanced() && bek2.getItem() == Items.FIREWORK_ROCKET) {
final List<Component> list3 = Lists.newArrayList();
Items.FIREWORK_ROCKET.appendHoverText(bek2, bjt, list3, bft);
if (!list3.isEmpty()) {
for (int integer9 = 0; integer9 < list3.size(); ++integer9) {
list3.set(integer9, new TextComponent(" ").append(list3.get(integer9)).withStyle(ChatFormatting.GRAY));
}
list.addAll(list3);
}
}
}
private static float getShootingPower(final ItemStack bek) {
if (bek.getItem() == Items.CROSSBOW && containsChargedProjectile(bek, Items.FIREWORK_ROCKET)) {
return 1.6f;
}
return 3.15f;
}
}