diff --git a/src/main/generated/assets/galacticraft/lang/en_us.json b/src/main/generated/assets/galacticraft/lang/en_us.json index 949e23913..b52b487b9 100644 --- a/src/main/generated/assets/galacticraft/lang/en_us.json +++ b/src/main/generated/assets/galacticraft/lang/en_us.json @@ -492,6 +492,7 @@ "entity.galacticraft.arch_grey": "Arch Grey", "entity.galacticraft.bubble": "Bubble", "entity.galacticraft.buggy": "Buggy", + "entity.galacticraft.cheese_slime": "Cheese Slime", "entity.galacticraft.comet_cube": "Comet Cube", "entity.galacticraft.evolved_creeper": "Evolved Creeper", "entity.galacticraft.evolved_enderman": "Evolved Enderman", @@ -568,6 +569,7 @@ "item.galacticraft.carbon_fragments": "Carbon Fragments", "item.galacticraft.cargo_rocket_schematic": "Cargo Rocket Schematic", "item.galacticraft.cheese_cracker": "Cracker with Moon Cheese", + "item.galacticraft.cheese_slime_spawn_egg": "Cheese Slime Spawn Egg", "item.galacticraft.cheeseburger": "Cheeseburger", "item.galacticraft.comet_cube_spawn_egg": "Comet Cube Spawn Egg", "item.galacticraft.compressed_aluminum": "Compressed Aluminum", diff --git a/src/main/generated/assets/galacticraft/models/item/cheese_slime_spawn_egg.json b/src/main/generated/assets/galacticraft/models/item/cheese_slime_spawn_egg.json new file mode 100644 index 000000000..d1aaa9d6e --- /dev/null +++ b/src/main/generated/assets/galacticraft/models/item/cheese_slime_spawn_egg.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:item/template_spawn_egg" +} \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/loot_table/entities/cheese_slime.json b/src/main/generated/data/galacticraft/loot_table/entities/cheese_slime.json new file mode 100644 index 000000000..5cb1e24b6 --- /dev/null +++ b/src/main/generated/data/galacticraft/loot_table/entities/cheese_slime.json @@ -0,0 +1,36 @@ +{ + "type": "minecraft:entity", + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + }, + { + "count": { + "type": "minecraft:uniform", + "max": 1.0, + "min": 0.0 + }, + "enchantment": "minecraft:looting", + "function": "minecraft:enchanted_count_increase" + } + ], + "name": "galacticraft:moon_cheese_curd" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "galacticraft:entities/cheese_slime" +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/Constant.java b/src/main/java/dev/galacticraft/mod/Constant.java index 45e468ad1..2f97ba46b 100644 --- a/src/main/java/dev/galacticraft/mod/Constant.java +++ b/src/main/java/dev/galacticraft/mod/Constant.java @@ -935,6 +935,7 @@ interface Entity { String GAZER = "gazer"; String FALLING_METEOR = "falling_meteor"; String EVOLVED_SKELETON_BOSS = "evolved_skeleton_boss"; + String CHEESE_SLIME = "cheese_slime"; } interface SpawnEgg { @@ -954,6 +955,7 @@ interface SpawnEgg { String OLI_GRUB = "oli_grub_spawn_egg"; String COMET_CUBE = "comet_cube_spawn_egg"; String GAZER = "gazer_spawn_egg"; + String CHEESE_SLIME = "cheese_slime_spawn_egg"; } interface EntityTexture { @@ -968,6 +970,7 @@ interface EntityTexture { String GAZER = "textures/entity/gazer.png"; String LANDER = "textures/entity/lander.png"; String SKELETON_BOSS = "textures/entity/skeletonboss.png"; + String CHEESE_SLIME = "textures/entity/cheese_slime.png"; } interface GearTexture { diff --git a/src/main/java/dev/galacticraft/mod/GalacticraftClient.java b/src/main/java/dev/galacticraft/mod/GalacticraftClient.java index fe4c7638a..4ed140dcf 100644 --- a/src/main/java/dev/galacticraft/mod/GalacticraftClient.java +++ b/src/main/java/dev/galacticraft/mod/GalacticraftClient.java @@ -153,6 +153,7 @@ public void onInitializeClient() { EntityRendererRegistry.register(GCEntityTypes.PARACHEST, ParachestRenderer::new); EntityRendererRegistry.register(GCEntityTypes.THROWABLE_METEOR_CHUNK, ThrownItemRenderer::new); EntityRendererRegistry.register(GCEntityTypes.SKELETON_BOSS, EvolvedSkeletonBossRenderer::new); + EntityRendererRegistry.register(GCEntityTypes.CHEESE_SLIME, CheeseSlimeEntityRenderer::new); GCBlockEntityRenderer.register(); GCClientPacketReceiver.register(); diff --git a/src/main/java/dev/galacticraft/mod/client/model/entity/CheeseSlimeEntityModel.java b/src/main/java/dev/galacticraft/mod/client/model/entity/CheeseSlimeEntityModel.java new file mode 100644 index 000000000..354c9cc26 --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/client/model/entity/CheeseSlimeEntityModel.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.galacticraft.mod.client.model.entity; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.HierarchicalModel; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.world.entity.Entity; + +public class CheeseSlimeEntityModel extends HierarchicalModel { + private final ModelPart root; + + public CheeseSlimeEntityModel(ModelPart root) { + this.root = root; + } + + public static LayerDefinition createOuterBodyLayer() { + MeshDefinition meshDefinition = new MeshDefinition(); + PartDefinition partDefinition = meshDefinition.getRoot(); + partDefinition.addOrReplaceChild("cube", CubeListBuilder.create().texOffs(0, 0).addBox(-4.0F, 16.0F, -4.0F, 8.0F, 8.0F, 8.0F), PartPose.ZERO); + return LayerDefinition.create(meshDefinition, 64, 32); + } + + public static LayerDefinition createInnerBodyLayer() { + MeshDefinition meshDefinition = new MeshDefinition(); + PartDefinition partDefinition = meshDefinition.getRoot(); + partDefinition.addOrReplaceChild("cube", CubeListBuilder.create().texOffs(0, 16).addBox(-3.0F, 17.0F, -3.0F, 6.0F, 6.0F, 6.0F), PartPose.ZERO); + partDefinition.addOrReplaceChild("right_eye", CubeListBuilder.create().texOffs(32, 0).addBox(-3.25F, 18.0F, -3.5F, 2.0F, 2.0F, 2.0F), PartPose.ZERO); + partDefinition.addOrReplaceChild("left_eye", CubeListBuilder.create().texOffs(32, 4).addBox(1.25F, 18.0F, -3.5F, 2.0F, 2.0F, 2.0F), PartPose.ZERO); + partDefinition.addOrReplaceChild("mouth", CubeListBuilder.create().texOffs(32, 8).addBox(0.0F, 21.0F, -3.5F, 1.0F, 1.0F, 1.0F), PartPose.ZERO); + return LayerDefinition.create(meshDefinition, 64, 32); + } + + @Override + public void setupAnim(T entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public ModelPart root() { + return this.root; + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertices, int light, int overlay, int color) { + this.root.render(poseStack, vertices, light, overlay); + } +} diff --git a/src/main/java/dev/galacticraft/mod/client/render/entity/CheeseSlimeEntityRenderer.java b/src/main/java/dev/galacticraft/mod/client/render/entity/CheeseSlimeEntityRenderer.java new file mode 100644 index 000000000..99b85a043 --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/client/render/entity/CheeseSlimeEntityRenderer.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.galacticraft.mod.client.render.entity; + +import com.mojang.blaze3d.vertex.PoseStack; +import dev.galacticraft.mod.Constant; +import dev.galacticraft.mod.client.model.entity.CheeseSlimeEntityModel; +import dev.galacticraft.mod.client.render.entity.feature.CheeseSlimeOuterLayer; +import dev.galacticraft.mod.client.render.entity.model.GCEntityModelLayer; +import dev.galacticraft.mod.content.entity.CheeseSlimeEntity; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.entity.MobRenderer; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; + +public class CheeseSlimeEntityRenderer extends MobRenderer>{ + private static final ResourceLocation TEXTURE = Constant.id(Constant.EntityTexture.CHEESE_SLIME); + + public CheeseSlimeEntityRenderer(EntityRendererProvider.Context context) { + super(context, new CheeseSlimeEntityModel<>(context.bakeLayer(GCEntityModelLayer.CHEESE_SLIME)), 0.25f); + this.addLayer(new CheeseSlimeOuterLayer<>(this, context.getModelSet())); + } + + @Override + public void render(CheeseSlimeEntity cheeseSlime, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i) { + this.shadowRadius = 0.25F * cheeseSlime.getSize(); + super.render(cheeseSlime, f, g, poseStack, multiBufferSource, i); + } + + @Override + protected void scale(CheeseSlimeEntity cheeseSlime, PoseStack poseStack, float f) { + poseStack.scale(0.999F, 0.999F, 0.999F); + poseStack.translate(0.0F, 0.001F, 0.0F); + float h = cheeseSlime.getSize(); + float i = Mth.lerp(f, cheeseSlime.oSquish, cheeseSlime.squish) / (h * 0.5F + 1.0F); + float j = 1.0F / (i + 1.0F); + poseStack.scale(j * h, 1.0F / j * h, j * h); + } + + @Override + public ResourceLocation getTextureLocation(CheeseSlimeEntity entity) { + return TEXTURE; + } +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/client/render/entity/feature/CheeseSlimeOuterLayer.java b/src/main/java/dev/galacticraft/mod/client/render/entity/feature/CheeseSlimeOuterLayer.java new file mode 100644 index 000000000..07de2103c --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/client/render/entity/feature/CheeseSlimeOuterLayer.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.galacticraft.mod.client.render.entity.feature; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import dev.galacticraft.mod.client.model.entity.CheeseSlimeEntityModel; +import net.minecraft.client.Minecraft; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.SlimeModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.LivingEntityRenderer; +import net.minecraft.client.renderer.entity.RenderLayerParent; +import net.minecraft.client.renderer.entity.layers.RenderLayer; +import net.minecraft.world.entity.LivingEntity; + +public class CheeseSlimeOuterLayer extends RenderLayer> { + private final EntityModel model; + + public CheeseSlimeOuterLayer(RenderLayerParent> parent, EntityModelSet set) { + super(parent); + this.model = new SlimeModel<>(set.bakeLayer(ModelLayers.SLIME_OUTER)); + } + + public void render(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, T livingEntity, float f, float g, float h, float j, float k, float l) { + Minecraft minecraft = Minecraft.getInstance(); + boolean bl = minecraft.shouldEntityAppearGlowing(livingEntity) && livingEntity.isInvisible(); + if (!livingEntity.isInvisible() || bl) { + VertexConsumer vertexConsumer; + if (bl) { + vertexConsumer = multiBufferSource.getBuffer(RenderType.outline(this.getTextureLocation(livingEntity))); + } else { + vertexConsumer = multiBufferSource.getBuffer(RenderType.entityTranslucent(this.getTextureLocation(livingEntity))); + } + + this.getParentModel().copyPropertiesTo(this.model); + this.model.prepareMobModel(livingEntity, f, g, h); + this.model.setupAnim(livingEntity, f, g, j, k, l); + this.model.renderToBuffer(poseStack, vertexConsumer, i, LivingEntityRenderer.getOverlayCoords(livingEntity, 0.0F)); + } + } +} diff --git a/src/main/java/dev/galacticraft/mod/client/render/entity/model/GCEntityModelLayer.java b/src/main/java/dev/galacticraft/mod/client/render/entity/model/GCEntityModelLayer.java index 62175f73a..52fb5b6fe 100644 --- a/src/main/java/dev/galacticraft/mod/client/render/entity/model/GCEntityModelLayer.java +++ b/src/main/java/dev/galacticraft/mod/client/render/entity/model/GCEntityModelLayer.java @@ -36,6 +36,8 @@ public class GCEntityModelLayer { public static final ModelLayerLocation RUMBLER = registerModelLayer("rumbler"); public static final ModelLayerLocation COMET_CUBE = registerModelLayer("comet_cube"); public static final ModelLayerLocation OLI_GRUB = registerModelLayer("oli_grub"); + public static final ModelLayerLocation CHEESE_SLIME = registerModelLayer("cheese_slime"); + public static final ModelLayerLocation CHEESE_SLIME_OUTER = registerModelLayer("cheese_slime", "outer"); public static final ModelLayerLocation GREY = registerModelLayer("grey"); public static final ModelLayerLocation ARCH_GREY = registerModelLayer("arch_grey"); public static final ModelLayerLocation LANDER = registerModelLayer("lander"); @@ -50,7 +52,11 @@ public class GCEntityModelLayer { public static final ModelLayerLocation ROCKET_WORKBENCH = registerModelLayer("rocket_workbench"); private static ModelLayerLocation registerModelLayer(String id) { - return new ModelLayerLocation(Constant.id(id), DEFAULT_LAYER); + return registerModelLayer(id, DEFAULT_LAYER); + } + + private static ModelLayerLocation registerModelLayer(String id, String layer) { + return new ModelLayerLocation(Constant.id(id), layer); } public static void register() { @@ -58,6 +64,8 @@ public static void register() { EntityModelLayerRegistry.registerModelLayer(RUMBLER, RumblerEntityModel::createBodyLayer); EntityModelLayerRegistry.registerModelLayer(COMET_CUBE, CometCubeEntityModel::createBodyLayer); EntityModelLayerRegistry.registerModelLayer(OLI_GRUB, OliGrubEntityModel::createBodyLayer); + EntityModelLayerRegistry.registerModelLayer(CHEESE_SLIME, CheeseSlimeEntityModel::createInnerBodyLayer); + EntityModelLayerRegistry.registerModelLayer(CHEESE_SLIME_OUTER, CheeseSlimeEntityModel::createOuterBodyLayer); EntityModelLayerRegistry.registerModelLayer(GREY, GreyEntityModel::createBodyLayer); EntityModelLayerRegistry.registerModelLayer(ARCH_GREY, ArchGreyEntityModel::createBodyLayer); EntityModelLayerRegistry.registerModelLayer(LANDER, LanderModel::createBodyLayer); diff --git a/src/main/java/dev/galacticraft/mod/content/GCEntityTypes.java b/src/main/java/dev/galacticraft/mod/content/GCEntityTypes.java index 0c4962a8e..e9ad25863 100644 --- a/src/main/java/dev/galacticraft/mod/content/GCEntityTypes.java +++ b/src/main/java/dev/galacticraft/mod/content/GCEntityTypes.java @@ -115,6 +115,10 @@ public class GCEntityTypes { .sized(0.75F, 0.375F) .clientTrackingRange(8) .build()); + public static final EntityType CHEESE_SLIME = ENTITIES.register(Entity.CHEESE_SLIME, EntityType.Builder.of(CheeseSlimeEntity::new, MobCategory.MONSTER) + .sized(0.75F, 0.375F) + .clientTrackingRange(8) + .build()); public static final EntityType GREY = ENTITIES.register(Entity.GREY, EntityType.Builder.of(GreyEntity::new, MobCategory.CREATURE) .sized(0.6F, 1.55F) .eyeHeight(1.25F) @@ -196,5 +200,6 @@ public static void register() { FabricDefaultAttributeRegistry.register(GREY, GreyEntity.createAttributes()); FabricDefaultAttributeRegistry.register(ARCH_GREY, ArchGreyEntity.createAttributes()); FabricDefaultAttributeRegistry.register(SKELETON_BOSS, SkeletonBoss.createAttributes().add(GcApiEntityAttributes.CAN_BREATHE_IN_SPACE, 1.0D)); + FabricDefaultAttributeRegistry.register(CHEESE_SLIME, CheeseSlimeEntity.createAttributes()); } } diff --git a/src/main/java/dev/galacticraft/mod/content/entity/CheeseSlimeEntity.java b/src/main/java/dev/galacticraft/mod/content/entity/CheeseSlimeEntity.java new file mode 100644 index 000000000..ea9357041 --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/content/entity/CheeseSlimeEntity.java @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2019-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.galacticraft.mod.content.entity; + +import com.google.common.annotations.VisibleForTesting; +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleTypes; +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.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.tags.BiomeTags; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.Difficulty; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.effect.MobEffects; +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.ai.control.MoveControl; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; +import net.minecraft.world.entity.animal.IronGolem; +import net.minecraft.world.entity.monster.Enemy; +import net.minecraft.world.entity.monster.Monster; +import net.minecraft.world.entity.monster.Slime; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.level.*; +import net.minecraft.world.level.levelgen.WorldgenRandom; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumSet; + +public class CheeseSlimeEntity extends Mob implements Enemy { + private static final EntityDataAccessor ID_SIZE = SynchedEntityData.defineId(CheeseSlimeEntity.class, EntityDataSerializers.INT); + public static final int MIN_SIZE = 1; + public static final int MAX_SIZE = 127; + public static final int MAX_NATURAL_SIZE = 4; + public float targetSquish; + public float squish; + public float oSquish; + private boolean wasOnGround; + + public CheeseSlimeEntity(EntityType entityType, Level level) { + super(entityType, level); + fixupDimensions(); + moveControl = new CheeseSlimeEntity.CheeseSlimeMoveControl(this); + } + + @Override + protected void registerGoals() { + goalSelector.addGoal(1, new CheeseSlimeFloatGoal(this)); + goalSelector.addGoal(2, new CheeseSlimeAttackGoal(this)); + goalSelector.addGoal(3, new CheeseSlimeRandomDirectionGoal(this)); + goalSelector.addGoal(5, new CheeseSlimeKeepOnJumpingGoal(this)); + targetSelector + .addGoal(1, new NearestAttackableTargetGoal(this, Player.class, 10, true, false, + livingEntity -> Math.abs(((LivingEntity)livingEntity).getY() - getY()) <= 4.0)); + targetSelector.addGoal(3, new NearestAttackableTargetGoal(this, IronGolem.class, true)); + } + + @Override + public SoundSource getSoundSource() { + return SoundSource.HOSTILE; + } + + @Override + protected void defineSynchedData(SynchedEntityData.Builder compositeStateBuilder) { + super.defineSynchedData(compositeStateBuilder); + compositeStateBuilder.define(ID_SIZE, 1); + } + + @VisibleForTesting + public void setSize(int size, boolean heal) { + int i = Mth.clamp(size, 1, 127); + entityData.set(ID_SIZE, i); + reapplyPosition(); + refreshDimensions(); + getAttribute(Attributes.MAX_HEALTH).setBaseValue(i * i); + getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.2F + 0.1F * i); + getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(i); + if (heal) { + setHealth(getMaxHealth()); + } + + xpReward = i; + } + + public int getSize() { + return entityData.get(ID_SIZE); + } + + @Override + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); + compound.putInt("Size", this.getSize() - 1); + compound.putBoolean("wasOnGround", this.wasOnGround); + } + + @Override + public void readAdditionalSaveData(CompoundTag compound) { + setSize(compound.getInt("Size") + 1, false); + super.readAdditionalSaveData(compound); + wasOnGround = compound.getBoolean("wasOnGround"); + } + + public boolean isTiny() { + return getSize() <= 1; + } + + protected ParticleOptions getParticleType() { + return ParticleTypes.ITEM_SLIME; + } + + @Override + protected boolean shouldDespawnInPeaceful() { + return getSize() > 0; + } + + @Override + public void tick() { + squish = squish + (targetSquish - squish) * 0.5F; + oSquish = squish; + super.tick(); + if (onGround() && !wasOnGround) { + float f = getDimensions(getPose()).width() * 2.0F; + float g = f / 2.0F; + + for (int i = 0; i < f * 16.0F; i++) { + float h = random.nextFloat() * (float) (Math.PI * 2); + float j = random.nextFloat() * 0.5F + 0.5F; + float k = Mth.sin(h) * g * j; + float l = Mth.cos(h) * g * j; + level().addParticle(getParticleType(), getX() + k, getY(), getZ() + l, 0.0, 0.0, 0.0); + } + + playSound(getSquishSound(), getSoundVolume(), ((random.nextFloat() - random.nextFloat()) * 0.2F + 1.0F) / 0.8F); + targetSquish = -0.5F; + } else if (!onGround() && wasOnGround) { + targetSquish = 1.0F; + } + + wasOnGround = onGround(); + decreaseSquish(); + } + + protected void decreaseSquish() { + targetSquish *= 0.6F; + } + + protected int getJumpDelay() { + return random.nextInt(20) + 10; + } + + @Override + public void refreshDimensions() { + double d = getX(); + double e = getY(); + double f = getZ(); + super.refreshDimensions(); + setPos(d, e, f); + } + + @Override + public void onSyncedDataUpdated(EntityDataAccessor data) { + if (ID_SIZE.equals(data)) { + refreshDimensions(); + setYRot(yHeadRot); + yBodyRot = yHeadRot; + if (isInWater() && random.nextInt(20) == 0) { + doWaterSplashEffect(); + } + } + + super.onSyncedDataUpdated(data); + } + + @Override + public EntityType getType() { + return (EntityType)super.getType(); + } + + @Override + public void remove(Entity.RemovalReason reason) { + int i = getSize(); + if (!level().isClientSide && i > 1 && isDeadOrDying()) { + Component component = getCustomName(); + boolean bl = isNoAi(); + float f = getDimensions(getPose()).width(); + float g = f / 2.0F; + int j = i / 2; + int k = 2 + random.nextInt(3); + + for (int l = 0; l < k; l++) { + float h = (l % 2 - 0.5F) * g; + float m = (l / 2 - 0.5F) * g; + CheeseSlimeEntity cheeseSlime = getType().create(level()); + if (cheeseSlime != null) { + if (isPersistenceRequired()) { + cheeseSlime.setPersistenceRequired(); + } + + cheeseSlime.setCustomName(component); + cheeseSlime.setNoAi(bl); + cheeseSlime.setInvulnerable(isInvulnerable()); + cheeseSlime.setSize(j, true); + cheeseSlime.moveTo(getX() + h, getY() + 0.5, getZ() + m, random.nextFloat() * 360.0F, 0.0F); + level().addFreshEntity(cheeseSlime); + } + } + } + + super.remove(reason); + } + + @Override + public void push(Entity entityGoalInfo) { + super.push(entityGoalInfo); + if (entityGoalInfo instanceof IronGolem && isDealsDamage()) { + dealDamage((LivingEntity)entityGoalInfo); + } + } + + @Override + public void playerTouch(Player player) { + if (isDealsDamage()) { + dealDamage(player); + } + } + + protected void dealDamage(LivingEntity target) { + if (isAlive() && isWithinMeleeAttackRange(target) && hasLineOfSight(target)) { + DamageSource damageSource = damageSources().mobAttack(this); + if (target.hurt(damageSource, getAttackDamage())) { + playSound(SoundEvents.SLIME_ATTACK, 1.0F, (random.nextFloat() - random.nextFloat()) * 0.2F + 1.0F); + if (level() instanceof ServerLevel serverLevel) { + EnchantmentHelper.doPostAttackEffects(serverLevel, target, damageSource); + } + } + } + } + + @Override + protected Vec3 getPassengerAttachmentPoint(Entity passenger, EntityDimensions dimensions, float scaleFactor) { + return new Vec3(0.0, dimensions.height() - 0.015625 * getSize() * scaleFactor, 0.0); + } + + protected boolean isDealsDamage() { + return !isTiny() && isEffectiveAi(); + } + + protected float getAttackDamage() { + return (float)getAttributeValue(Attributes.ATTACK_DAMAGE); + } + + @Override + protected SoundEvent getHurtSound(DamageSource source) { + return isTiny() ? SoundEvents.SLIME_HURT_SMALL : SoundEvents.SLIME_HURT; + } + + @Override + protected SoundEvent getDeathSound() { + return isTiny() ? SoundEvents.SLIME_DEATH_SMALL : SoundEvents.SLIME_DEATH; + } + + protected SoundEvent getSquishSound() { + return isTiny() ? SoundEvents.SLIME_SQUISH_SMALL : SoundEvents.SLIME_SQUISH; + } + + public static boolean checkSlimeSpawnRules(EntityType type, LevelAccessor level, MobSpawnType mobSpawnType, BlockPos pos, RandomSource randomSource) { + if (MobSpawnType.isSpawner(mobSpawnType)) { + return checkMobSpawnRules(type, level, mobSpawnType, pos, randomSource); + } else { + if (level.getDifficulty() != Difficulty.PEACEFUL) { + if (mobSpawnType == MobSpawnType.SPAWNER) { + return checkMobSpawnRules(type, level, mobSpawnType, pos, randomSource); + } + + if (level.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) + && pos.getY() > 50 + && pos.getY() < 70 + && randomSource.nextFloat() < 0.5F + && randomSource.nextFloat() < level.getMoonBrightness() + && level.getMaxLocalRawBrightness(pos) <= randomSource.nextInt(8)) { + return checkMobSpawnRules(type, level, mobSpawnType, pos, randomSource); + } + + if (!(level instanceof WorldGenLevel)) { + return false; + } + + ChunkPos chunkPos = new ChunkPos(pos); + boolean bl = WorldgenRandom.seedSlimeChunk(chunkPos.x, chunkPos.z, ((WorldGenLevel)level).getSeed(), 987234911L).nextInt(10) == 0; + if (randomSource.nextInt(10) == 0 && bl && pos.getY() < 40) { + return checkMobSpawnRules(type, level, mobSpawnType, pos, randomSource); + } + } + + return false; + } + } + + @Override + protected float getSoundVolume() { + return 0.4F * getSize(); + } + + @Override + public int getMaxHeadXRot() { + return 0; + } + + protected boolean doPlayJumpSound() { + return getSize() > 0; + } + + @Override + public void jumpFromGround() { + Vec3 vec3 = getDeltaMovement(); + setDeltaMovement(vec3.x, getJumpPower(), vec3.z); + hasImpulse = true; + } + + @Nullable + @Override + public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance instance, MobSpawnType mobSpawnType, @Nullable SpawnGroupData spawnGroupData) { + RandomSource randomSource = level.getRandom(); + int i = randomSource.nextInt(3); + if (i < 2 && randomSource.nextFloat() < 0.5F * instance.getSpecialMultiplier()) { + i++; + } + + int j = 1 << i; + setSize(j, true); + return super.finalizeSpawn(level, instance, mobSpawnType, spawnGroupData); + } + + float getSoundPitch() { + float f = isTiny() ? 1.4F : 0.8F; + return ((random.nextFloat() - random.nextFloat()) * 0.2F + 1.0F) * f; + } + + protected SoundEvent getJumpSound() { + return isTiny() ? SoundEvents.SLIME_JUMP_SMALL : SoundEvents.SLIME_JUMP; + } + + @Override + public EntityDimensions getDefaultDimensions(Pose pose) { + return super.getDefaultDimensions(pose).scale(getSize()); + } + + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes(); + } + + static class CheeseSlimeAttackGoal extends Goal { + private final CheeseSlimeEntity cheeseSlime; + private int growTiredTimer; + + public CheeseSlimeAttackGoal(CheeseSlimeEntity cheeseSlime) { + this.cheeseSlime = cheeseSlime; + setFlags(EnumSet.of(Goal.Flag.LOOK)); + } + + @Override + public boolean canUse() { + LivingEntity livingEntity = cheeseSlime.getTarget(); + if (livingEntity == null) { + return false; + } else { + return cheeseSlime.canAttack(livingEntity) && cheeseSlime.getMoveControl() instanceof CheeseSlimeMoveControl; + } + } + + @Override + public void start() { + growTiredTimer = reducedTickDelay(300); + super.start(); + } + + @Override + public boolean canContinueToUse() { + LivingEntity livingEntity = cheeseSlime.getTarget(); + if (livingEntity == null) { + return false; + } else { + return cheeseSlime.canAttack(livingEntity) && --growTiredTimer > 0; + } + } + + @Override + public boolean requiresUpdateEveryTick() { + return true; + } + + @Override + public void tick() { + LivingEntity livingEntity = cheeseSlime.getTarget(); + if (livingEntity != null) { + cheeseSlime.lookAt(livingEntity, 10.0F, 10.0F); + } + + if (cheeseSlime.getMoveControl() instanceof CheeseSlimeEntity.CheeseSlimeMoveControl slimeMoveControl) { + slimeMoveControl.setDirection(cheeseSlime.getYRot(), cheeseSlime.isDealsDamage()); + } + } + } + + static class CheeseSlimeFloatGoal extends Goal { + private final CheeseSlimeEntity cheeseSlime; + + public CheeseSlimeFloatGoal(CheeseSlimeEntity cheeseSlime) { + this.cheeseSlime = cheeseSlime; + setFlags(EnumSet.of(Goal.Flag.JUMP, Goal.Flag.MOVE)); + cheeseSlime.getNavigation().setCanFloat(true); + } + + @Override + public boolean canUse() { + return (cheeseSlime.isInWater() || cheeseSlime.isInLava()) && cheeseSlime.getMoveControl() instanceof CheeseSlimeEntity.CheeseSlimeMoveControl; + } + + @Override + public boolean requiresUpdateEveryTick() { + return true; + } + + @Override + public void tick() { + if (cheeseSlime.getRandom().nextFloat() < 0.8F) { + cheeseSlime.getJumpControl().jump(); + } + + if (cheeseSlime.getMoveControl() instanceof CheeseSlimeEntity.CheeseSlimeMoveControl slimeMoveControl) { + slimeMoveControl.setWantedMovement(1.2); + } + } + } + + static class CheeseSlimeKeepOnJumpingGoal extends Goal { + private final CheeseSlimeEntity cheeseSlime; + + public CheeseSlimeKeepOnJumpingGoal(CheeseSlimeEntity cheeseSlime) { + this.cheeseSlime = cheeseSlime; + setFlags(EnumSet.of(Goal.Flag.JUMP, Goal.Flag.MOVE)); + } + + @Override + public boolean canUse() { + return !this.cheeseSlime.isPassenger(); + } + + @Override + public void tick() { + if (cheeseSlime.getMoveControl() instanceof CheeseSlimeEntity.CheeseSlimeMoveControl slimeMoveControl) { + slimeMoveControl.setWantedMovement(1.0); + } + } + } + + static class CheeseSlimeMoveControl extends MoveControl { + private float yRot; + private int jumpDelay; + private final CheeseSlimeEntity cheeseSlime; + private boolean isAggressive; + + public CheeseSlimeMoveControl(CheeseSlimeEntity cheeseSlime) { + super(cheeseSlime); + this.cheeseSlime = cheeseSlime; + yRot = 180.0F * cheeseSlime.getYRot() / (float) Math.PI; + } + + public void setDirection(float targetYaw, boolean jumpOften) { + yRot = targetYaw; + isAggressive = jumpOften; + } + + public void setWantedMovement(double speed) { + speedModifier = speed; + operation = MoveControl.Operation.MOVE_TO; + } + + @Override + public void tick() { + mob.setYRot(rotlerp(mob.getYRot(), yRot, 90.0F)); + mob.yHeadRot = mob.getYRot(); + mob.yBodyRot = mob.getYRot(); + if (operation != MoveControl.Operation.MOVE_TO) { + mob.setZza(0.0F); + } else { + operation = MoveControl.Operation.WAIT; + if (mob.onGround()) { + mob.setSpeed((float)(speedModifier * mob.getAttributeValue(Attributes.MOVEMENT_SPEED))); + if (jumpDelay-- <= 0) { + jumpDelay = cheeseSlime.getJumpDelay(); + if (isAggressive) { + jumpDelay /= 3; + } + + cheeseSlime.getJumpControl().jump(); + if (cheeseSlime.doPlayJumpSound()) { + cheeseSlime.playSound(cheeseSlime.getJumpSound(), cheeseSlime.getSoundVolume(), cheeseSlime.getSoundPitch()); + } + } else { + cheeseSlime.xxa = 0.0F; + cheeseSlime.zza = 0.0F; + mob.setSpeed(0.0F); + } + } else { + mob.setSpeed((float)(speedModifier * mob.getAttributeValue(Attributes.MOVEMENT_SPEED))); + } + } + } + } + + static class CheeseSlimeRandomDirectionGoal extends Goal { + private final CheeseSlimeEntity cheeseSlime; + private float chosenDegrees; + private int nextRandomizeTime; + + public CheeseSlimeRandomDirectionGoal(CheeseSlimeEntity cheeseSlime) { + this.cheeseSlime = cheeseSlime; + setFlags(EnumSet.of(Goal.Flag.LOOK)); + } + + @Override + public boolean canUse() { + return cheeseSlime.getTarget() == null + && (cheeseSlime.onGround() || cheeseSlime.isInWater() || cheeseSlime.isInLava() || cheeseSlime.hasEffect(MobEffects.LEVITATION)) + && cheeseSlime.getMoveControl() instanceof CheeseSlimeEntity.CheeseSlimeMoveControl; + } + + @Override + public void tick() { + if (--nextRandomizeTime <= 0) { + nextRandomizeTime = adjustedTickDelay(40 + cheeseSlime.getRandom().nextInt(60)); + chosenDegrees = cheeseSlime.getRandom().nextInt(360); + } + + if (cheeseSlime.getMoveControl() instanceof CheeseSlimeEntity.CheeseSlimeMoveControl slimeMoveControl) { + slimeMoveControl.setDirection(chosenDegrees, false); + } + } + } +} diff --git a/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java b/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java index 83a21bccd..8599900f7 100644 --- a/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java +++ b/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java @@ -524,6 +524,7 @@ public static void registerSpawnEggs() { content.addAfter(ItemStack.EMPTY, OLI_GRUB_SPAWN_EGG); content.addAfter(ItemStack.EMPTY, GREY_SPAWN_EGG); content.addAfter(ItemStack.EMPTY, ARCH_GREY_SPAWN_EGG); + content.addAfter(ItemStack.EMPTY, CHEESE_SLIME_SPAWN_EGG); }); } diff --git a/src/main/java/dev/galacticraft/mod/content/item/GCItems.java b/src/main/java/dev/galacticraft/mod/content/item/GCItems.java index 5439079cb..79d2d2dc0 100644 --- a/src/main/java/dev/galacticraft/mod/content/item/GCItems.java +++ b/src/main/java/dev/galacticraft/mod/content/item/GCItems.java @@ -301,6 +301,7 @@ public class GCItems { public static final Item OLI_GRUB_SPAWN_EGG = ITEMS.register(Constant.SpawnEgg.OLI_GRUB, new SpawnEggItem(GCEntityTypes.OLI_GRUB, 0xd4dd7e, 0xa4bf63, new Item.Properties())); public static final Item GREY_SPAWN_EGG = ITEMS.register(Constant.SpawnEgg.GREY, new SpawnEggItem(GCEntityTypes.GREY, 0x656463, 0x769e41, new Item.Properties())); public static final Item ARCH_GREY_SPAWN_EGG = ITEMS.register(Constant.SpawnEgg.ARCH_GREY, new SpawnEggItem(GCEntityTypes.ARCH_GREY, 0x656463, 0x2d8563, new Item.Properties())); + public static final Item CHEESE_SLIME_SPAWN_EGG = ITEMS.register(Constant.SpawnEgg.CHEESE_SLIME, new SpawnEggItem(GCEntityTypes.CHEESE_SLIME, 0xd6cd76, 0xbeb44b, new Item.Properties())); private static Item registerGeneric(String id) { return ITEMS.register(id, new Item(new Item.Properties())); @@ -327,5 +328,6 @@ public static void register() { DispenserBlock.registerBehavior(OLI_GRUB_SPAWN_EGG, DispenserBlock.DISPENSER_REGISTRY.get(Items.CREEPER_SPAWN_EGG)); DispenserBlock.registerBehavior(GREY_SPAWN_EGG, DispenserBlock.DISPENSER_REGISTRY.get(Items.CREEPER_SPAWN_EGG)); DispenserBlock.registerBehavior(ARCH_GREY_SPAWN_EGG, DispenserBlock.DISPENSER_REGISTRY.get(Items.CREEPER_SPAWN_EGG)); + DispenserBlock.registerBehavior(CHEESE_SLIME_SPAWN_EGG, DispenserBlock.DISPENSER_REGISTRY.get(Items.CREEPER_SPAWN_EGG)); } } diff --git a/src/main/java/dev/galacticraft/mod/data/GCEntityLoot.java b/src/main/java/dev/galacticraft/mod/data/GCEntityLoot.java index 8175229d3..38ff32def 100644 --- a/src/main/java/dev/galacticraft/mod/data/GCEntityLoot.java +++ b/src/main/java/dev/galacticraft/mod/data/GCEntityLoot.java @@ -24,6 +24,7 @@ import dev.galacticraft.mod.content.GCEntityTypes; import dev.galacticraft.mod.content.GCRegistry; +import dev.galacticraft.mod.content.item.GCItems; import dev.galacticraft.mod.data.loot.GCEntityLootSubProvider; import dev.galacticraft.mod.tag.GCItemTags; import net.minecraft.advancements.critereon.EntityPredicate; @@ -227,6 +228,17 @@ public void generate() { .when(LootItemKilledByPlayerCondition.killedByPlayer()) ) ); + add(GCEntityTypes.CHEESE_SLIME, + LootTable.lootTable() + .withPool( + LootPool.lootPool() + .setRolls(ConstantValue.exactly(1.0F)) + .add( + LootItem.lootTableItem(GCItems.MOON_CHEESE_CURD) + .apply(SetItemCountFunction.setCount(UniformGenerator.between(1.0F, 2.0F))) + .apply(EnchantedCountIncreaseFunction.lootingMultiplier(this.lookup, UniformGenerator.between(0.0F, 1.0F))) + ) + )); add(GCEntityTypes.GAZER, noDrops()); add(GCEntityTypes.RUMBLER, noDrops()); add(GCEntityTypes.COMET_CUBE, noDrops()); diff --git a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java index 8ec8e3a34..617470c7f 100644 --- a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java @@ -617,6 +617,7 @@ protected void generateItemTranslations() { this.item(GCItems.OLI_GRUB_SPAWN_EGG, "Oli Grub Spawn Egg"); this.item(GCItems.GREY_SPAWN_EGG, "Grey Spawn Egg"); this.item(GCItems.ARCH_GREY_SPAWN_EGG, "Arch Grey Spawn Egg"); + this.item(GCItems.CHEESE_SLIME_SPAWN_EGG, "Cheese Slime Spawn Egg"); } protected void generateTagTranslations() { @@ -805,6 +806,7 @@ protected void generateEntityTranslations() { this.entity(GCEntityTypes.ROCKET, "Rocket"); this.entity(GCEntityTypes.RUMBLER, "Rumbler"); this.entity(GCEntityTypes.THROWABLE_METEOR_CHUNK, "Meteor Chunk"); + this.entity(GCEntityTypes.CHEESE_SLIME, "Cheese Slime"); } protected void generateCelestialBodyTranslations() { diff --git a/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java b/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java index cbcd2e57e..a88a531f5 100644 --- a/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java @@ -827,6 +827,7 @@ public void generateItemModels(ItemModelGenerators generator) { generator.generateFlatItem(GCItems.OLI_GRUB_SPAWN_EGG, GCModelTemplates.SPAWN_EGG); generator.generateFlatItem(GCItems.GREY_SPAWN_EGG, GCModelTemplates.SPAWN_EGG); generator.generateFlatItem(GCItems.ARCH_GREY_SPAWN_EGG, GCModelTemplates.SPAWN_EGG); + generator.generateFlatItem(GCItems.CHEESE_SLIME_SPAWN_EGG, GCModelTemplates.SPAWN_EGG); } private void createLayeredItem(ItemModelGenerators generator, Item item, String overlay) { diff --git a/src/main/resources/assets/galacticraft/textures/entity/cheese_slime.png b/src/main/resources/assets/galacticraft/textures/entity/cheese_slime.png new file mode 100644 index 000000000..dfaaf7d79 Binary files /dev/null and b/src/main/resources/assets/galacticraft/textures/entity/cheese_slime.png differ