Source ZombieInfo - Spiderman31807/Mob_Selector_Mod GitHub Wiki

package playasmob;

import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Item;
import net.minecraft.world.food.Foods;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.monster.ZombieVillager;
import net.minecraft.world.entity.monster.Zombie;
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ConversionParams;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.Difficulty;
import net.minecraft.util.Mth;
import net.minecraft.tags.FluidTags;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.core.BlockPos;
import net.minecraft.client.renderer.entity.state.ZombieRenderState;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.client.renderer.entity.ZombieRenderer;
import net.minecraft.client.renderer.entity.EntityRenderer;

public class ZombieInfo extends MonsterInfo implements MaturableInfo {
	public static final ResourceLocation ChargeId = ResourceLocation.withDefaultNamespace("reinforcement_caller_charge");
	public static final AttributeModifier ChargeMod = new AttributeModifier(ChargeId, -0.05F, AttributeModifier.Operation.ADD_VALUE);
	public static final EntityDimensions BabyHitbox = EntityType.ZOMBIE.getDimensions().scale(0.5F).withEyeHeight(0.93F);
	public static final Synched specialType = Synched.SpecialType;
	public static final Synched waterConverting = Synched.DrownedConverting;
	public int inWaterTime;
	public int conversionTime;

	public ZombieInfo(EntityType type, Class typeClass, Player player, CompoundTag compound) {
		super(type, typeClass, player, compound);
	}

	public ZombieInfo(Player player, CompoundTag compound) {
		this(EntityType.ZOMBIE, Zombie.class, player, compound);
	}

	public ZombieInfo(Player player) {
		this(player, null);
	}

	public ZombieInfo() {
		this(null);
	}

	// Custom Code
	@Override
	public void reset() {
		super.reset();
		this.reset(specialType);
		this.reset(waterConverting);
		this.inWaterTime = 0;
		this.conversionTime = 0;
	}
	
	@Override
	public float babySpawnChance() {
		return 0.05f;
	}
	
	@Override
	public void setupAttributes() {
		super.setupAttributes();
		this.randomizeReinforcementsChance();
	}

	@Override
	public EntityRenderer getRenderer() {
		return new ZombieRenderer(Utils.getContext());
	}

	@Override
	public EntityRenderState createState() {
		return new ZombieRenderState();
	}

	@Override
	public void modifyState(EntityRenderState state) {
		super.modifyState(state);
		if (state instanceof ZombieRenderState zombieState) {
			zombieState.isAggressive = this.isAggressive();
			zombieState.isConverting = this.isUnderWaterConverting();
		}
	}

	@Override
	public boolean canTradeWith(Entity entity) {
		return entity instanceof ZombieVillager;
	}

	@Override
	public Diet getDiet() {
		Diet diet = Diet.carnivore(true);
		diet.addFood(Items.ROTTEN_FLESH, true, Foods.BREAD);
		diet.addFood(Items.SPIDER_EYE, true, Foods.GOLDEN_CARROT);
		diet.addFood(Items.POISONOUS_POTATO, true, Foods.BAKED_POTATO);
		return diet;
	}

	@Override
	public float getSpeed(float original) {
		float baseSpeed = super.getSpeed(original);
		return this.isBaby() ? baseSpeed * 1.5f : baseSpeed;
	}

	@Override
	public float getAgeScale(float original) {
		return 1f;
	}

	public void resetDrownedConverting() {
		this.inWaterTime = -1;
		this.conversionTime = 0;
		this.set(waterConverting, false);
	}

	// Zombie.class Code
	public boolean isUnderWaterConverting() {
		return this.get(waterConverting);
	}

	public boolean convertsInWater() {
		return true;
	}

	@Override
	public void preTick() {
		if (!this.level().isClientSide && this.isAlive() && this.convertsInWater()) {
			if (this.isUnderWaterConverting()) {
				if (this.conversionTime-- < 0)
					this.doUnderWaterConversion();
			} else {
				if (this.isEyeInFluid(FluidTags.WATER)) {
					if (this.inWaterTime++ >= 600)
						this.startUnderWaterConversion(300);
				} else {
					this.inWaterTime = -1;
				}
			}
		}
		super.preTick();
	}

	@Override
	public boolean aiStep(Object object) {
		if (this.isAlive()) {
			boolean shouldBurn = this.isSunSensitive() && this.isSunBurnTick();
			if (shouldBurn) {
				ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
				if (!itemstack.isEmpty()) {
					if (itemstack.isDamageableItem()) {
						Item item = itemstack.getItem();
						itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2));
						if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) {
							this.onEquippedItemBroken(item, EquipmentSlot.HEAD);
							this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY);
						}
					}
					shouldBurn = false;
				}
				if (shouldBurn)
					this.igniteForSeconds(8);
			}
		}
		return super.aiStep(null);
	}

	public void startUnderWaterConversion(int time) {
		this.conversionTime = time;
		this.set(waterConverting, true);
	}

	public void doUnderWaterConversion() {
		this.convertToZombieType(EntityType.DROWNED);
		if (!this.isSilent())
			this.level().levelEvent(null, 1040, this.blockPosition(), 0);
	}

	public void convertToZombieType(EntityType<? extends Zombie> type) {
		this.resetDrownedConverting();
		this.changeInfo(type, this.saveData());
	}

	public boolean convertVillagerToZombieVillager(ServerLevel server, Villager villager) {
		ZombieVillager zombieVillager = villager.convertTo(EntityType.ZOMBIE_VILLAGER, ConversionParams.single(villager, true, true), zombie -> {
			zombie.finalizeSpawn(server, server.getCurrentDifficultyAt(zombie.blockPosition()), EntitySpawnReason.CONVERSION, new Zombie.ZombieGroupData(false, true));
			zombie.setVillagerData(villager.getVillagerData());
			zombie.setGossips(villager.getGossips().store(NbtOps.INSTANCE));
			zombie.setTradeOffers(villager.getOffers().copy());
			zombie.setVillagerXp(villager.getVillagerXp());
			net.neoforged.neoforge.event.EventHooks.onLivingConvert(villager, zombie);
			if (!this.isSilent())
				server.levelEvent(null, 1026, this.blockPosition(), 0);
		});
		return zombieVillager != null;
	}

	public boolean isSunSensitive() {
		return true;
	}

	@Override
	public boolean hurtServer(boolean original, ServerLevel server, DamageSource source, float damage) {
		if (!super.hurtServer(original, server, source, damage))
			return false;
		LivingEntity target = this.getTarget();
		if (target == null && source.getEntity() instanceof LivingEntity attacker)
			target = attacker;
		if (target != null && server.getDifficulty() == Difficulty.HARD && (double) this.random.nextFloat() < this.getAttributeValue(Attributes.SPAWN_REINFORCEMENTS_CHANCE) && server.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
			int i = Mth.floor(this.getX());
			int j = Mth.floor(this.getY());
			int k = Mth.floor(this.getZ());
			EntityType<? extends Zombie> entitytype = this.getType(null);
			Zombie zombie = entitytype.create(server, EntitySpawnReason.REINFORCEMENT);
			if (zombie == null)
				return false;
			for (int l = 0; l < 50; l++) {
				int i1 = i + Mth.nextInt(this.random, 7, 40) * Mth.nextInt(this.random, -1, 1);
				int j1 = j + Mth.nextInt(this.random, 7, 40) * Mth.nextInt(this.random, -1, 1);
				int k1 = k + Mth.nextInt(this.random, 7, 40) * Mth.nextInt(this.random, -1, 1);
				BlockPos blockpos = new BlockPos(i1, j1, k1);
				if (SpawnPlacements.isSpawnPositionOk(entitytype, server, blockpos) && SpawnPlacements.checkSpawnRules(entitytype, server, EntitySpawnReason.REINFORCEMENT, blockpos, server.random)) {
					zombie.setPos((double) i1, (double) j1, (double) k1);
					if (!server.hasNearbyAlivePlayer((double) i1, (double) j1, (double) k1, 7.0) && server.isUnobstructed(zombie) && server.noCollision(zombie) && (this.canSpawnInLiquids() || !server.containsAnyLiquid(zombie.getBoundingBox()))) {
						zombie.setTarget(target);
						zombie.finalizeSpawn(server, server.getCurrentDifficultyAt(zombie.blockPosition()), EntitySpawnReason.REINFORCEMENT, null);
						server.addFreshEntityWithPassengers(zombie);
						AttributeInstance attributeinstance = this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE);
						AttributeModifier attributemodifier = attributeinstance.getModifier(ChargeId);
						double d0 = attributemodifier != null ? attributemodifier.amount() : 0.0;
						attributeinstance.removeModifier(ChargeId);
						attributeinstance.addPermanentModifier(new AttributeModifier(ChargeId, d0 - 0.05, AttributeModifier.Operation.ADD_VALUE));
						zombie.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).addPermanentModifier(ChargeMod);
						break;
					}
				}
			}
		}
		return true;
	}

	@Override
	public SoundEvent getAmbientSound() {
		return SoundEvents.ZOMBIE_AMBIENT;
	}

	@Override
	public SoundEvent getHurtSound(SoundEvent original, DamageSource source) {
		return SoundEvents.ZOMBIE_HURT;
	}

	@Override
	public SoundEvent getDeathSound(SoundEvent original) {
		return SoundEvents.ZOMBIE_DEATH;
	}

	public SoundEvent getStepSound() {
		return SoundEvents.ZOMBIE_STEP;
	}

	@Override
	public boolean playStepSound(Object object, BlockPos pos, BlockState state) {
		this.playSound(this.getStepSound(), 0.15f, 1);
		return false;
	}

	@Override
	public EntityType<? extends Zombie> getType(EntityType original) {
		return (EntityType<? extends Zombie>) super.getType(original);
	}

	public boolean canSpawnInLiquids() {
		return false;
	}

	@Override
	public ItemStack spawnedMainHandStack() {
		if (this.random.nextFloat() < (this.level().getDifficulty() == Difficulty.HARD ? 0.05F : 0.01F)) {
			int i = this.random.nextInt(3);
			if (i == 0)
				return new ItemStack(Items.IRON_SWORD);
			return new ItemStack(Items.IRON_SHOVEL);
		}
		return super.spawnedMainHandStack();
	}

	@Override
	public CompoundTag saveData() {
		CompoundTag compound = super.saveData();
		compound.putInt("InWaterTime", this.isInWater() ? this.inWaterTime : -1);
		compound.putInt("DrownedConversionTime", this.isUnderWaterConverting() ? this.conversionTime : -1);
		return compound;
	}

	@Override
	public void loadData(CompoundTag compound) {
		super.loadData(compound);
		this.inWaterTime = compound.getInt("InWaterTime");
		if (compound.contains("DrownedConversionTime", 99) && compound.getInt("DrownedConversionTime") > -1)
			this.startUnderWaterConversion(compound.getInt("DrownedConversionTime"));
	}

	@Override
	public boolean killedEntity(boolean original, ServerLevel server, LivingEntity victim) {
		boolean flag = super.killedEntity(original, server, victim);
		DamageSource source = victim.getLastDamageSource();
		boolean isClose = this.closerThan(victim, 5);
		if (!isClose || (source != null && !source.isDirect()))
			return flag;
		if ((server.getDifficulty() == Difficulty.NORMAL || server.getDifficulty() == Difficulty.HARD) && victim instanceof Villager villager && net.neoforged.neoforge.event.EventHooks.canLivingConvert(victim, EntityType.ZOMBIE_VILLAGER, (timer) -> {
		})) {
			if (server.getDifficulty() != Difficulty.HARD && this.random.nextBoolean())
				return flag;
			if (this.convertVillagerToZombieVillager(server, villager))
				flag = false;
		}
		return flag;
	}

	@Override
	public EntityDimensions getDimensions(Pose pose) {
		return this.isBaby() ? this.dynamicHitbox(BabyHitbox, pose) : super.getDimensions(pose);
	}

	public void setInWaterTime(int time) {
		this.inWaterTime = time;
	}

	public void setConversionTime(int time) {
		this.conversionTime = time;
	}

	public void randomizeReinforcementsChance() {
		if (this.isBound())
			this.player.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * 0.1F);
	}

	@Override
	public boolean dropCustomDeathLoot(Object object, ServerLevel server, DamageSource source, boolean booleanValue) {
		if (!super.dropCustomDeathLoot(null, server, source, booleanValue))
			return false;
		ItemStack skull = this.getSkull();
		if (!skull.isEmpty()) {
			if (source.getEntity() instanceof Creeper creeper && creeper.canDropMobsSkull()) {
				creeper.increaseDroppedSkulls();
				this.spawnAtLocation(server, skull);
			} else if (source.getEntity() instanceof InfoHolder holder && holder.getInfo() instanceof CreeperInfo creeper && creeper.canDropMobsSkull()) {
				creeper.increaseDroppedSkulls();
				this.spawnAtLocation(server, skull);
			}
		}
		return true;
	}

	public ItemStack getSkull() {
		return new ItemStack(Items.ZOMBIE_HEAD);
	}
}