package com.bitzlay.albanehorsegenetics.entity.horse; import com.bitzlay.albanehorsegenetics.AlbaneHorseGeneticsMod; import com.bitzlay.albanehorsegenetics.genetics.data.HorseGenome; import com.bitzlay.albanehorsegenetics.entity.horse.HorseStats; import com.bitzlay.albanehorsegenetics.genetics.inheritance.BreedingManager; import com.bitzlay.albanehorsegenetics.network.NetworkHandler; import com.bitzlay.albanehorsegenetics.network.packets.HorseDataSyncPacket; import com.bitzlay.albanehorsegenetics.world.inventory.HorseTackContainer; import net.minecraft.commands.arguments.EntityAnchorArgument; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; 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.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.util.Mth; import net.minecraft.world.Container; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.MenuProvider; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.*; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.horse.Horse; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemStackHandler; import net.minecraftforge.network.NetworkHooks; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import software.bernie.geckolib.animatable.GeoEntity; import software.bernie.geckolib.constant.DefaultAnimations; import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache; import software.bernie.geckolib.core.animation.*; import software.bernie.geckolib.core.animation.Animation.LoopType; import software.bernie.geckolib.core.object.PlayState; import software.bernie.geckolib.util.GeckoLibUtil; public class GeneticHorseEntity extends Horse implements GeoEntity, MenuProvider { /* --------------------------------------------------------------------------- SYNCHRONIZED DATA --------------------------------------------------------------------------- */ private static final EntityDataAccessor PLAY_PAS; private static final EntityDataAccessor PLAY_TROT; private static final EntityDataAccessor PLAY_RASSEMBLE; // NOUVEAU private static final EntityDataAccessor PLAY_GALOP; private static final EntityDataAccessor PLAY_ALLONGE; private static final EntityDataAccessor PLAY_UNHAPPY; // Animation de refus private static final EntityDataAccessor GENOME_DATA; private static final EntityDataAccessor IS_PREGNANT; private static final EntityDataAccessor PREGNANCY_TIME; private static final EntityDataAccessor GROWTH_FACTOR; private static final EntityDataAccessor HAIRCUT_ID; private static final EntityDataAccessor IS_LONGEING; private static final int GESTATION_TIME = 40; private static final int JUMP_ANIM_DURATION = 10; private static final int UNHAPPY_ANIM_DURATION = 25; private static final int TACK_SLOTS = 15; /* --------------------------------------------------------------------------- INSTANCE DATA --------------------------------------------------------------------------- */ private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this); private HorseGenome genome; private boolean genomeInitialized = false; private HorseGenome storedStallionGenome; private HorseStats horseStats; private boolean jumpingRecently = false; private int jumpTicks = 0; private int unhappyTicks = 0; private final ItemStackHandler tackInventory = new ItemStackHandler(TACK_SLOTS) { @Override protected void onContentsChanged(int slot) { if (!GeneticHorseEntity.this.level().isClientSide) { GeneticHorseEntity.this.level().getChunkAt(GeneticHorseEntity.this.blockPosition()).setUnsaved(true); } } }; private final LazyOptional inventoryCapability = LazyOptional.of(() -> this.tackInventory); /* --------------------------------------------------------------------------- CONSTRUCTOR --------------------------------------------------------------------------- */ public GeneticHorseEntity(EntityType entityType, Level level) { super(entityType, level); if (!level.isClientSide && !this.genomeInitialized) { this.initializeGenome(); } } /* --------------------------------------------------------------------------- ATTRIBUTES --------------------------------------------------------------------------- */ public static AttributeSupplier.Builder createAttributes() { return Horse.createBaseHorseAttributes() .add(Attributes.MAX_HEALTH, 30.0D) .add(Attributes.MOVEMENT_SPEED, 0.2D) .add(Attributes.JUMP_STRENGTH, 1.0D); } /* --------------------------------------------------------------------------- INVENTORY CAPABILITY --------------------------------------------------------------------------- */ @NotNull @Override public LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { if (cap == ForgeCapabilities.ITEM_HANDLER) { return this.inventoryCapability.cast(); } return super.getCapability(cap, side); } @Override public void invalidateCaps() { super.invalidateCaps(); this.inventoryCapability.invalidate(); } /* --------------------------------------------------------------------------- GENOME --------------------------------------------------------------------------- */ private void initializeGenome() { if (!level().isClientSide && !genomeInitialized) { try { if (genome == null) { genome = HorseGenome.randomWild(); AlbaneHorseGeneticsMod.LOGGER.info( "Nouveau cheval créé avec génome (serveur) : {}", this.getId()); } genomeInitialized = true; applyGenomeToStats(); syncGenomeToClient(); } catch (Exception e) { AlbaneHorseGeneticsMod.LOGGER.error( "Error initializing genome for horse {}: {}", this.getId(), e.getMessage()); } } } private void applyGenomeToStats() { if (genome == null) return; horseStats = genome.getStats(); if (horseStats == null) return; if (!level().isClientSide) { if (getAttribute(Attributes.MAX_HEALTH) != null) getAttribute(Attributes.MAX_HEALTH).setBaseValue(horseStats.getHealth()); setHealth((float) horseStats.getHealth()); if (getAttribute(Attributes.MOVEMENT_SPEED) != null) getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(horseStats.getSpeed()); if (getAttribute(Attributes.JUMP_STRENGTH) != null) getAttribute(Attributes.JUMP_STRENGTH).setBaseValue(horseStats.getJumpStrength()); } } public HorseGenome getGenome() { if (genome == null) { if (!level().isClientSide) initializeGenome(); else { genome = HorseGenome.randomWild(); AlbaneHorseGeneticsMod.LOGGER.debug( "Genoma temporaire créé côté client pour cheval {}", this.getId()); } } return genome; } public void setGenome(HorseGenome genome) { this.genome = genome; if (!level().isClientSide) { applyGenomeToStats(); syncGenomeToClient(); } } public void syncGenomeToClient() { if (level().isClientSide || genome == null) return; try { String data = genome.serialize(); entityData.set(GENOME_DATA, data); if (level() instanceof ServerLevel server) { server.players().stream() .filter(p -> p.distanceToSqr(this) < 4096.0D) .forEach(p -> NetworkHandler.sendToPlayer( new HorseDataSyncPacket(getId(), data), p)); } } catch (Exception e) { AlbaneHorseGeneticsMod.LOGGER.error( "Error syncing genome for horse {}: {}", this.getId(), e.getMessage()); } } @Override public void onSyncedDataUpdated(EntityDataAccessor key) { super.onSyncedDataUpdated(key); if (GENOME_DATA.equals(key) && level().isClientSide) { try { String data = entityData.get(GENOME_DATA); if (data != null && !data.isEmpty()) { genome = HorseGenome.deserialize(data); applyGenomeToStats(); genomeInitialized = true; refreshDimensions(); } } catch (Exception e) { AlbaneHorseGeneticsMod.LOGGER.error( "Error deserializing genome client-side for horse {}: {}", getId(), e.getMessage()); } } } /* --------------------------------------------------------------------------- HAIRCUT / CLIPPER --------------------------------------------------------------------------- */ public String getHaircutId() { return entityData.get(HAIRCUT_ID); } public void setHaircutId(String id) { this.entityData.set(HAIRCUT_ID, id); } /* --------------------------------------------------------------------------- GROWTH & DIMENSIONS --------------------------------------------------------------------------- */ public float getGrowth() { return entityData.get(GROWTH_FACTOR); } public void setGrowth(float value) { entityData.set(GROWTH_FACTOR, Mth.clamp(value, 0f, 1f)); } public float getHorseHeightScale() { if (horseStats == null) return 1f; final float adultHeight = horseStats.getHeight(); float scale; if (adultHeight < 156.0f) { scale = 1.0f + ((adultHeight - 156.0f) / 156.0f) * 0.4f; } else { scale = 1.0f + ((adultHeight - 156.0f) / 156.0f) * 0.9f; } if (isBaby()) { scale *= getGrowth(); } return scale; } @Override public EntityDimensions getDimensions(Pose pose) { return super.getDimensions(pose).scale(1.0F, getHorseHeightScale()); } @Override public double getPassengersRidingOffset() { return super.getPassengersRidingOffset() * getHorseHeightScale(); } @Override public float getEyeHeight(Pose pose) { return super.getEyeHeight(pose) * getHorseHeightScale(); } @Override protected void positionRider(Entity passenger, Entity.MoveFunction callback) { if (this.hasPassenger(passenger)) { double offsetRecul = -0.3D; double scaledBackOffset = offsetRecul * getHorseHeightScale(); double yPos = this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(); double rads = Math.toRadians(this.yBodyRot); double xPos = this.getX() - (Math.sin(rads) * scaledBackOffset); double zPos = this.getZ() + (Math.cos(rads) * scaledBackOffset); callback.accept(passenger, xPos, yPos, zPos); } else { super.positionRider(passenger, callback); } } /* --------------------------------------------------------------------------- TICK --------------------------------------------------------------------------- */ @Override public void tick() { super.tick(); if (!level().isClientSide && !genomeInitialized) initializeGenome(); if (!level().isClientSide) { if (isLongeing() && getLeashHolder() instanceof Player player) { double radius = 5.0; double speed = 0.05; double centerX = player.getX(); double centerZ = player.getZ(); double angle = (level().getGameTime() * speed); double targetX = centerX + Math.cos(angle) * radius; double targetZ = centerZ + Math.sin(angle) * radius; this.getNavigation().moveTo(targetX, player.getY(), targetZ, 1.5D); if (this.distanceToSqr(targetX, getY(), targetZ) > 1.0) { double moveX = (targetX - this.getX()) * 0.2; double moveZ = (targetZ - this.getZ()) * 0.2; this.setDeltaMovement(moveX, this.getDeltaMovement().y, moveZ); } double lookX = centerX + Math.cos(angle + 0.5) * radius; double lookZ = centerZ + Math.sin(angle + 0.5) * radius; this.lookAt(EntityAnchorArgument.Anchor.EYES, new Vec3(lookX, this.getEyeY(), lookZ)); this.yBodyRot = this.yRotO; if (this.distanceTo(player) > 12.0) { this.setLongeing(false); } } } if (!level().isClientSide && isPregnant()) { int time = getPregnancyTime() + 1; setPregnancyTime(time); if (time >= GESTATION_TIME) giveBirth(); } if (!level().isClientSide && isBaby()) { float currentGrowth = getGrowth(); float increment = 0.00000111f; float next = Mth.clamp(currentGrowth + increment, 0f, 1f); setGrowth(next); if (next >= 1.0f) { setGrowth(1.0f); this.setBaby(false); } } if (isSprinting()) { jumpingRecently = false; jumpTicks = 0; } else if (getDeltaMovement().y > 0.1 && !onGround() && getControllingPassenger() != null) { jumpingRecently = true; jumpTicks = JUMP_ANIM_DURATION; } if (jumpingRecently && level().isClientSide) { triggerAnim("jump_controller", "move.jump"); if (--jumpTicks <= 0) jumpingRecently = false; } // Gestion unhappy if (!this.level().isClientSide) { if (isUnhappyPlaying()) { if (unhappyTicks > 0) { unhappyTicks--; } else { setUnhappyPlaying(false); } } } } /* --------------------------------------------------------------------------- UNHAPPY ANIMATION --------------------------------------------------------------------------- */ public boolean isUnhappyPlaying() { return entityData.get(PLAY_UNHAPPY); } public void setUnhappyPlaying(boolean b) { entityData.set(PLAY_UNHAPPY, b); } public void playUnhappyAnimation() { if (!this.level().isClientSide) { this.setUnhappyPlaying(true); this.unhappyTicks = UNHAPPY_ANIM_DURATION; } } @Override public void setStanding(boolean standing) { super.setStanding(standing); if (standing && !this.isTamed()) { this.playUnhappyAnimation(); } } /* --------------------------------------------------------------------------- REPRODUCTION --------------------------------------------------------------------------- */ public boolean isPregnant() { return entityData.get(IS_PREGNANT); } public void setPregnant(boolean p) { entityData.set(IS_PREGNANT, p); if (!p) setPregnancyTime(0); } public int getPregnancyTime() { return entityData.get(PREGNANCY_TIME); } public void setPregnancyTime(int t) { entityData.set(PREGNANCY_TIME, t); } public void setStoredStallionGenome(HorseGenome g) { storedStallionGenome = g; } public void startPregnancy(HorseGenome stallionGenome, Player player) { if (level().isClientSide || isPregnant()) return; storedStallionGenome = stallionGenome; setPregnant(true); setPregnancyTime(0); if (player != null) player.sendSystemMessage(Component.translatable("message.albanehorsegenetics.pregnancy_started", GESTATION_TIME / 20)); } private void giveBirth() { if (level().isClientSide || storedStallionGenome == null) return; try { HorseGenome childGenome = BreedingManager.breedHorses(getGenome(), storedStallionGenome); GeneticHorseEntity foal = (GeneticHorseEntity) this.getType().create(this.level()); if (foal != null) { foal.setGenome(childGenome); foal.syncGenomeToClient(); foal.setBaby(true); foal.setGrowth(0.75f); foal.moveTo(getX(), getY(), getZ(), getYRot(), 0.0F); level().addFreshEntity(foal); level().playSound(null, getX(), getY(), getZ(), SoundEvents.HORSE_AMBIENT, getSoundSource(), 1f, 1f); setPregnant(false); storedStallionGenome = null; if (level() instanceof ServerLevel serverLevel) serverLevel.players().stream() .filter(p -> p.distanceToSqr(this) < 100) .forEach(p -> p.sendSystemMessage(Component.translatable("message.albanehorsegenetics.foal_born"))); } } catch (Exception e) { AlbaneHorseGeneticsMod.LOGGER.error("Erreur durant la parturition : {}", e.getMessage()); setPregnant(false); storedStallionGenome = null; } } @Override public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob otherParent) { if (!(otherParent instanceof GeneticHorseEntity geneticParent)) return null; GeneticHorseEntity child = (GeneticHorseEntity) this.getType().create(world); if (child != null) { HorseGenome childGenome = BreedingManager.breedHorses(getGenome(), geneticParent.getGenome()); child.setGenome(childGenome); child.syncGenomeToClient(); child.setBaby(true); child.setGrowth(0.6f); } return child; } /* --------------------------------------------------------------------------- SAVE & LOAD --------------------------------------------------------------------------- */ @Override public void addAdditionalSaveData(CompoundTag tag) { super.addAdditionalSaveData(tag); try { if (genome != null) { if (this.hasCustomName()) { this.genome.setHorseName(this.getCustomName().getString()); } else { this.genome.setHorseName(""); } tag.putString("HorseGenome", genome.serialize()); } if (storedStallionGenome != null) tag.putString("StoredStallionGenome", storedStallionGenome.serialize()); tag.putBoolean("IsPregnant", isPregnant()); tag.putInt("PregnancyTime", getPregnancyTime()); tag.putBoolean("GenomeInitialized", genomeInitialized); tag.putFloat("GrowthFactor", getGrowth()); tag.putString("HaircutId", getHaircutId()); tag.put("TackInventory", tackInventory.serializeNBT()); } catch (Exception e) { AlbaneHorseGeneticsMod.LOGGER.error("Error saving horse {}: {}", getId(), e.getMessage()); } } @Override public void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); try { if (tag.contains("HorseGenome")) { genome = HorseGenome.deserialize(tag.getString("HorseGenome")); } else if (tag.contains("Genome")) { genome = HorseGenome.deserialize(tag.getString("Genome")); } genomeInitialized = tag.getBoolean("GenomeInitialized"); if (genome == null && !level().isClientSide) { AlbaneHorseGeneticsMod.LOGGER.error("Cheval #{} chargé sans HorseGenome dans NBT. Il sera réinitialisé au prochain tick s'il n'est pas initialized.", this.getId()); } if (genome != null) { applyGenomeToStats(); if (!genome.getHorseName().isEmpty() && !this.hasCustomName()) { this.setCustomName(Component.literal(genome.getHorseName())); this.setCustomNameVisible(true); } } if (tag.contains("StoredStallionGenome")) storedStallionGenome = HorseGenome.deserialize(tag.getString("StoredStallionGenome")); setPregnant(tag.getBoolean("IsPregnant")); setPregnancyTime(tag.getInt("PregnancyTime")); setGrowth(tag.contains("GrowthFactor") ? tag.getFloat("GrowthFactor") : 1f); if (tag.contains("HaircutId")) { setHaircutId(tag.getString("HaircutId")); } if (tag.contains("TackInventory")) { tackInventory.deserializeNBT(tag.getCompound("TackInventory")); } } catch (Exception e) { AlbaneHorseGeneticsMod.LOGGER.error("Error loading horse {}: {}", getId(), e.getMessage()); if (!level().isClientSide) { genome = HorseGenome.randomWild(); genomeInitialized = true; AlbaneHorseGeneticsMod.LOGGER.warn("Erreur irrécupérable de chargement de génome pour le cheval {}, un nouveau génome aléatoire a été créé.", getId()); } } if (!level().isClientSide && genome != null) syncGenomeToClient(); } /* --------------------------------------------------------------------------- INVENTORY ACCESS --------------------------------------------------------------------------- */ public Container getHorseVanillaInventory() { return this.inventory; } /* --------------------------------------------------------------------------- INTERACTION & GUI --------------------------------------------------------------------------- */ @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { if (player.isSecondaryUseActive()) { return this.openTackGui(player); } InteractionResult interactionResult = super.mobInteract(player, hand); if (interactionResult.consumesAction()) { return interactionResult; } if (!this.level().isClientSide && !this.isVehicle()) { this.doPlayerRide(player); return InteractionResult.sidedSuccess(this.level().isClientSide); } return InteractionResult.PASS; } private InteractionResult openTackGui(Player player) { if (this.isVehicle()) { return InteractionResult.PASS; } if (!this.level().isClientSide() && player instanceof ServerPlayer serverPlayer) { NetworkHooks.openScreen(serverPlayer, this, buf -> { buf.writeInt(this.getId()); }); } return InteractionResult.sidedSuccess(this.level().isClientSide()); } @Override public Component getDisplayName() { if (this.hasCustomName()) { return this.getCustomName(); } if (this.genome != null && !this.genome.getHorseName().isEmpty()) { return Component.literal(this.genome.getHorseName()); } return super.getDisplayName(); // } @Override public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) { return new HorseTackContainer(id, inventory, this); } /* --------------------------------------------------------------------------- ANIMATION & MOVEMENT --------------------------------------------------------------------------- */ public boolean isPasPlaying() { return entityData.get(PLAY_PAS); } public void setPasPlaying(boolean b) { entityData.set(PLAY_PAS, b); } public boolean isTrotPlaying() { return entityData.get(PLAY_TROT); } public void setTrotPlaying(boolean b) { entityData.set(PLAY_TROT, b); } public boolean isRassemblePlaying() { return entityData.get(PLAY_RASSEMBLE); } public void setRassemblePlaying(boolean b) { entityData.set(PLAY_RASSEMBLE, b); } public boolean isGalopPlaying() { return entityData.get(PLAY_GALOP); } public void setGalopPlaying(boolean b) { entityData.set(PLAY_GALOP, b); } public boolean isAllongePlaying() { return entityData.get(PLAY_ALLONGE); } public void setAllongePlaying(boolean b) { entityData.set(PLAY_ALLONGE, b); } public boolean isLongeing() { return entityData.get(IS_LONGEING); } public void setLongeing(boolean longeing) { this.entityData.set(IS_LONGEING, longeing); } @Nullable @Override public LivingEntity getControllingPassenger() { Entity p = getFirstPassenger(); return p instanceof LivingEntity l ? l : null; } @Override public void travel(Vec3 travelVector) { if (isVehicle()) setSpeed((float)getAttributeValue(Attributes.MOVEMENT_SPEED)); super.travel(travelVector); } @Override protected void defineSynchedData() { super.defineSynchedData(); entityData.define(PLAY_PAS, false); entityData.define(PLAY_TROT, false); entityData.define(PLAY_RASSEMBLE, false); entityData.define(PLAY_GALOP, false); entityData.define(PLAY_ALLONGE, false); entityData.define(PLAY_UNHAPPY, false); // Définition unhappy entityData.define(GENOME_DATA, ""); entityData.define(IS_PREGNANT, false); entityData.define(PREGNANCY_TIME, 0); entityData.define(GROWTH_FACTOR, 1f); entityData.define(HAIRCUT_ID, "none"); entityData.define(IS_LONGEING, false); } @Override public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { controllers.add(DefaultAnimations.genericWalkIdleController(this)); controllers.add(new AnimationController<>(this, "jump_controller", 0, s -> PlayState.CONTINUE) .triggerableAnim("move.jump", RawAnimation.begin().then("move.jump", LoopType.PLAY_ONCE))); controllers.add(new AnimationController<>(this, "main_controller", 10, state -> { if (jumpingRecently) return PlayState.STOP; boolean ridden = isSaddled() && getControllingPassenger() instanceof Player; boolean moving = getDeltaMovement().horizontalDistanceSqr() > 0.001; // PRIORITÉ UNHAPPY if (isUnhappyPlaying()) return state.setAndContinue(RawAnimation.begin().then("unhappy", LoopType.PLAY_ONCE)); if (isLongeing()) return state.setAndContinue(RawAnimation.begin().then("galop", LoopType.LOOP)); if (isAllongePlaying()) return state.setAndContinue(RawAnimation.begin().then("allonge", LoopType.LOOP)); if (isGalopPlaying()) return state.setAndContinue(RawAnimation.begin().then("galop", LoopType.LOOP)); if (isRassemblePlaying()) return state.setAndContinue(RawAnimation.begin().then("rassemble", LoopType.LOOP)); if (isTrotPlaying()) return state.setAndContinue(RawAnimation.begin().then("trot", LoopType.LOOP)); if (isPasPlaying()) return state.setAndContinue(RawAnimation.begin().then("move.walk", LoopType.LOOP)); return (!ridden && moving) ? state.setAndContinue(RawAnimation.begin().then("move.walk", LoopType.LOOP)) : state.setAndContinue(RawAnimation.begin().then("misc.idle", LoopType.LOOP)); })); } @Override public AnimatableInstanceCache getAnimatableInstanceCache() { return cache; } /* --------------------------------------------------------------------------- SOUNDS --------------------------------------------------------------------------- */ @Override protected SoundEvent getAmbientSound() { return SoundEvents.HORSE_AMBIENT; } @Override protected SoundEvent getDeathSound() { return SoundEvents.HORSE_DEATH; } @Override protected SoundEvent getHurtSound(DamageSource src) { return SoundEvents.HORSE_HURT; } protected SoundEvent getEatingSound() { return SoundEvents.HORSE_EAT; } protected SoundEvent getGallopSound() { return SoundEvents.HORSE_GALLOP; } /* --------------------------------------------------------------------------- STATIC INIT --------------------------------------------------------------------------- */ static { PLAY_PAS = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.BOOLEAN); PLAY_TROT = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.BOOLEAN); PLAY_RASSEMBLE = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.BOOLEAN); PLAY_GALOP = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.BOOLEAN); PLAY_ALLONGE = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.BOOLEAN); PLAY_UNHAPPY = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.BOOLEAN); GENOME_DATA = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.STRING); IS_PREGNANT = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.BOOLEAN); PREGNANCY_TIME = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.INT); GROWTH_FACTOR = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.FLOAT); HAIRCUT_ID = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.STRING); IS_LONGEING = SynchedEntityData.defineId(GeneticHorseEntity.class, EntityDataSerializers.BOOLEAN); } }