219 lines
7.1 KiB
Java
219 lines
7.1 KiB
Java
package net.minecraft.world.entity.monster;
|
|
|
|
import java.util.EnumSet;
|
|
import java.util.function.IntFunction;
|
|
import net.minecraft.core.particles.ColorParticleOption;
|
|
import net.minecraft.core.particles.ParticleTypes;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
|
import net.minecraft.network.syncher.SynchedEntityData;
|
|
import net.minecraft.network.syncher.SynchedEntityData.Builder;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.util.ByIdMap;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.ByIdMap.OutOfBoundsStrategy;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.ai.goal.Goal;
|
|
import net.minecraft.world.entity.ai.goal.Goal.Flag;
|
|
import net.minecraft.world.level.Level;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public abstract class SpellcasterIllager extends AbstractIllager {
|
|
private static final EntityDataAccessor<Byte> DATA_SPELL_CASTING_ID = SynchedEntityData.defineId(SpellcasterIllager.class, EntityDataSerializers.BYTE);
|
|
protected int spellCastingTickCount;
|
|
private SpellcasterIllager.IllagerSpell currentSpell = SpellcasterIllager.IllagerSpell.NONE;
|
|
|
|
protected SpellcasterIllager(EntityType<? extends SpellcasterIllager> entityType, Level level) {
|
|
super(entityType, level);
|
|
}
|
|
|
|
@Override
|
|
protected void defineSynchedData(Builder builder) {
|
|
super.defineSynchedData(builder);
|
|
builder.define(DATA_SPELL_CASTING_ID, (byte)0);
|
|
}
|
|
|
|
@Override
|
|
public void readAdditionalSaveData(CompoundTag tag) {
|
|
super.readAdditionalSaveData(tag);
|
|
this.spellCastingTickCount = tag.getInt("SpellTicks");
|
|
}
|
|
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag tag) {
|
|
super.addAdditionalSaveData(tag);
|
|
tag.putInt("SpellTicks", this.spellCastingTickCount);
|
|
}
|
|
|
|
@Override
|
|
public AbstractIllager.IllagerArmPose getArmPose() {
|
|
if (this.isCastingSpell()) {
|
|
return AbstractIllager.IllagerArmPose.SPELLCASTING;
|
|
} else {
|
|
return this.isCelebrating() ? AbstractIllager.IllagerArmPose.CELEBRATING : AbstractIllager.IllagerArmPose.CROSSED;
|
|
}
|
|
}
|
|
|
|
public boolean isCastingSpell() {
|
|
return this.level().isClientSide ? this.entityData.get(DATA_SPELL_CASTING_ID) > 0 : this.spellCastingTickCount > 0;
|
|
}
|
|
|
|
public void setIsCastingSpell(SpellcasterIllager.IllagerSpell currentSpell) {
|
|
this.currentSpell = currentSpell;
|
|
this.entityData.set(DATA_SPELL_CASTING_ID, (byte)currentSpell.id);
|
|
}
|
|
|
|
protected SpellcasterIllager.IllagerSpell getCurrentSpell() {
|
|
return !this.level().isClientSide ? this.currentSpell : SpellcasterIllager.IllagerSpell.byId(this.entityData.get(DATA_SPELL_CASTING_ID));
|
|
}
|
|
|
|
@Override
|
|
protected void customServerAiStep(ServerLevel level) {
|
|
super.customServerAiStep(level);
|
|
if (this.spellCastingTickCount > 0) {
|
|
this.spellCastingTickCount--;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
super.tick();
|
|
if (this.level().isClientSide && this.isCastingSpell()) {
|
|
SpellcasterIllager.IllagerSpell illagerSpell = this.getCurrentSpell();
|
|
float f = (float)illagerSpell.spellColor[0];
|
|
float g = (float)illagerSpell.spellColor[1];
|
|
float h = (float)illagerSpell.spellColor[2];
|
|
float i = this.yBodyRot * (float) (Math.PI / 180.0) + Mth.cos(this.tickCount * 0.6662F) * 0.25F;
|
|
float j = Mth.cos(i);
|
|
float k = Mth.sin(i);
|
|
double d = 0.6 * this.getScale();
|
|
double e = 1.8 * this.getScale();
|
|
this.level()
|
|
.addParticle(ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, f, g, h), this.getX() + j * d, this.getY() + e, this.getZ() + k * d, 0.0, 0.0, 0.0);
|
|
this.level()
|
|
.addParticle(ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, f, g, h), this.getX() - j * d, this.getY() + e, this.getZ() - k * d, 0.0, 0.0, 0.0);
|
|
}
|
|
}
|
|
|
|
protected int getSpellCastingTime() {
|
|
return this.spellCastingTickCount;
|
|
}
|
|
|
|
protected abstract SoundEvent getCastingSoundEvent();
|
|
|
|
protected static enum IllagerSpell {
|
|
NONE(0, 0.0, 0.0, 0.0),
|
|
SUMMON_VEX(1, 0.7, 0.7, 0.8),
|
|
FANGS(2, 0.4, 0.3, 0.35),
|
|
WOLOLO(3, 0.7, 0.5, 0.2),
|
|
DISAPPEAR(4, 0.3, 0.3, 0.8),
|
|
BLINDNESS(5, 0.1, 0.1, 0.2);
|
|
|
|
private static final IntFunction<SpellcasterIllager.IllagerSpell> BY_ID = ByIdMap.continuous(
|
|
illagerSpell -> illagerSpell.id, values(), OutOfBoundsStrategy.ZERO
|
|
);
|
|
final int id;
|
|
final double[] spellColor;
|
|
|
|
private IllagerSpell(final int id, final double red, final double green, final double blue) {
|
|
this.id = id;
|
|
this.spellColor = new double[]{red, green, blue};
|
|
}
|
|
|
|
public static SpellcasterIllager.IllagerSpell byId(int id) {
|
|
return (SpellcasterIllager.IllagerSpell)BY_ID.apply(id);
|
|
}
|
|
}
|
|
|
|
protected class SpellcasterCastingSpellGoal extends Goal {
|
|
public SpellcasterCastingSpellGoal() {
|
|
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
return SpellcasterIllager.this.getSpellCastingTime() > 0;
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
super.start();
|
|
SpellcasterIllager.this.navigation.stop();
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
super.stop();
|
|
SpellcasterIllager.this.setIsCastingSpell(SpellcasterIllager.IllagerSpell.NONE);
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
if (SpellcasterIllager.this.getTarget() != null) {
|
|
SpellcasterIllager.this.getLookControl()
|
|
.setLookAt(SpellcasterIllager.this.getTarget(), SpellcasterIllager.this.getMaxHeadYRot(), SpellcasterIllager.this.getMaxHeadXRot());
|
|
}
|
|
}
|
|
}
|
|
|
|
protected abstract class SpellcasterUseSpellGoal extends Goal {
|
|
protected int attackWarmupDelay;
|
|
protected int nextAttackTickCount;
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
LivingEntity livingEntity = SpellcasterIllager.this.getTarget();
|
|
if (livingEntity == null || !livingEntity.isAlive()) {
|
|
return false;
|
|
} else {
|
|
return SpellcasterIllager.this.isCastingSpell() ? false : SpellcasterIllager.this.tickCount >= this.nextAttackTickCount;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean canContinueToUse() {
|
|
LivingEntity livingEntity = SpellcasterIllager.this.getTarget();
|
|
return livingEntity != null && livingEntity.isAlive() && this.attackWarmupDelay > 0;
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
this.attackWarmupDelay = this.adjustedTickDelay(this.getCastWarmupTime());
|
|
SpellcasterIllager.this.spellCastingTickCount = this.getCastingTime();
|
|
this.nextAttackTickCount = SpellcasterIllager.this.tickCount + this.getCastingInterval();
|
|
SoundEvent soundEvent = this.getSpellPrepareSound();
|
|
if (soundEvent != null) {
|
|
SpellcasterIllager.this.playSound(soundEvent, 1.0F, 1.0F);
|
|
}
|
|
|
|
SpellcasterIllager.this.setIsCastingSpell(this.getSpell());
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
this.attackWarmupDelay--;
|
|
if (this.attackWarmupDelay == 0) {
|
|
this.performSpellCasting();
|
|
SpellcasterIllager.this.playSound(SpellcasterIllager.this.getCastingSoundEvent(), 1.0F, 1.0F);
|
|
}
|
|
}
|
|
|
|
protected abstract void performSpellCasting();
|
|
|
|
protected int getCastWarmupTime() {
|
|
return 20;
|
|
}
|
|
|
|
protected abstract int getCastingTime();
|
|
|
|
protected abstract int getCastingInterval();
|
|
|
|
@Nullable
|
|
protected abstract SoundEvent getSpellPrepareSound();
|
|
|
|
protected abstract SpellcasterIllager.IllagerSpell getSpell();
|
|
}
|
|
}
|