/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.common.collect.Lists * it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap * org.jspecify.annotations.Nullable */ package net.minecraft.world.entity.raid; import com.google.common.collect.Lists; import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap; import java.util.EnumSet; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.Registries; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.SpawnGroupData; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.entity.ai.goal.PathfindToRaidGoal; import net.minecraft.world.entity.ai.targeting.TargetingConditions; import net.minecraft.world.entity.ai.util.DefaultRandomPos; import net.minecraft.world.entity.ai.village.poi.PoiManager; import net.minecraft.world.entity.ai.village.poi.PoiTypes; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.monster.AbstractIllager; import net.minecraft.world.entity.monster.PatrollingMonster; import net.minecraft.world.entity.raid.Raid; import net.minecraft.world.entity.raid.Raids; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.pathfinder.Path; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.Vec3; import org.jspecify.annotations.Nullable; public abstract class Raider extends PatrollingMonster { protected static final EntityDataAccessor IS_CELEBRATING = SynchedEntityData.defineId(Raider.class, EntityDataSerializers.BOOLEAN); private static final Predicate ALLOWED_ITEMS = e -> !e.hasPickUpDelay() && e.isAlive() && ItemStack.matches(e.getItem(), Raid.getOminousBannerInstance(e.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN))); private static final int DEFAULT_WAVE = 0; private static final boolean DEFAULT_CAN_JOIN_RAID = false; protected @Nullable Raid raid; private int wave = 0; private boolean canJoinRaid = false; private int ticksOutsideRaid; protected Raider(EntityType type, Level level) { super((EntityType)type, level); } @Override protected void registerGoals() { super.registerGoals(); this.goalSelector.addGoal(1, new ObtainRaidLeaderBannerGoal(this, this)); this.goalSelector.addGoal(3, new PathfindToRaidGoal(this)); this.goalSelector.addGoal(4, new RaiderMoveThroughVillageGoal(this, 1.05f, 1)); this.goalSelector.addGoal(5, new RaiderCelebration(this)); } @Override protected void defineSynchedData(SynchedEntityData.Builder entityData) { super.defineSynchedData(entityData); entityData.define(IS_CELEBRATING, false); } public abstract void applyRaidBuffs(ServerLevel var1, int var2, boolean var3); public boolean canJoinRaid() { return this.canJoinRaid; } public void setCanJoinRaid(boolean canJoinRaid) { this.canJoinRaid = canJoinRaid; } @Override public void aiStep() { Level level = this.level(); if (level instanceof ServerLevel) { ServerLevel level2 = (ServerLevel)level; if (this.isAlive()) { Raid currentRaid = this.getCurrentRaid(); if (this.canJoinRaid()) { if (currentRaid == null) { Raid nearbyRaid; if (this.level().getGameTime() % 20L == 0L && (nearbyRaid = level2.getRaidAt(this.blockPosition())) != null && Raids.canJoinRaid(this)) { nearbyRaid.joinRaid(level2, nearbyRaid.getGroupsSpawned(), this, null, true); } } else { LivingEntity target = this.getTarget(); if (target != null && (target.getType() == EntityType.PLAYER || target.getType() == EntityType.IRON_GOLEM)) { this.noActionTime = 0; } } } } } super.aiStep(); } @Override protected void updateNoActionTime() { this.noActionTime += 2; } @Override public void die(DamageSource source) { Level level = this.level(); if (level instanceof ServerLevel) { ServerLevel serverLevel = (ServerLevel)level; Entity killer = source.getEntity(); Raid raidWhenKilled = this.getCurrentRaid(); if (raidWhenKilled != null) { if (this.isPatrolLeader()) { raidWhenKilled.removeLeader(this.getWave()); } if (killer != null && killer.getType() == EntityType.PLAYER) { raidWhenKilled.addHeroOfTheVillage(killer); } raidWhenKilled.removeFromRaid(serverLevel, this, false); } } super.die(source); } @Override public boolean canJoinPatrol() { return !this.hasActiveRaid(); } public void setCurrentRaid(@Nullable Raid raid) { this.raid = raid; } public @Nullable Raid getCurrentRaid() { return this.raid; } public boolean isCaptain() { ItemStack banner = this.getItemBySlot(EquipmentSlot.HEAD); boolean hasCaptainBanner = !banner.isEmpty() && ItemStack.matches(banner, Raid.getOminousBannerInstance(this.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN))); boolean patrolLeader = this.isPatrolLeader(); return hasCaptainBanner && patrolLeader; } public boolean hasRaid() { Level level = this.level(); if (!(level instanceof ServerLevel)) { return false; } ServerLevel serverLevel = (ServerLevel)level; return this.getCurrentRaid() != null || serverLevel.getRaidAt(this.blockPosition()) != null; } public boolean hasActiveRaid() { return this.getCurrentRaid() != null && this.getCurrentRaid().isActive(); } public void setWave(int wave) { this.wave = wave; } public int getWave() { return this.wave; } public boolean isCelebrating() { return this.entityData.get(IS_CELEBRATING); } public void setCelebrating(boolean celebrating) { this.entityData.set(IS_CELEBRATING, celebrating); } @Override protected void addAdditionalSaveData(ValueOutput output) { Level level; super.addAdditionalSaveData(output); output.putInt("Wave", this.wave); output.putBoolean("CanJoinRaid", this.canJoinRaid); if (this.raid != null && (level = this.level()) instanceof ServerLevel) { ServerLevel level2 = (ServerLevel)level; level2.getRaids().getId(this.raid).ifPresent(id -> output.putInt("RaidId", id)); } } @Override protected void readAdditionalSaveData(ValueInput input) { super.readAdditionalSaveData(input); this.wave = input.getIntOr("Wave", 0); this.canJoinRaid = input.getBooleanOr("CanJoinRaid", false); Level level = this.level(); if (level instanceof ServerLevel) { ServerLevel level2 = (ServerLevel)level; input.getInt("RaidId").ifPresent(raidId -> { this.raid = level2.getRaids().get((int)raidId); if (this.raid != null) { this.raid.addWaveMob(level2, this.wave, this, false); if (this.isPatrolLeader()) { this.raid.setLeader(this.wave, this); } } }); } } @Override protected void pickUpItem(ServerLevel level, ItemEntity entity) { boolean hasRaidLeader; ItemStack itemStack = entity.getItem(); boolean bl = hasRaidLeader = this.hasActiveRaid() && this.getCurrentRaid().getLeader(this.getWave()) != null; if (this.hasActiveRaid() && !hasRaidLeader && ItemStack.matches(itemStack, Raid.getOminousBannerInstance(this.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)))) { EquipmentSlot slot = EquipmentSlot.HEAD; ItemStack current = this.getItemBySlot(slot); double dropChance = this.getDropChances().byEquipment(slot); if (!current.isEmpty() && (double)Math.max(this.random.nextFloat() - 0.1f, 0.0f) < dropChance) { this.spawnAtLocation(level, current); } this.onItemPickup(entity); this.setItemSlot(slot, itemStack); this.take(entity, itemStack.getCount()); entity.discard(); this.getCurrentRaid().setLeader(this.getWave(), this); this.setPatrolLeader(true); } else { super.pickUpItem(level, entity); } } @Override public boolean removeWhenFarAway(double distSqr) { if (this.getCurrentRaid() == null) { return super.removeWhenFarAway(distSqr); } return false; } @Override public boolean requiresCustomPersistence() { return super.requiresCustomPersistence() || this.getCurrentRaid() != null; } public int getTicksOutsideRaid() { return this.ticksOutsideRaid; } public void setTicksOutsideRaid(int ticksOutsideRaid) { this.ticksOutsideRaid = ticksOutsideRaid; } @Override public boolean hurtServer(ServerLevel level, DamageSource source, float damage) { if (this.hasActiveRaid()) { this.getCurrentRaid().updateBossbar(); } return super.hurtServer(level, source, damage); } @Override public @Nullable SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData groupData) { this.setCanJoinRaid(this.getType() != EntityType.WITCH || spawnReason != EntitySpawnReason.NATURAL); return super.finalizeSpawn(level, difficulty, spawnReason, groupData); } public abstract SoundEvent getCelebrateSound(); public static class ObtainRaidLeaderBannerGoal extends Goal { private final T mob; private Int2LongOpenHashMap unreachableBannerCache = new Int2LongOpenHashMap(); private @Nullable Path pathToBanner; private @Nullable ItemEntity pursuedBannerItemEntity; final /* synthetic */ Raider this$0; public ObtainRaidLeaderBannerGoal(T mob) { this.this$0 = this$0; this.mob = mob; this.setFlags(EnumSet.of(Goal.Flag.MOVE)); } @Override public boolean canUse() { if (this.cannotPickUpBanner()) { return false; } Int2LongOpenHashMap tempCache = new Int2LongOpenHashMap(); double followRange = this.this$0.getAttributeValue(Attributes.FOLLOW_RANGE); List items = ((Entity)this.mob).level().getEntitiesOfClass(ItemEntity.class, ((Entity)this.mob).getBoundingBox().inflate(followRange, 8.0, followRange), ALLOWED_ITEMS); for (ItemEntity banner : items) { long unreachableUntilTime = this.unreachableBannerCache.getOrDefault(banner.getId(), Long.MIN_VALUE); if (this.this$0.level().getGameTime() < unreachableUntilTime) { tempCache.put(banner.getId(), unreachableUntilTime); continue; } Path path = ((Mob)this.mob).getNavigation().createPath(banner, 1); if (path != null && path.canReach()) { this.pathToBanner = path; this.pursuedBannerItemEntity = banner; return true; } tempCache.put(banner.getId(), this.this$0.level().getGameTime() + 600L); } this.unreachableBannerCache = tempCache; return false; } @Override public boolean canContinueToUse() { if (this.pursuedBannerItemEntity == null || this.pathToBanner == null) { return false; } if (this.pursuedBannerItemEntity.isRemoved()) { return false; } if (this.pathToBanner.isDone()) { return false; } return !this.cannotPickUpBanner(); } private boolean cannotPickUpBanner() { if (!((Raider)this.mob).hasActiveRaid()) { return true; } if (((Raider)this.mob).getCurrentRaid().isOver()) { return true; } if (!((PatrollingMonster)this.mob).canBeLeader()) { return true; } if (ItemStack.matches(((LivingEntity)this.mob).getItemBySlot(EquipmentSlot.HEAD), Raid.getOminousBannerInstance(((Entity)this.mob).registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)))) { return true; } Raider leader = this.this$0.raid.getLeader(((Raider)this.mob).getWave()); return leader != null && leader.isAlive(); } @Override public void start() { ((Mob)this.mob).getNavigation().moveTo(this.pathToBanner, (double)1.15f); } @Override public void stop() { this.pathToBanner = null; this.pursuedBannerItemEntity = null; } @Override public void tick() { if (this.pursuedBannerItemEntity != null && this.pursuedBannerItemEntity.closerThan((Entity)this.mob, 1.414)) { ((Raider)this.mob).pickUpItem(ObtainRaidLeaderBannerGoal.getServerLevel(this.this$0.level()), this.pursuedBannerItemEntity); } } } private static class RaiderMoveThroughVillageGoal extends Goal { private final Raider raider; private final double speedModifier; private BlockPos poiPos; private final List visited = Lists.newArrayList(); private final int distanceToPoi; private boolean stuck; public RaiderMoveThroughVillageGoal(Raider mob, double speedModifier, int distanceToPoi) { this.raider = mob; this.speedModifier = speedModifier; this.distanceToPoi = distanceToPoi; this.setFlags(EnumSet.of(Goal.Flag.MOVE)); } @Override public boolean canUse() { this.updateVisited(); return this.isValidRaid() && this.hasSuitablePoi() && this.raider.getTarget() == null; } private boolean isValidRaid() { return this.raider.hasActiveRaid() && !this.raider.getCurrentRaid().isOver(); } private boolean hasSuitablePoi() { ServerLevel level = (ServerLevel)this.raider.level(); BlockPos pos = this.raider.blockPosition(); Optional homePos = level.getPoiManager().getRandom(p -> p.is(PoiTypes.HOME), this::hasNotVisited, PoiManager.Occupancy.ANY, pos, 48, this.raider.random); if (homePos.isEmpty()) { return false; } this.poiPos = homePos.get().immutable(); return true; } @Override public boolean canContinueToUse() { if (this.raider.getNavigation().isDone()) { return false; } return this.raider.getTarget() == null && !this.poiPos.closerToCenterThan(this.raider.position(), this.raider.getBbWidth() + (float)this.distanceToPoi) && !this.stuck; } @Override public void stop() { if (this.poiPos.closerToCenterThan(this.raider.position(), this.distanceToPoi)) { this.visited.add(this.poiPos); } } @Override public void start() { super.start(); this.raider.setNoActionTime(0); this.raider.getNavigation().moveTo(this.poiPos.getX(), this.poiPos.getY(), this.poiPos.getZ(), this.speedModifier); this.stuck = false; } @Override public void tick() { if (this.raider.getNavigation().isDone()) { Vec3 poiVec = Vec3.atBottomCenterOf(this.poiPos); Vec3 nextPos = DefaultRandomPos.getPosTowards(this.raider, 16, 7, poiVec, 0.3141592741012573); if (nextPos == null) { nextPos = DefaultRandomPos.getPosTowards(this.raider, 8, 7, poiVec, 1.5707963705062866); } if (nextPos == null) { this.stuck = true; return; } this.raider.getNavigation().moveTo(nextPos.x, nextPos.y, nextPos.z, this.speedModifier); } } private boolean hasNotVisited(BlockPos poi) { for (BlockPos visitedPoi : this.visited) { if (!Objects.equals(poi, visitedPoi)) continue; return false; } return true; } private void updateVisited() { if (this.visited.size() > 2) { this.visited.remove(0); } } } public class RaiderCelebration extends Goal { private final Raider mob; RaiderCelebration(Raider mob) { this.mob = mob; this.setFlags(EnumSet.of(Goal.Flag.MOVE)); } @Override public boolean canUse() { Raid currentRaid = this.mob.getCurrentRaid(); return this.mob.isAlive() && this.mob.getTarget() == null && currentRaid != null && currentRaid.isLoss(); } @Override public void start() { this.mob.setCelebrating(true); super.start(); } @Override public void stop() { this.mob.setCelebrating(false); super.stop(); } @Override public void tick() { if (!this.mob.isSilent() && this.mob.random.nextInt(this.adjustedTickDelay(100)) == 0) { Raider.this.makeSound(Raider.this.getCelebrateSound()); } if (!this.mob.isPassenger() && this.mob.random.nextInt(this.adjustedTickDelay(50)) == 0) { this.mob.getJumpControl().jump(); } super.tick(); } } protected static class HoldGroundAttackGoal extends Goal { private final Raider mob; private final float hostileRadiusSqr; public final TargetingConditions shoutTargeting = TargetingConditions.forNonCombat().range(8.0).ignoreLineOfSight().ignoreInvisibilityTesting(); public HoldGroundAttackGoal(AbstractIllager mob, float hostileRadius) { this.mob = mob; this.hostileRadiusSqr = hostileRadius * hostileRadius; this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); } @Override public boolean canUse() { LivingEntity lastHurtByMob = this.mob.getLastHurtByMob(); return this.mob.getCurrentRaid() == null && this.mob.isPatrolling() && this.mob.getTarget() != null && !this.mob.isAggressive() && (lastHurtByMob == null || lastHurtByMob.getType() != EntityType.PLAYER); } @Override public void start() { super.start(); this.mob.getNavigation().stop(); List nearbyEntities = HoldGroundAttackGoal.getServerLevel(this.mob).getNearbyEntities(Raider.class, this.shoutTargeting, this.mob, this.mob.getBoundingBox().inflate(8.0, 8.0, 8.0)); for (Raider entity : nearbyEntities) { entity.setTarget(this.mob.getTarget()); } } @Override public void stop() { super.stop(); LivingEntity target = this.mob.getTarget(); if (target != null) { List nearbyEntities = HoldGroundAttackGoal.getServerLevel(this.mob).getNearbyEntities(Raider.class, this.shoutTargeting, this.mob, this.mob.getBoundingBox().inflate(8.0, 8.0, 8.0)); for (Raider entity : nearbyEntities) { entity.setTarget(target); entity.setAggressive(true); } this.mob.setAggressive(true); } } @Override public boolean requiresUpdateEveryTick() { return true; } @Override public void tick() { LivingEntity target = this.mob.getTarget(); if (target == null) { return; } if (this.mob.distanceToSqr(target) > (double)this.hostileRadiusSqr) { this.mob.getLookControl().setLookAt(target, 30.0f, 30.0f); if (this.mob.random.nextInt(50) == 0) { this.mob.playAmbientSound(); } } else { this.mob.setAggressive(true); } super.tick(); } } }