2020-04-15 14:22:03 +02:00
|
|
|
package com.minelittlepony.unicopia.entity;
|
2020-01-16 12:35:46 +01:00
|
|
|
|
|
|
|
import java.util.Optional;
|
|
|
|
import java.util.UUID;
|
|
|
|
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
|
2020-04-15 14:22:03 +02:00
|
|
|
import com.minelittlepony.unicopia.EquinePredicates;
|
|
|
|
import com.minelittlepony.unicopia.Race;
|
2020-04-22 20:23:54 +02:00
|
|
|
import com.minelittlepony.unicopia.ducks.PickedItemSupplier;
|
2020-04-15 14:22:03 +02:00
|
|
|
import com.minelittlepony.unicopia.item.UItems;
|
|
|
|
import com.minelittlepony.unicopia.magic.Affinity;
|
2020-04-15 18:12:00 +02:00
|
|
|
import com.minelittlepony.unicopia.magic.Castable;
|
|
|
|
import com.minelittlepony.unicopia.magic.Caster;
|
|
|
|
import com.minelittlepony.unicopia.magic.MagicEffect;
|
2020-04-15 14:22:03 +02:00
|
|
|
import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
|
|
|
|
import com.minelittlepony.unicopia.network.EffectSync;
|
2020-01-16 12:35:46 +01:00
|
|
|
|
|
|
|
import net.minecraft.block.BlockState;
|
|
|
|
import net.minecraft.entity.Entity;
|
2020-04-15 12:37:14 +02:00
|
|
|
import net.minecraft.entity.EntityDimensions;
|
|
|
|
import net.minecraft.entity.EntityPose;
|
2020-01-16 12:35:46 +01:00
|
|
|
import net.minecraft.entity.EntityType;
|
|
|
|
import net.minecraft.entity.EquipmentSlot;
|
|
|
|
import net.minecraft.entity.LivingEntity;
|
|
|
|
import net.minecraft.entity.ai.goal.GoalSelector;
|
|
|
|
import net.minecraft.entity.damage.DamageSource;
|
|
|
|
import net.minecraft.entity.data.DataTracker;
|
|
|
|
import net.minecraft.entity.data.TrackedData;
|
|
|
|
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
|
|
|
|
import net.minecraft.entity.mob.MobEntityWithAi;
|
|
|
|
import net.minecraft.entity.player.PlayerEntity;
|
|
|
|
import net.minecraft.item.Item;
|
|
|
|
import net.minecraft.item.ItemStack;
|
|
|
|
import net.minecraft.nbt.CompoundTag;
|
|
|
|
import net.minecraft.sound.BlockSoundGroup;
|
|
|
|
import net.minecraft.sound.SoundCategory;
|
|
|
|
import net.minecraft.sound.SoundEvents;
|
|
|
|
import net.minecraft.util.ActionResult;
|
|
|
|
import net.minecraft.util.Hand;
|
|
|
|
import net.minecraft.util.math.BlockPos;
|
|
|
|
import net.minecraft.util.math.Vec3d;
|
|
|
|
import net.minecraft.world.GameRules;
|
|
|
|
import net.minecraft.world.World;
|
|
|
|
import net.minecraft.world.explosion.Explosion.DestructionType;
|
|
|
|
|
2020-04-22 20:23:54 +02:00
|
|
|
public class SpellcastEntity extends MobEntityWithAi implements IMagicals, Caster<LivingEntity>, InAnimate, PickedItemSupplier {
|
2020-01-16 12:35:46 +01:00
|
|
|
|
|
|
|
private LivingEntity owner = null;
|
|
|
|
|
|
|
|
public float hoverStart;
|
|
|
|
|
|
|
|
private static final TrackedData<Integer> LEVEL = DataTracker.registerData(SpellcastEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
|
|
|
|
|
|
|
private static final TrackedData<Optional<UUID>> OWNER = DataTracker.registerData(SpellcastEntity.class, TrackedDataHandlerRegistry.OPTIONAL_UUID);
|
|
|
|
|
|
|
|
private static final TrackedData<CompoundTag> EFFECT = DataTracker.registerData(SpellcastEntity.class, TrackedDataHandlerRegistry.TAG_COMPOUND);
|
|
|
|
|
|
|
|
private static final TrackedData<Integer> AFFINITY = DataTracker.registerData(SpellcastEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
|
|
|
|
|
|
|
private final EffectSync effectDelegate = new EffectSync(this, EFFECT);
|
|
|
|
|
|
|
|
public SpellcastEntity(EntityType<SpellcastEntity> type, World w) {
|
|
|
|
super(type, w);
|
|
|
|
hoverStart = (float)(Math.random() * Math.PI * 2.0D);
|
|
|
|
setPersistent();
|
|
|
|
}
|
|
|
|
|
|
|
|
public GoalSelector getGoals() {
|
|
|
|
return goalSelector;
|
|
|
|
}
|
|
|
|
|
2020-04-15 12:37:14 +02:00
|
|
|
public GoalSelector getTargets() {
|
|
|
|
return targetSelector;
|
|
|
|
}
|
|
|
|
|
2020-01-16 12:35:46 +01:00
|
|
|
@Override
|
|
|
|
public boolean cannotDespawn() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-04-22 16:28:20 +02:00
|
|
|
public boolean shouldRender(double distance) {
|
2020-01-16 12:35:46 +01:00
|
|
|
if (getCurrentLevel() > 0) {
|
|
|
|
distance /= getCurrentLevel();
|
|
|
|
}
|
|
|
|
if (distance > 0) {
|
|
|
|
distance--;
|
|
|
|
}
|
2020-04-22 16:28:20 +02:00
|
|
|
return super.shouldRender(distance);
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Affinity getAffinity() {
|
|
|
|
return Affinity.values()[dataTracker.get(AFFINITY)];
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setAffinity(Affinity affinity) {
|
|
|
|
dataTracker.set(AFFINITY, affinity.ordinal());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-04-15 18:12:00 +02:00
|
|
|
public void setEffect(@Nullable MagicEffect effect) {
|
2020-01-16 12:35:46 +01:00
|
|
|
effectDelegate.set(effect);
|
|
|
|
|
|
|
|
if (effect != null) {
|
|
|
|
effect.onPlaced(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean canInteract(Race race) {
|
|
|
|
return race.canCast();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
@Override
|
2020-04-15 18:12:00 +02:00
|
|
|
public <T extends MagicEffect> T getEffect(@Nullable Class<T> type, boolean update) {
|
2020-01-16 12:35:46 +01:00
|
|
|
return effectDelegate.get(type, update);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean hasEffect() {
|
|
|
|
return effectDelegate.has();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void initDataTracker() {
|
|
|
|
super.initDataTracker();
|
|
|
|
dataTracker.startTracking(LEVEL, 0);
|
|
|
|
dataTracker.startTracking(EFFECT, new CompoundTag());
|
|
|
|
dataTracker.startTracking(OWNER, Optional.empty());
|
|
|
|
dataTracker.startTracking(AFFINITY, Affinity.NEUTRAL.ordinal());
|
|
|
|
}
|
|
|
|
|
2020-04-22 20:23:54 +02:00
|
|
|
@Override
|
|
|
|
public ItemStack getPickedStack() {
|
2020-01-16 12:35:46 +01:00
|
|
|
return SpellRegistry.instance().enchantStack(new ItemStack(getItem()), getEffect().getName());
|
2020-04-22 20:23:54 +02:00
|
|
|
}
|
2020-01-16 12:35:46 +01:00
|
|
|
|
|
|
|
protected Item getItem() {
|
|
|
|
return getAffinity() == Affinity.BAD ? UItems.curse : UItems.spell;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isPushable() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean doesRenderOnFire() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setOwner(LivingEntity owner) {
|
|
|
|
this.owner = owner;
|
|
|
|
setOwner(owner.getUuid());
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setOwner(UUID ownerId) {
|
|
|
|
dataTracker.set(OWNER, Optional.ofNullable(ownerId));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected String getOwnerName() {
|
|
|
|
LivingEntity owner = getOwner();
|
|
|
|
|
|
|
|
if (owner != null) {
|
|
|
|
return owner.getEntityName();
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
@Override
|
|
|
|
public LivingEntity getOwner() {
|
|
|
|
if (owner == null) {
|
|
|
|
owner = dataTracker.get(OWNER).map(world::getPlayerByUuid).orElse(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
return owner;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void displayTick() {
|
|
|
|
if (hasEffect()) {
|
|
|
|
getEffect().render(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void tickMovement() {
|
|
|
|
super.mobTick();
|
|
|
|
if (world.isClient) {
|
|
|
|
displayTick();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hasEffect()) {
|
|
|
|
remove();
|
|
|
|
} else {
|
2020-01-17 14:27:26 +01:00
|
|
|
if (getEffect().isDead()) {
|
2020-01-16 12:35:46 +01:00
|
|
|
remove();
|
|
|
|
onDeath();
|
|
|
|
} else {
|
|
|
|
getEffect().update(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getEffect().allowAI()) {
|
|
|
|
super.tickMovement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (overLevelCap()) {
|
|
|
|
if (world.random.nextInt(10) == 0) {
|
|
|
|
playSpawnEffects();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!world.isClient && hasEffect()) {
|
|
|
|
float exhaustionChance = getEffect().getExhaustion(this);
|
|
|
|
|
|
|
|
if (exhaustionChance == 0 || world.random.nextInt((int)(exhaustionChance / 500)) == 0) {
|
|
|
|
addLevels(-1);
|
|
|
|
} else if (world.random.nextInt((int)(exhaustionChance * 500)) == 0) {
|
|
|
|
setEffect(null);
|
|
|
|
} else if (world.random.nextInt((int)(exhaustionChance * 3500)) == 0) {
|
2020-04-22 16:28:20 +02:00
|
|
|
world.createExplosion(this, getX(), getY(), getZ(), getCurrentLevel()/2, DestructionType.BREAK);
|
2020-01-16 12:35:46 +01:00
|
|
|
remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getCurrentLevel() < 0) {
|
|
|
|
remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-15 12:37:14 +02:00
|
|
|
@Override
|
|
|
|
public EntityDimensions getDimensions(EntityPose pose) {
|
|
|
|
EntityDimensions dims = super.getDimensions(pose);
|
|
|
|
|
|
|
|
if (hasEffect() && getEffect().allowAI()) {
|
|
|
|
return EntityDimensions.changing(dims.width, 1.5F);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dims;
|
|
|
|
}
|
|
|
|
|
2020-01-16 12:35:46 +01:00
|
|
|
public boolean overLevelCap() {
|
|
|
|
return getCurrentLevel() > getMaxLevel();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void fall(double y, boolean onGround, BlockState state, BlockPos pos) {
|
|
|
|
this.onGround = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean damage(DamageSource source, float amount) {
|
|
|
|
if (!world.isClient) {
|
|
|
|
remove();
|
|
|
|
onDeath();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void onDeath() {
|
|
|
|
BlockSoundGroup sound = BlockSoundGroup.STONE;
|
|
|
|
|
2020-04-22 16:28:20 +02:00
|
|
|
world.playSound(getX(), getY(), getZ(), sound.getBreakSound(), SoundCategory.NEUTRAL, sound.getVolume(), sound.getPitch(), true);
|
2020-01-16 12:35:46 +01:00
|
|
|
|
|
|
|
if (world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)) {
|
|
|
|
int level = getCurrentLevel();
|
|
|
|
|
|
|
|
ItemStack stack = new ItemStack(getItem(), level + 1);
|
|
|
|
if (hasEffect()) {
|
|
|
|
SpellRegistry.instance().enchantStack(stack, getEffect().getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
dropStack(stack, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void remove() {
|
|
|
|
if (hasEffect()) {
|
|
|
|
getEffect().setDead();
|
|
|
|
}
|
|
|
|
super.remove();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ActionResult interactAt(PlayerEntity player, Vec3d vec, Hand hand) {
|
2020-01-17 14:27:26 +01:00
|
|
|
if (EquinePredicates.MAGI.test(player)) {
|
2020-01-16 12:35:46 +01:00
|
|
|
ItemStack currentItem = player.getStackInHand(Hand.MAIN_HAND);
|
|
|
|
|
|
|
|
if (currentItem != null
|
2020-04-15 18:12:00 +02:00
|
|
|
&& currentItem.getItem() instanceof Castable
|
|
|
|
&& ((Castable)currentItem.getItem()).canFeed(this, currentItem)
|
2020-01-16 12:35:46 +01:00
|
|
|
&& tryLevelUp(currentItem)) {
|
|
|
|
|
|
|
|
if (!player.abilities.creativeMode) {
|
|
|
|
currentItem.decrement(1);
|
|
|
|
|
|
|
|
if (currentItem.isEmpty()) {
|
|
|
|
player.sendEquipmentBreakStatus(EquipmentSlot.MAINHAND);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ActionResult.SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ActionResult.FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean tryLevelUp(ItemStack stack) {
|
|
|
|
if (hasEffect() && SpellRegistry.stackHasEnchantment(stack)) {
|
|
|
|
if (!getEffect().getName().contentEquals(SpellRegistry.getKeyFromStack(stack))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
addLevels(1);
|
|
|
|
|
|
|
|
playSound(SoundEvents.ENTITY_ZOMBIE_VILLAGER_CURE, 0.1f, 1);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getMaxLevel() {
|
|
|
|
return hasEffect() ? getEffect().getMaxLevelCutOff(this) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getCurrentLevel() {
|
|
|
|
return dataTracker.get(LEVEL);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setCurrentLevel(int level) {
|
|
|
|
dataTracker.set(LEVEL, Math.max(level, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Entity getEntity() {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void readCustomDataFromTag(CompoundTag compound) {
|
|
|
|
super.readCustomDataFromTag(compound);
|
2020-04-22 16:28:20 +02:00
|
|
|
if (compound.contains("affinity")) {
|
2020-01-16 12:35:46 +01:00
|
|
|
setAffinity(Affinity.of(compound.getString("affinity")));
|
|
|
|
}
|
|
|
|
|
|
|
|
setOwner(compound.getUuid("owner"));
|
|
|
|
setCurrentLevel(compound.getInt("level"));
|
|
|
|
|
2020-04-22 16:28:20 +02:00
|
|
|
if (compound.contains("effect")) {
|
2020-01-16 12:35:46 +01:00
|
|
|
setEffect(SpellRegistry.instance().createEffectFromNBT(compound.getCompound("effect")));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void writeCustomDataToTag(CompoundTag compound) {
|
|
|
|
super.writeCustomDataToTag(compound);
|
|
|
|
|
|
|
|
compound.putString("affinity", getAffinity().name());
|
|
|
|
compound.putString("owner", getOwnerName());
|
|
|
|
compound.putInt("level", getCurrentLevel());
|
|
|
|
|
|
|
|
if (hasEffect()) {
|
|
|
|
compound.put("effect", SpellRegistry.instance().serializeEffectToNBT(getEffect()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|