Skip to content

Commit

Permalink
Make Duck variants data-driven (#194)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hugman76 authored May 6, 2024
2 parents 319f698 + e65d893 commit ba45fa6
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package fr.hugman.promenade.client.render.entity;

import fr.hugman.promenade.Promenade;
import fr.hugman.promenade.client.render.entity.model.DuckModel;
import fr.hugman.promenade.client.render.entity.model.PromenadeEntityModelLayers;
import fr.hugman.promenade.entity.DuckEntity;
Expand All @@ -13,16 +12,13 @@

@Environment(EnvType.CLIENT)
public class DuckRenderer extends MobEntityRenderer<DuckEntity, DuckModel<DuckEntity>> {
private static final Identifier DUCKLING_TEXTURE = Promenade.id("textures/entity/duck/duckling.png");

public DuckRenderer(EntityRendererFactory.Context context) {
super(context, new DuckModel<>(context.getPart(PromenadeEntityModelLayers.DUCK)), 0.3F);
}

@Override
public Identifier getTexture(DuckEntity entity) {
if (entity.isBaby()) return DUCKLING_TEXTURE;
return Promenade.id("textures/entity/duck/" + entity.getVariant().getName() + ".png");
return entity.getTexture();
}

protected float getAnimationProgress(DuckEntity entity, float tickDelta) {
Expand Down
115 changes: 38 additions & 77 deletions src/main/java/fr/hugman/promenade/entity/DuckEntity.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package fr.hugman.promenade.entity;

import fr.hugman.promenade.Promenade;
import fr.hugman.promenade.entity.data.PromenadeTrackedData;
import fr.hugman.promenade.item.PromenadeItemTags;
import fr.hugman.promenade.registry.PromenadeRegistryKeys;
import fr.hugman.promenade.sound.PromenadeSoundEvents;
import fr.hugman.promenade.world.biome.PromenadeBiomeTags;
import net.minecraft.block.BlockState;
import net.minecraft.entity.*;
import net.minecraft.entity.ai.goal.*;
Expand All @@ -21,10 +23,11 @@
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
Expand All @@ -34,13 +37,13 @@
import net.minecraft.world.biome.Biome;

import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Collectors;
import java.util.Optional;

public class DuckEntity extends AnimalEntity {
private static final TrackedData<Integer> DUCK_TYPE = DataTracker.registerData(DuckEntity.class, TrackedDataHandlerRegistry.INTEGER);
public class DuckEntity extends AnimalEntity implements VariantHolder<RegistryEntry<DuckVariant>> {
private static final TrackedData<RegistryEntry<DuckVariant>> VARIANT = DataTracker.registerData(DuckEntity.class, PromenadeTrackedData.DUCK_VARIANT);
private static final EntityDimensions BABY_BASE_DIMENSIONS = EntityDimensions.changing(0.4F, 0.8F).scaled(0.5F).withEyeHeight(0.78125F);
public static final String TYPE_KEY = "type";

public static final String VARIANT_KEY = "variant";

public float wingRotation;
public float destPos;
Expand All @@ -60,20 +63,21 @@ public static Builder createDuckAttributes() {
@Override
protected void initDataTracker(DataTracker.Builder builder) {
super.initDataTracker(builder);
builder.add(DUCK_TYPE, 0);
builder.add(VARIANT, this.getRegistryManager().get(PromenadeRegistryKeys.DUCK_VARIANT).entryOf(DuckVariants.PEKIN));
}

@Override
public EntityData initialize(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, @org.jetbrains.annotations.Nullable EntityData entityData) {
RegistryEntry<Biome> biomeEntry = world.getBiome(this.getBlockPos());

DuckEntity.Type type = DuckEntity.Type.fromBiome(biomeEntry);
if (entityData instanceof DuckEntity.DuckData) {
type = ((DuckEntity.DuckData) entityData).type;
RegistryEntry<Biome> registryEntry = world.getBiome(this.getBlockPos());
RegistryEntry<DuckVariant> variant;
if (entityData instanceof DuckData duckData) {
variant = duckData.variant;
} else {
entityData = new DuckEntity.DuckData(type);
variant = DuckVariants.fromBiome(this.getRegistryManager(), registryEntry);
entityData = new DuckData(variant);
}
this.setVariant(type);

this.setVariant(variant);
return super.initialize(world, difficulty, spawnReason, entityData);
}

Expand All @@ -95,15 +99,18 @@ protected void initGoals() {
}

@Override
public void writeCustomDataToNbt(NbtCompound compound) {
super.writeCustomDataToNbt(compound);
compound.putString(TYPE_KEY, this.getVariant().getName());
public void writeCustomDataToNbt(NbtCompound nbt) {
super.writeCustomDataToNbt(nbt);
nbt.putString(VARIANT_KEY, (this.getVariant().getKey().orElse(DuckVariants.PEKIN)).getValue().toString());
}

@Override
public void readCustomDataFromNbt(NbtCompound compound) {
super.readCustomDataFromNbt(compound);
this.setVariant(DuckEntity.Type.byName(compound.getString(TYPE_KEY)));
public void readCustomDataFromNbt(NbtCompound nbt) {
super.readCustomDataFromNbt(nbt);
Optional.ofNullable(Identifier.tryParse(nbt.getString(VARIANT_KEY)))
.map(variantId -> RegistryKey.of(PromenadeRegistryKeys.DUCK_VARIANT, variantId))
.flatMap(variantKey -> this.getRegistryManager().get(PromenadeRegistryKeys.DUCK_VARIANT).getEntry(variantKey))
.ifPresent(this::setVariant);
}

@Override
Expand Down Expand Up @@ -192,71 +199,25 @@ protected void updatePassengerPosition(Entity passenger, Entity.PositionUpdater
}
}

public DuckEntity.Type getVariant() {
return DuckEntity.Type.fromId(this.dataTracker.get(DUCK_TYPE));
public RegistryEntry<DuckVariant> getVariant() {
return this.dataTracker.get(VARIANT);
}

private void setVariant(DuckEntity.Type type) {
this.dataTracker.set(DUCK_TYPE, type.getIndex());
public void setVariant(RegistryEntry<DuckVariant> registryEntry) {
this.dataTracker.set(VARIANT, registryEntry);
}

public enum Type {
PEKIN(0, "pekin", PromenadeBiomeTags.PEKIN_DUCK_SPAWN),
MALLARD(1, "mallard", PromenadeBiomeTags.MALLARD_DUCK_SPAWN);

private static final DuckEntity.Type[] typeList = Arrays.stream(values()).sorted(Comparator.comparingInt(DuckEntity.Type::getIndex)).toArray(Type[]::new);
private static final Map<String, DuckEntity.Type> TYPES_BY_NAME = Arrays.stream(values()).collect(Collectors.toMap(DuckEntity.Type::getName, (type) -> type));
private final int index;
private final String name;
private final TagKey<Biome> spawnBiomes;

Type(int indexIn, String nameIn, TagKey<Biome> spawnBiomes) {
this.index = indexIn;
this.name = nameIn;
this.spawnBiomes = spawnBiomes;
}

public static DuckEntity.Type byName(String name) {
return TYPES_BY_NAME.getOrDefault(name, PEKIN);
}

public static DuckEntity.Type fromId(int index) {
if (index < 0 || index > typeList.length) {
index = 0;
}
return typeList[index];
}

public static DuckEntity.Type fromBiome(RegistryEntry<Biome> biome) {
List<Type> shuffledList = Arrays.asList(typeList.clone());
Collections.shuffle(shuffledList);
for (DuckEntity.Type type : shuffledList) {
if (biome.isIn(type.getSpawnBiomes())) {
return type;
}
}
return PEKIN;
}

public String getName() {
return this.name;
}

public TagKey<Biome> getSpawnBiomes() {
return this.spawnBiomes;
}

public int getIndex() {
return this.index;
}
public Identifier getTexture() {
if (this.isBaby()) return this.getVariant().value().babyTexture();
return this.getVariant().value().texture();
}

public static class DuckData extends PassiveEntity.PassiveData {
public final DuckEntity.Type type;
public final RegistryEntry<DuckVariant> variant;

public DuckData(DuckEntity.Type typeIn) {
public DuckData(RegistryEntry<DuckVariant> variant) {
super(false);
this.type = typeIn;
this.variant = variant;
}
}
}
72 changes: 72 additions & 0 deletions src/main/java/fr/hugman/promenade/entity/DuckVariant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package fr.hugman.promenade.entity;

import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import fr.hugman.promenade.Promenade;
import net.minecraft.entity.passive.WolfVariant;
import net.minecraft.loot.LootTable;
import net.minecraft.registry.RegistryCodecs;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntryList;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;
import net.minecraft.world.biome.Biome;

import java.util.Objects;
import java.util.Optional;

public final class DuckVariant {
private static final Identifier DEFAULT_DUCKLING_TEXTURE = Promenade.id("entity/duck/duckling");

public static final Codec<DuckVariant> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Identifier.CODEC.fieldOf("texture").forGetter(duck -> duck.texture),
Identifier.CODEC.optionalFieldOf("baby_texture", DEFAULT_DUCKLING_TEXTURE).forGetter(duck -> duck.babyTexture),
RegistryCodecs.entryList(RegistryKeys.BIOME).fieldOf("biomes").forGetter(duck -> duck.biomes)
).apply(instance, DuckVariant::new));

private final Identifier texture;
private final Identifier babyTexture;
private final RegistryEntryList<Biome> biomes;

private final Identifier texturePath;
private final Identifier babyTexturePath;

public DuckVariant(Identifier texture, Identifier babyTexture, RegistryEntryList<Biome> biomes) {
this.texture = texture;
this.babyTexture = babyTexture;
this.biomes = biomes;

this.texturePath = getTexturePath(texture);
this.babyTexturePath = getTexturePath(babyTexture);
}

public Identifier texture() {
return texturePath;
}

public Identifier babyTexture() {
return babyTexturePath;
}

public RegistryEntryList<Biome> getBiomes() {
return this.biomes;
}

private static Identifier getTexturePath(Identifier id) {
return id.withPath(oldPath -> "textures/" + oldPath + ".png");
}

@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (DuckVariant) obj;
return Objects.equals(this.texture, that.texture) && Objects.equals(this.babyTexture, that.babyTexture);
}

@Override
public int hashCode() {
return Objects.hash(texture, babyTexture);
}
}
30 changes: 30 additions & 0 deletions src/main/java/fr/hugman/promenade/entity/DuckVariants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package fr.hugman.promenade.entity;

import fr.hugman.promenade.Promenade;
import fr.hugman.promenade.registry.PromenadeRegistryKeys;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.Identifier;
import net.minecraft.world.biome.Biome;

public class DuckVariants {
public static final RegistryKey<DuckVariant> PEKIN = of("pekin");

private static RegistryKey<DuckVariant> of(String path) {
return of(Promenade.id(path));
}

public static RegistryKey<DuckVariant> of(Identifier id) {
return RegistryKey.of(PromenadeRegistryKeys.DUCK_VARIANT, id);
}

public static RegistryEntry<DuckVariant> fromBiome(DynamicRegistryManager dynamicRegistryManager, RegistryEntry<Biome> biome) {
Registry<DuckVariant> registry = dynamicRegistryManager.get(PromenadeRegistryKeys.DUCK_VARIANT);
return registry.streamEntries()
.filter(entry -> entry.value().getBiomes().contains(biome))
.findFirst()
.orElse(registry.entryOf(PEKIN));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import fr.hugman.promenade.entity.CapybaraEntity;
import fr.hugman.promenade.entity.CapybaraVariant;
import fr.hugman.promenade.entity.DuckVariant;
import fr.hugman.promenade.entity.SunkenVariant;
import fr.hugman.promenade.registry.PromenadeRegistryKeys;
import net.minecraft.entity.data.TrackedDataHandler;
Expand All @@ -12,6 +13,7 @@
import net.minecraft.registry.entry.RegistryEntry;

public class PromenadeTrackedData {
public static final TrackedDataHandler<RegistryEntry<DuckVariant>> DUCK_VARIANT = of(PacketCodecs.registryEntry(PromenadeRegistryKeys.DUCK_VARIANT));
public static final TrackedDataHandler<RegistryEntry<CapybaraVariant>> CAPYBARA_VARIANT = of(PacketCodecs.registryEntry(PromenadeRegistryKeys.CAPYBARA_VARIANT));
public static final TrackedDataHandler<RegistryEntry<SunkenVariant>> SUNKEN_VARIANT = of(PacketCodecs.registryEntry(PromenadeRegistryKeys.SUNKEN_VARIANT));
public static final TrackedDataHandler<CapybaraEntity.State> CAPYBARA_STATE = of(CapybaraEntity.State.PACKET_CODEC);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package fr.hugman.promenade.registry;

import fr.hugman.promenade.entity.CapybaraVariant;
import fr.hugman.promenade.entity.DuckVariant;
import fr.hugman.promenade.entity.SunkenVariant;
import net.fabricmc.fabric.api.event.registry.DynamicRegistries;

public class PromenadeRegistries {
public static void register() {
DynamicRegistries.registerSynced(PromenadeRegistryKeys.DUCK_VARIANT, DuckVariant.CODEC);
DynamicRegistries.registerSynced(PromenadeRegistryKeys.CAPYBARA_VARIANT, CapybaraVariant.CODEC);
DynamicRegistries.registerSynced(PromenadeRegistryKeys.SUNKEN_VARIANT, SunkenVariant.CODEC);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import fr.hugman.promenade.Promenade;
import fr.hugman.promenade.entity.CapybaraVariant;
import fr.hugman.promenade.entity.DuckVariant;
import fr.hugman.promenade.entity.SunkenVariant;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;

public class PromenadeRegistryKeys {
public static final RegistryKey<Registry<DuckVariant>> DUCK_VARIANT = RegistryKey.ofRegistry(Promenade.id("duck_variant"));
public static final RegistryKey<Registry<CapybaraVariant>> CAPYBARA_VARIANT = RegistryKey.ofRegistry(Promenade.id("capybara_variant"));
public static final RegistryKey<Registry<SunkenVariant>> SUNKEN_VARIANT = RegistryKey.ofRegistry(Promenade.id("sunken_variant"));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"texture": "promenade:entity/duck/mallard",
"biomes": "#promenade:spawns/duck/mallard"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"texture": "promenade:entity/duck/pekin",
"biomes": "#promenade:spawns/duck/pekin"
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
{
"replace": false,
"values": [
"minecraft:plains",
"minecraft:river",
"minecraft:swamp"
"#minecraft:is_ocean",
"#minecraft:is_river"
]
}

0 comments on commit ba45fa6

Please sign in to comment.