diff --git a/src/main/java/com/minelittlepony/unicopia/UEntities.java b/src/main/java/com/minelittlepony/unicopia/UEntities.java index c33486ad..d2bf34f1 100644 --- a/src/main/java/com/minelittlepony/unicopia/UEntities.java +++ b/src/main/java/com/minelittlepony/unicopia/UEntities.java @@ -1,9 +1,11 @@ package com.minelittlepony.unicopia; +import com.minelittlepony.unicopia.entity.ButterflyEntity; import com.minelittlepony.unicopia.entity.CastSpellEntity; import com.minelittlepony.unicopia.entity.FloatingArtefactEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; +import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry; import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityDimensions; @@ -14,6 +16,8 @@ import net.minecraft.util.registry.Registry; public interface UEntities { + EntityType BUTTERFLY = register("butterfly", FabricEntityTypeBuilder.create(SpawnGroup.AMBIENT, ButterflyEntity::new) + .dimensions(EntityDimensions.fixed(0.25F, 0.25F))); EntityType THROWN_ITEM = register("thrown_item", FabricEntityTypeBuilder.create(SpawnGroup.MISC, MagicProjectileEntity::new) .trackRangeBlocks(100) .trackedUpdateRate(2) @@ -30,5 +34,7 @@ public interface UEntities { return Registry.register(Registry.ENTITY_TYPE, new Identifier("unicopia", name), type); } - static void bootstrap() {} + static void bootstrap() { + FabricDefaultAttributeRegistry.register(BUTTERFLY, ButterflyEntity.createButterflyAttributes()); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/Unicopia.java b/src/main/java/com/minelittlepony/unicopia/Unicopia.java index bf685958..6f4fdfc6 100644 --- a/src/main/java/com/minelittlepony/unicopia/Unicopia.java +++ b/src/main/java/com/minelittlepony/unicopia/Unicopia.java @@ -42,6 +42,7 @@ public class Unicopia implements ModInitializer { Channel.bootstrap(); UTags.bootstrap(); UCriteria.bootstrap(); + UEntities.bootstrap(); Commands.bootstrap(); ServerTickEvents.END_WORLD_TICK.register(w -> { diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index b6e2a59b..01020c80 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -16,6 +16,7 @@ import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer; import com.minelittlepony.unicopia.client.render.AmuletFeatureRenderer; import com.minelittlepony.unicopia.client.render.BatWingsFeatureRenderer; import com.minelittlepony.unicopia.client.render.BraceletFeatureRenderer; +import com.minelittlepony.unicopia.client.render.ButterflyEntityRenderer; import com.minelittlepony.unicopia.client.render.CastSpellEntityRenderer; import com.minelittlepony.unicopia.client.render.FloatingArtefactEntityRenderer; import com.minelittlepony.unicopia.client.render.IcarusWingsFeatureRenderer; @@ -65,6 +66,7 @@ public interface URenderers { AccessoryFeatureRenderer.register(BatWingsFeatureRenderer::new); EntityRendererRegistry.INSTANCE.register(UEntities.THROWN_ITEM, FlyingItemEntityRenderer::new); + EntityRendererRegistry.INSTANCE.register(UEntities.BUTTERFLY, ButterflyEntityRenderer::new); EntityRendererRegistry.INSTANCE.register(UEntities.FLOATING_ARTEFACT, FloatingArtefactEntityRenderer::new); EntityRendererRegistry.INSTANCE.register(UEntities.CAST_SPELL, CastSpellEntityRenderer::new); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/ButterflyEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/ButterflyEntityRenderer.java new file mode 100644 index 00000000..1ce54975 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/ButterflyEntityRenderer.java @@ -0,0 +1,95 @@ +package com.minelittlepony.unicopia.client.render; + +import com.minelittlepony.unicopia.entity.ButterflyEntity; + +import net.minecraft.client.model.ModelData; +import net.minecraft.client.model.ModelPart; +import net.minecraft.client.model.ModelPartBuilder; +import net.minecraft.client.model.ModelPartData; +import net.minecraft.client.model.ModelTransform; +import net.minecraft.client.model.TexturedModelData; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.render.entity.MobEntityRenderer; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; + +public class ButterflyEntityRenderer extends MobEntityRenderer { + public ButterflyEntityRenderer(EntityRendererFactory.Context context) { + super(context, new ButterflyEntityModel(ButterflyEntityModel.getData().createModel()), 0.25F); + shadowRadius = 0.2F; + shadowOpacity = 0.3F; + } + + @Override + public Identifier getTexture(ButterflyEntity entity) { + return entity.getVariant().getSkin(); + } + + @Override + protected void scale(ButterflyEntity entity, MatrixStack matrices, float ticks) { + matrices.scale(0.35F, 0.35F, 0.35F); + matrices.translate(0.5F, 0, -0.5F); + } + + @Override + protected void setupTransforms(ButterflyEntity entity, MatrixStack matrices, float age, float yaw, float ticks) { + + if (!entity.isResting()) { + matrices.translate(0, MathHelper.cos(age / 3F) / 10F, 0); + } + + super.setupTransforms(entity, matrices, age, yaw, ticks); + } + + public static class ButterflyEntityModel extends EntityModel { + private final ModelPart body; + private final ModelPart leftWing; + private final ModelPart rightWing; + + public ButterflyEntityModel(ModelPart tree) { + super(RenderLayers::getEntityAlpha); + body = tree; + body.pivotX = -10; + body.pivotY = 12; + leftWing = tree.getChild("left_wing"); + rightWing = tree.getChild("right_wing"); + } + + static TexturedModelData getData() { + ModelData data = new ModelData(); + ModelPartData tree = data.getRoot(); + + tree.addChild("right_wing", ModelPartBuilder.create().uv(42, 0).cuboid(-13, -5, 0, 10, 19, 1), ModelTransform.rotation(0, 0, -0.2F)) + .addChild("right_wing_outer", ModelPartBuilder.create().uv(24, 16).cuboid(0, 0, 0, 10, 12, 1), ModelTransform.of(-13, 10, 0.1F, 0, 0, -0.2F)); + + tree.addChild("left_wing", ModelPartBuilder.create().uv(42, 0).mirrored().cuboid(2, -5, 0, 10, 19, 1), ModelTransform.rotation(0, 0, 0.2F)) + .addChild("left_wing_outer", ModelPartBuilder.create().uv(24, 16).cuboid(0, 0, 0, 10, 12, 1), ModelTransform.of(2, 10, 0.1F, 0, 0, 0.2F)); + + return TexturedModelData.of(data, 64, 64); + } + + @Override + public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) { + body.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); + } + + @Override + public void setAngles(ButterflyEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float headYaw, float headPitch) { + + float flap = MathHelper.cos(ageInTicks) * (float)Math.PI / 4; + + if (entity.isResting()) { + body.pitch = 0.8F; + flap = MathHelper.cos((ageInTicks + (1 + entity.getId()) % 2) / 20) * (float)Math.PI / 6 + 0.7F; + } else { + body.pitch = ((float)Math.PI / 4) + MathHelper.cos(ageInTicks * 0.1F) * 0.15F; + } + + leftWing.yaw = -flap; + rightWing.yaw = flap; + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/ButterflyEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/ButterflyEntity.java new file mode 100644 index 00000000..ae9a077c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/ButterflyEntity.java @@ -0,0 +1,226 @@ +package com.minelittlepony.unicopia.entity; + +import java.util.Random; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityPose; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.SpawnReason; +import net.minecraft.entity.attribute.DefaultAttributeContainer; +import net.minecraft.entity.attribute.EntityAttributes; +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.AmbientEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.sound.SoundEvent; +import net.minecraft.sound.SoundEvents; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraft.world.WorldAccess; + +public class ButterflyEntity extends AmbientEntity { + private static final TrackedData RESTING = DataTracker.registerData(ButterflyEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private static final TrackedData VARIANT = DataTracker.registerData(ButterflyEntity.class, TrackedDataHandlerRegistry.INTEGER); + + private BlockPos hoveringPosition; + + public ButterflyEntity(EntityType type, World world) { + super(type, world); + setVariant(Variant.random(world.random)); + setResting(true); + } + + public static DefaultAttributeContainer.Builder createButterflyAttributes() { + return createMobAttributes().add(EntityAttributes.GENERIC_MAX_HEALTH, 2); + } + + @Override + public float getSoundPitch() { + return super.getSoundPitch() * 0.95F; + } + + @Nullable + @Override + protected SoundEvent getHurtSound(DamageSource damageSourceIn) { + return SoundEvents.ENTITY_BAT_HURT; + } + + @Nullable + @Override + protected SoundEvent getDeathSound() { + return null; + } + + @Override + protected void initDataTracker() { + super.initDataTracker(); + getDataTracker().startTracking(VARIANT, Variant.BUTTERFLY.ordinal()); + getDataTracker().startTracking(RESTING, false); + } + + @Override + public boolean isPushable() { + return false; + } + + @Override + public boolean collides() { + return true; + } + + @Override + public void tick() { + super.tick(); + + Vec3d vel = getVelocity(); + setVelocity(vel.x, vel.y * 0.6 + 0.02F, vel.z); + } + + public boolean isResting() { + return getDataTracker().get(RESTING); + } + + public void setResting(boolean resting) { + getDataTracker().set(RESTING, resting); + } + + public Variant getVariant() { + Variant[] values = Variant.values(); + return values[getDataTracker().get(VARIANT) % values.length]; + } + + public void setVariant(Variant variant) { + getDataTracker().set(VARIANT, variant.ordinal()); + } + + protected boolean isAggressor(Entity e) { + if (e instanceof ButterflyEntity) { + return false; + } + + if (e instanceof PlayerEntity) { + PlayerEntity player = (PlayerEntity)e; + + if (player.isCreative() || player.isSpectator()) { + return false; + } + + if (player.isSprinting() || player.handSwinging || player.forwardSpeed > 0 || player.sidewaysSpeed > 0) { + return true; + } + } else if (!EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.test(e)) { + return false; + } + + return e.getVelocity().x != 0 || e.getVelocity().z != 0; + } + + @Override + public void tickMovement() { + super.tickMovement(); + + BlockPos pos = getBlockPos(); + BlockPos below = pos.down(); + + if (isResting()) { + if (world.getBlockState(below).isOpaque()) { + if (!world.getOtherEntities(this, getBoundingBox().expand(7), this::isAggressor).isEmpty()) { + setResting(false); + } + } else { + setResting(false); + } + } else { + + // invalidate the hovering position + if (hoveringPosition != null && (!world.isAir(hoveringPosition) || hoveringPosition.getY() < 1)) { + hoveringPosition = null; + } + + // select a new hovering position + if (hoveringPosition == null || random.nextInt(30) == 0 || hoveringPosition.getSquaredDistance(pos) < 4) { + hoveringPosition = new BlockPos( + getX() + random.nextInt(7) - random.nextInt(7), + getY() + random.nextInt(6) - 2, + getZ() + random.nextInt(7) - random.nextInt(7) + ); + } + + // hover casually towards the chosen position + Vec3d motion = Vec3d.ofCenter(hoveringPosition, 0.1).subtract(getPos()); + Vec3d vel = getVelocity(); + + addVelocity( + (Math.signum(motion.getX()) * 0.5 - vel.x) * 0.1, + (Math.signum(motion.getY()) * 0.7 - vel.y) * 0.1, + (Math.signum(motion.getZ()) * 0.5 - vel.z) * 0.1 + ); + + float direction = (float)(MathHelper.atan2(vel.z, vel.x) * (180 / Math.PI)) - 90; + + forwardSpeed = 0.5F; + headYaw += MathHelper.wrapDegrees(direction - headYaw); + + if (random.nextInt(100) == 0 && world.getBlockState(below).isOpaque()) { + setResting(true); + } + } + } + + @Override + public boolean handleFallDamage(float distance, float damageMultiplier, DamageSource cause) { + return false; + } + + @Override + protected void fall(double y, boolean onGroundIn, BlockState state, BlockPos pos) { + } + + @Override + public boolean canSpawn(WorldAccess world, SpawnReason reason) { + return reason != SpawnReason.NATURAL || (getY() < world.getSeaLevel() && world.getLightLevel(getBlockPos()) > 7); + } + + @Override + public float getEyeHeight(EntityPose pose) { + return getHeight() / 2; + } + + public enum Variant { + BUTTERFLY, + YELLOW, + LIME, + RED, + GREEN, + BLUE, + PURPLE, + MAGENTA, + PINK, + HEDYLIDAE, + LYCAENIDAE, + NYMPHALIDAE, + MONARCH, + WHITE_MONARCH, + BRIMSTONE; + + private final Identifier skin = new Identifier("unicopia", "textures/entity/butterfly/" + name().toLowerCase() + ".png"); + + public Identifier getSkin() { + return skin; + } + + static Variant random(Random rand) { + Variant[] values = values(); + return values[rand.nextInt(values.length)]; + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index 0dfad5ee..3e36436b 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.item; import java.util.ArrayList; import java.util.List; +import com.minelittlepony.unicopia.UEntities; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.item.toxin.UFoodComponents; @@ -15,6 +16,7 @@ import net.fabricmc.fabric.api.item.v1.FabricItemSettings; import net.minecraft.item.BlockItem; import net.minecraft.item.FoodComponents; import net.minecraft.item.MusicDiscItem; +import net.minecraft.item.SpawnEggItem; import net.minecraft.sound.SoundEvent; import net.minecraft.util.Identifier; import net.minecraft.util.Rarity; @@ -72,6 +74,8 @@ public interface UItems { Item GOLDEN_FEATHER = register("golden_feather", new Item(new Item.Settings().rarity(Rarity.UNCOMMON).group(ItemGroup.MATERIALS))); Item GOLDEN_WING = register("golden_wing", new Item(new Item.Settings().rarity(Rarity.UNCOMMON).group(ItemGroup.MATERIALS))); + Item BUTTERFLY_SPAWN_EGG = register("butterfly_spawn_egg", new SpawnEggItem(UEntities.BUTTERFLY, 0x222200, 0xaaeeff, new Item.Settings().group(ItemGroup.MISC))); + AmuletItem PEGASUS_AMULET = register("pegasus_amulet", new AmuletItem(new FabricItemSettings() .maxCount(1) .maxDamage(890) diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 080de638..74ef7e84 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -66,6 +66,7 @@ "item.unicopia.music_disc_funk": "Music Disc", "item.unicopia.music_disc_funk.desc": "funk, just funk", + "entity.unicopia.butterfly": "Butterfly", "entity.unicopia.cast_spell": "Cast Spell", "entity.unicopia.cast_spell.by": "a spell cast by %s", diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/blue.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/blue.png new file mode 100644 index 00000000..05f741bd Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/blue.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/brimstone.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/brimstone.png new file mode 100644 index 00000000..b19268ad Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/brimstone.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/butterfly.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/butterfly.png new file mode 100644 index 00000000..7dcebd91 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/butterfly.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/green.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/green.png new file mode 100644 index 00000000..e68882b9 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/green.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/hedylidae.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/hedylidae.png new file mode 100644 index 00000000..c53c52c8 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/hedylidae.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/lime.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/lime.png new file mode 100644 index 00000000..29105c0f Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/lime.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/lycaenidae.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/lycaenidae.png new file mode 100644 index 00000000..05bbb7fd Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/lycaenidae.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/magenta.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/magenta.png new file mode 100644 index 00000000..d0be31d9 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/magenta.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/monarch.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/monarch.png new file mode 100644 index 00000000..1cec4597 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/monarch.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/nymphalidae.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/nymphalidae.png new file mode 100644 index 00000000..11c113ca Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/nymphalidae.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/pink.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/pink.png new file mode 100644 index 00000000..d5f080ad Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/pink.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/purple.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/purple.png new file mode 100644 index 00000000..ac8c7e1b Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/purple.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/red.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/red.png new file mode 100644 index 00000000..380ad7fc Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/red.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/white_monarch.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/white_monarch.png new file mode 100644 index 00000000..bed08d52 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/white_monarch.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/butterfly/yellow.png b/src/main/resources/assets/unicopia/textures/entity/butterfly/yellow.png new file mode 100644 index 00000000..2c991be9 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/butterfly/yellow.png differ