364 lines
14 KiB
Java
364 lines
14 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
package net.minecraft.world.entity.monster;
|
|
|
|
import java.util.List;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.util.Mth;
|
|
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.LivingEntity;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
|
|
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
|
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
|
import net.minecraft.world.entity.ai.goal.RandomStrollGoal;
|
|
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
|
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
|
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
|
|
import net.minecraft.world.entity.animal.IronGolem;
|
|
import net.minecraft.world.entity.animal.sheep.Sheep;
|
|
import net.minecraft.world.entity.monster.Monster;
|
|
import net.minecraft.world.entity.monster.SpellcasterIllager;
|
|
import net.minecraft.world.entity.monster.Vex;
|
|
import net.minecraft.world.entity.monster.creaking.Creaking;
|
|
import net.minecraft.world.entity.npc.AbstractVillager;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.entity.projectile.EvokerFangs;
|
|
import net.minecraft.world.entity.raid.Raider;
|
|
import net.minecraft.world.item.DyeColor;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.level.gamerules.GameRules;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import net.minecraft.world.scores.PlayerTeam;
|
|
import org.jspecify.annotations.Nullable;
|
|
|
|
public class Evoker
|
|
extends SpellcasterIllager {
|
|
private @Nullable Sheep wololoTarget;
|
|
|
|
public Evoker(EntityType<? extends Evoker> type, Level level) {
|
|
super((EntityType<? extends SpellcasterIllager>)type, level);
|
|
this.xpReward = 10;
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
this.goalSelector.addGoal(1, new EvokerCastingSpellGoal());
|
|
this.goalSelector.addGoal(2, new AvoidEntityGoal<Player>(this, Player.class, 8.0f, 0.6, 1.0));
|
|
this.goalSelector.addGoal(3, new AvoidEntityGoal<Creaking>(this, Creaking.class, 8.0f, 0.6, 1.0));
|
|
this.goalSelector.addGoal(4, new EvokerSummonSpellGoal());
|
|
this.goalSelector.addGoal(5, new EvokerAttackSpellGoal());
|
|
this.goalSelector.addGoal(6, new EvokerWololoSpellGoal());
|
|
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6));
|
|
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0f, 1.0f));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0f));
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers(new Class[0]));
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<Player>((Mob)this, Player.class, true).setUnseenMemoryTicks(300));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<AbstractVillager>((Mob)this, AbstractVillager.class, false).setUnseenMemoryTicks(300));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<IronGolem>((Mob)this, IronGolem.class, false));
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.FOLLOW_RANGE, 12.0).add(Attributes.MAX_HEALTH, 24.0);
|
|
}
|
|
|
|
@Override
|
|
public SoundEvent getCelebrateSound() {
|
|
return SoundEvents.EVOKER_CELEBRATE;
|
|
}
|
|
|
|
@Override
|
|
protected boolean considersEntityAsAlly(Entity other) {
|
|
Vex vex;
|
|
if (other == this) {
|
|
return true;
|
|
}
|
|
if (super.considersEntityAsAlly(other)) {
|
|
return true;
|
|
}
|
|
if (other instanceof Vex && (vex = (Vex)other).getOwner() != null) {
|
|
return this.considersEntityAsAlly(vex.getOwner());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getAmbientSound() {
|
|
return SoundEvents.EVOKER_AMBIENT;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getDeathSound() {
|
|
return SoundEvents.EVOKER_DEATH;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getHurtSound(DamageSource source) {
|
|
return SoundEvents.EVOKER_HURT;
|
|
}
|
|
|
|
private void setWololoTarget(@Nullable Sheep wololoTarget) {
|
|
this.wololoTarget = wololoTarget;
|
|
}
|
|
|
|
private @Nullable Sheep getWololoTarget() {
|
|
return this.wololoTarget;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getCastingSoundEvent() {
|
|
return SoundEvents.EVOKER_CAST_SPELL;
|
|
}
|
|
|
|
@Override
|
|
public void applyRaidBuffs(ServerLevel level, int wave, boolean isCaptain) {
|
|
}
|
|
|
|
private class EvokerCastingSpellGoal
|
|
extends SpellcasterIllager.SpellcasterCastingSpellGoal {
|
|
private EvokerCastingSpellGoal() {
|
|
super(Evoker.this);
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
if (Evoker.this.getTarget() != null) {
|
|
Evoker.this.getLookControl().setLookAt(Evoker.this.getTarget(), Evoker.this.getMaxHeadYRot(), Evoker.this.getMaxHeadXRot());
|
|
} else if (Evoker.this.getWololoTarget() != null) {
|
|
Evoker.this.getLookControl().setLookAt(Evoker.this.getWololoTarget(), Evoker.this.getMaxHeadYRot(), Evoker.this.getMaxHeadXRot());
|
|
}
|
|
}
|
|
}
|
|
|
|
private class EvokerSummonSpellGoal
|
|
extends SpellcasterIllager.SpellcasterUseSpellGoal {
|
|
private final TargetingConditions vexCountTargeting;
|
|
|
|
private EvokerSummonSpellGoal() {
|
|
super(Evoker.this);
|
|
this.vexCountTargeting = TargetingConditions.forNonCombat().range(16.0).ignoreLineOfSight().ignoreInvisibilityTesting();
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
if (!super.canUse()) {
|
|
return false;
|
|
}
|
|
int vexes = EvokerSummonSpellGoal.getServerLevel(Evoker.this.level()).getNearbyEntities(Vex.class, this.vexCountTargeting, Evoker.this, Evoker.this.getBoundingBox().inflate(16.0)).size();
|
|
return Evoker.this.random.nextInt(8) + 1 > vexes;
|
|
}
|
|
|
|
@Override
|
|
protected int getCastingTime() {
|
|
return 100;
|
|
}
|
|
|
|
@Override
|
|
protected int getCastingInterval() {
|
|
return 340;
|
|
}
|
|
|
|
@Override
|
|
protected void performSpellCasting() {
|
|
ServerLevel serverLevel = (ServerLevel)Evoker.this.level();
|
|
PlayerTeam evokerTeam = Evoker.this.getTeam();
|
|
for (int i = 0; i < 3; ++i) {
|
|
BlockPos pos = Evoker.this.blockPosition().offset(-2 + Evoker.this.random.nextInt(5), 1, -2 + Evoker.this.random.nextInt(5));
|
|
Vex vex = EntityType.VEX.create(Evoker.this.level(), EntitySpawnReason.MOB_SUMMONED);
|
|
if (vex == null) continue;
|
|
vex.snapTo(pos, 0.0f, 0.0f);
|
|
vex.finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(pos), EntitySpawnReason.MOB_SUMMONED, null);
|
|
vex.setOwner(Evoker.this);
|
|
vex.setBoundOrigin(pos);
|
|
vex.setLimitedLife(20 * (30 + Evoker.this.random.nextInt(90)));
|
|
if (evokerTeam != null) {
|
|
serverLevel.getScoreboard().addPlayerToTeam(vex.getScoreboardName(), evokerTeam);
|
|
}
|
|
serverLevel.addFreshEntityWithPassengers(vex);
|
|
serverLevel.gameEvent(GameEvent.ENTITY_PLACE, pos, GameEvent.Context.of(Evoker.this));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getSpellPrepareSound() {
|
|
return SoundEvents.EVOKER_PREPARE_SUMMON;
|
|
}
|
|
|
|
@Override
|
|
protected SpellcasterIllager.IllagerSpell getSpell() {
|
|
return SpellcasterIllager.IllagerSpell.SUMMON_VEX;
|
|
}
|
|
}
|
|
|
|
private class EvokerAttackSpellGoal
|
|
extends SpellcasterIllager.SpellcasterUseSpellGoal {
|
|
private EvokerAttackSpellGoal() {
|
|
super(Evoker.this);
|
|
}
|
|
|
|
@Override
|
|
protected int getCastingTime() {
|
|
return 40;
|
|
}
|
|
|
|
@Override
|
|
protected int getCastingInterval() {
|
|
return 100;
|
|
}
|
|
|
|
@Override
|
|
protected void performSpellCasting() {
|
|
LivingEntity target = Evoker.this.getTarget();
|
|
double minY = Math.min(target.getY(), Evoker.this.getY());
|
|
double maxY = Math.max(target.getY(), Evoker.this.getY()) + 1.0;
|
|
float angleTowardsTarget = (float)Mth.atan2(target.getZ() - Evoker.this.getZ(), target.getX() - Evoker.this.getX());
|
|
if (Evoker.this.distanceToSqr(target) < 9.0) {
|
|
float angle;
|
|
int i;
|
|
for (i = 0; i < 5; ++i) {
|
|
angle = angleTowardsTarget + (float)i * (float)Math.PI * 0.4f;
|
|
this.createSpellEntity(Evoker.this.getX() + (double)Mth.cos(angle) * 1.5, Evoker.this.getZ() + (double)Mth.sin(angle) * 1.5, minY, maxY, angle, 0);
|
|
}
|
|
for (i = 0; i < 8; ++i) {
|
|
angle = angleTowardsTarget + (float)i * (float)Math.PI * 2.0f / 8.0f + 1.2566371f;
|
|
this.createSpellEntity(Evoker.this.getX() + (double)Mth.cos(angle) * 2.5, Evoker.this.getZ() + (double)Mth.sin(angle) * 2.5, minY, maxY, angle, 3);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < 16; ++i) {
|
|
double reach = 1.25 * (double)(i + 1);
|
|
int spellSpeed = 1 * i;
|
|
this.createSpellEntity(Evoker.this.getX() + (double)Mth.cos(angleTowardsTarget) * reach, Evoker.this.getZ() + (double)Mth.sin(angleTowardsTarget) * reach, minY, maxY, angleTowardsTarget, spellSpeed);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void createSpellEntity(double x, double z, double minY, double maxY, float angle, int delayTicks) {
|
|
BlockPos pos = BlockPos.containing(x, maxY, z);
|
|
boolean success = false;
|
|
double topOffset = 0.0;
|
|
do {
|
|
BlockState blockState;
|
|
VoxelShape shape;
|
|
BlockPos below = pos.below();
|
|
BlockState belowState = Evoker.this.level().getBlockState(below);
|
|
if (!belowState.isFaceSturdy(Evoker.this.level(), below, Direction.UP)) continue;
|
|
if (!Evoker.this.level().isEmptyBlock(pos) && !(shape = (blockState = Evoker.this.level().getBlockState(pos)).getCollisionShape(Evoker.this.level(), pos)).isEmpty()) {
|
|
topOffset = shape.max(Direction.Axis.Y);
|
|
}
|
|
success = true;
|
|
break;
|
|
} while ((pos = pos.below()).getY() >= Mth.floor(minY) - 1);
|
|
if (success) {
|
|
Evoker.this.level().addFreshEntity(new EvokerFangs(Evoker.this.level(), x, (double)pos.getY() + topOffset, z, angle, delayTicks, Evoker.this));
|
|
Evoker.this.level().gameEvent(GameEvent.ENTITY_PLACE, new Vec3(x, (double)pos.getY() + topOffset, z), GameEvent.Context.of(Evoker.this));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getSpellPrepareSound() {
|
|
return SoundEvents.EVOKER_PREPARE_ATTACK;
|
|
}
|
|
|
|
@Override
|
|
protected SpellcasterIllager.IllagerSpell getSpell() {
|
|
return SpellcasterIllager.IllagerSpell.FANGS;
|
|
}
|
|
}
|
|
|
|
public class EvokerWololoSpellGoal
|
|
extends SpellcasterIllager.SpellcasterUseSpellGoal {
|
|
private final TargetingConditions wololoTargeting;
|
|
|
|
public EvokerWololoSpellGoal() {
|
|
super(Evoker.this);
|
|
this.wololoTargeting = TargetingConditions.forNonCombat().range(16.0).selector((target, level) -> ((Sheep)target).getColor() == DyeColor.BLUE);
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
if (Evoker.this.getTarget() != null) {
|
|
return false;
|
|
}
|
|
if (Evoker.this.isCastingSpell()) {
|
|
return false;
|
|
}
|
|
if (Evoker.this.tickCount < this.nextAttackTickCount) {
|
|
return false;
|
|
}
|
|
ServerLevel level = EvokerWololoSpellGoal.getServerLevel(Evoker.this.level());
|
|
if (!level.getGameRules().get(GameRules.MOB_GRIEFING).booleanValue()) {
|
|
return false;
|
|
}
|
|
List<Sheep> entities = level.getNearbyEntities(Sheep.class, this.wololoTargeting, Evoker.this, Evoker.this.getBoundingBox().inflate(16.0, 4.0, 16.0));
|
|
if (entities.isEmpty()) {
|
|
return false;
|
|
}
|
|
Evoker.this.setWololoTarget(entities.get(Evoker.this.random.nextInt(entities.size())));
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean canContinueToUse() {
|
|
return Evoker.this.getWololoTarget() != null && this.attackWarmupDelay > 0;
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
super.stop();
|
|
Evoker.this.setWololoTarget(null);
|
|
}
|
|
|
|
@Override
|
|
protected void performSpellCasting() {
|
|
Sheep wololoTarget = Evoker.this.getWololoTarget();
|
|
if (wololoTarget != null && wololoTarget.isAlive()) {
|
|
wololoTarget.setColor(DyeColor.RED);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected int getCastWarmupTime() {
|
|
return 40;
|
|
}
|
|
|
|
@Override
|
|
protected int getCastingTime() {
|
|
return 60;
|
|
}
|
|
|
|
@Override
|
|
protected int getCastingInterval() {
|
|
return 140;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getSpellPrepareSound() {
|
|
return SoundEvents.EVOKER_PREPARE_WOLOLO;
|
|
}
|
|
|
|
@Override
|
|
protected SpellcasterIllager.IllagerSpell getSpell() {
|
|
return SpellcasterIllager.IllagerSpell.WOLOLO;
|
|
}
|
|
}
|
|
}
|
|
|