Skip to content

Commit

Permalink
Hamster ball progress (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sydokiddo committed Nov 10, 2024
1 parent 702ea4c commit 4ccf9c9
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,6 @@ public void setCustomAnimations(HamsterBall animatable, long instanceId, Animati
super.setCustomAnimations(animatable, instanceId, animationState);

if (animationState == null) return;

CoreGeoBone root = this.getAnimationProcessor().getBone("root");

// if (animatable.getDeltaMovement().x() != 0 || animatable.getDeltaMovement().z() != 0) {
// root.setRotX(15);
// }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import com.starfish_studios.hamsters.client.model.HamsterBallModel;
import com.starfish_studios.hamsters.entity.HamsterBall;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.util.Mth;
import org.jetbrains.annotations.NotNull;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
Expand All @@ -29,4 +31,17 @@ public void preRender(PoseStack poseStack, HamsterBall animatable, BakedGeoModel
public void render(@NotNull HamsterBall animatable, float entityYaw, float partialTicks, @NotNull PoseStack poseStack, @NotNull MultiBufferSource bufferSource, int packedLight) {
super.render(animatable, entityYaw, partialTicks, poseStack, bufferSource, packedLight);
}

@Override
protected void applyRotations(HamsterBall animatable, PoseStack poseStack, float ageInTicks, float rotationYaw, float partialTick) {
poseStack.mulPose(Axis.YP.rotationDegrees(180.0F - rotationYaw));
float hitRotation = (float) (animatable.level().getGameTime() - animatable.lastHit) + partialTick;
if (hitRotation < 5.0F) poseStack.mulPose(Axis.YP.rotationDegrees(Mth.sin(hitRotation / 1.5F * (float) Math.PI) * 5.0F).normalize());
super.applyRotations(animatable, poseStack, ageInTicks, rotationYaw, partialTick);
}

@Override
public boolean shouldShowName(@NotNull HamsterBall animatable) {
return false;
}
}
170 changes: 106 additions & 64 deletions src/main/java/com/starfish_studios/hamsters/entity/HamsterBall.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,79 +4,130 @@
import com.starfish_studios.hamsters.registry.HamstersEntityType;
import com.starfish_studios.hamsters.registry.HamstersItems;
import net.minecraft.core.NonNullList;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
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.player.Player;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.core.animation.AnimatableManager;
import software.bernie.geckolib.core.animation.AnimationController;
import software.bernie.geckolib.core.animation.AnimationState;
import software.bernie.geckolib.core.animation.RawAnimation;
import software.bernie.geckolib.core.object.PlayState;
import software.bernie.geckolib.util.GeckoLibUtil;
import java.util.Objects;

public class HamsterBall extends PathfinderMob implements HamstersGeoEntity {
public class HamsterBall extends LivingEntity implements HamstersGeoEntity {

private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private final NonNullList<ItemStack> armorItems = NonNullList.withSize(0, ItemStack.EMPTY);
public long lastHit;

protected static final RawAnimation ROLL = RawAnimation.begin().thenLoop("animation.sf_nba.hamster_ball.roll");

public HamsterBall(EntityType<? extends PathfinderMob> entityType, Level level) {
public HamsterBall(EntityType<? extends LivingEntity> entityType, Level level) {
super(entityType, level);
this.setMaxUpStep(1.0F);
}

public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 500.0).add(Attributes.MOVEMENT_SPEED, 0.25);
return LivingEntity.createLivingAttributes().add(Attributes.MAX_HEALTH, 1.0D).add(Attributes.MOVEMENT_SPEED, 0.3D);
}

protected SoundEvent getHurtSound(@NotNull DamageSource damageSource) {
return SoundEvents.SHULKER_HURT_CLOSED;
}
@Override
public @NotNull InteractionResult interactAt(@NotNull Player player, @NotNull Vec3 vec3, @NotNull InteractionHand interactionHand) {

protected void positionRider(@NotNull Entity entity, Entity.@NotNull MoveFunction moveFunction) {
super.positionRider(entity, moveFunction);
if (entity instanceof Hamster) entity.setPos(this.getX(), this.getY() + 0.125f, this.getZ());
}
InteractionResult original = super.interactAt(player, vec3, interactionHand);
if (player.getItemInHand(interactionHand).is(Items.NAME_TAG)) return original;

@Nullable
public LivingEntity getControllingPassenger() {
if (this.getFirstPassenger() instanceof Hamster hamster) return hamster;
return null;
if (player.getAbilities().mayBuild && player.isShiftKeyDown()) {
this.breakHamsterBall();
return InteractionResult.SUCCESS;
}

return original;
}

@Override
public final @NotNull InteractionResult mobInteract(@NotNull Player player, @NotNull InteractionHand interactionHand) {
public boolean hurt(@NotNull DamageSource damageSource, float damageAmount) {

if (!this.level().isClientSide() && player.isShiftKeyDown()) {
this.remove(RemovalReason.DISCARDED);
this.spawnAtLocation(Objects.requireNonNull(this.getPickResult()));
level().playSound(null, this.blockPosition(), SoundEvents.CHICKEN_EGG, SoundSource.NEUTRAL, 0.6F, ((this.level().getRandom().nextFloat() - this.level().getRandom().nextFloat()) * 0.2F + 1.0F));
return InteractionResult.SUCCESS;
Entity entity = damageSource.getEntity();

if (this.level().isClientSide() || this.isRemoved() || this.isInvulnerableTo(damageSource) || entity instanceof Player player && !player.getAbilities().mayBuild) return false;

if (damageSource.is(DamageTypeTags.BYPASSES_INVULNERABILITY) || damageSource.is(DamageTypeTags.IS_EXPLOSION)) {
this.breakHamsterBall();
return false;
}

if (damageSource.isCreativePlayer()) {
this.breakHamsterBall();
return damageSource.getDirectEntity() instanceof AbstractArrow abstractArrow && abstractArrow.getPierceLevel() > 0;
}

long gameTime = this.level().getGameTime();

if (gameTime - this.lastHit <= 5L || damageSource.getDirectEntity() instanceof AbstractArrow) {
this.breakHamsterBall();
} else {
this.knockback(0.4F, Objects.requireNonNull(damageSource.getSourcePosition()).x() - this.getX(), Objects.requireNonNull(damageSource.getSourcePosition()).z() - this.getZ());
this.level().broadcastEntityEvent(this, (byte) 32);
this.gameEvent(GameEvent.ENTITY_DAMAGE, damageSource.getEntity());
this.lastHit = gameTime;
}

return super.mobInteract(player, interactionHand);
return true;
}

@Override
public void kill() {
this.remove(Entity.RemovalReason.KILLED);
this.gameEvent(GameEvent.ENTITY_DIE);
}

private void breakHamsterBall() {

ItemStack itemStack = Objects.requireNonNull(this.getPickResult()).getItem().getDefaultInstance();
if (this.hasCustomName()) itemStack.setHoverName(this.getCustomName());
Block.popResource(this.level(), this.blockPosition(), itemStack);

this.playSound(SoundEvents.CHICKEN_EGG, 0.5F, 1.0F + (this.level().getRandom().nextFloat() - this.level().getRandom().nextFloat()) * 0.2F);
this.kill();
}

@Override
public void handleEntityEvent(byte entityEvent) {
if (entityEvent == 32 && this.level().isClientSide()) {
this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.SHULKER_HURT_CLOSED, this.getSoundSource(), 0.3F, 1.0F, false);
this.lastHit = this.level().getGameTime();
} else {
super.handleEntityEvent(entityEvent);
}
}

// region Movement

@Override
public void travel(@NotNull Vec3 vec3) {
float baseMovement = 0.91F;
float horizontalMultiplier = this.onGround() ? baseMovement * 0.91F : 0.91F;
Vec3 calculateMovement = this.handleRelativeFrictionAndCalculateMovement(vec3, baseMovement);
this.setDeltaMovement(calculateMovement.x() * (double) (horizontalMultiplier), calculateMovement.y() * 0.9800000190734863, calculateMovement.z() * (double) horizontalMultiplier);
if (this.onGround()) this.setDeltaMovement(this.getDeltaMovement().multiply(1.2D, 1.0D, 1.2D));
super.travel(vec3);
}

@Override
protected @NotNull Vec3 getRiddenInput(@NotNull Player player, @NotNull Vec3 vec3) {
return new Vec3(0.0D, 0.0D, 1.0D);
}

@Override
protected void positionRider(@NotNull Entity entity, Entity.@NotNull MoveFunction moveFunction) {
super.positionRider(entity, moveFunction);
if (entity instanceof Hamster) entity.setPos(this.getX(), this.getY() + 0.125F, this.getZ());
}

@Override
Expand All @@ -93,21 +144,23 @@ protected void tickRidden(@NotNull Player player, @NotNull Vec3 vec3) {
super.tickRidden(player, vec3);
}

// endregion

// region Misc

@Override
protected @NotNull Vec3 getRiddenInput(@NotNull Player player, @NotNull Vec3 vec3) {
return new Vec3(0.0, 0.0, 1.0);
public ItemStack getPickResult() {
return new ItemStack(HamstersItems.LIGHT_BLUE_HAMSTER_BALL);
}

@Override
protected float getRiddenSpeed(@NotNull Player player) {
return (float) (this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.25);
public @NotNull HumanoidArm getMainArm() {
return HumanoidArm.RIGHT;
}

// region Misc

@Override
public @NotNull Iterable<ItemStack> getArmorSlots() {
return this.armorItems;
return NonNullList.withSize(4, ItemStack.EMPTY);
}

@Override
Expand All @@ -116,42 +169,31 @@ protected float getRiddenSpeed(@NotNull Player player) {
}

@Override
public void setItemSlot(@NotNull EquipmentSlot equipmentSlot, @NotNull ItemStack itemStack) {
this.verifyEquippedItem(itemStack);
}
public void setItemSlot(@NotNull EquipmentSlot equipmentSlot, @NotNull ItemStack itemStack) {}

@Override
public @NotNull HumanoidArm getMainArm() {
return HumanoidArm.RIGHT;
public void thunderHit(@NotNull ServerLevel serverLevel, @NotNull LightningBolt lightningBolt) {}

@Override
public boolean isAffectedByPotions() {
return false;
}

@Override
public ItemStack getPickResult() {
return new ItemStack(HamstersItems.BLUE_HAMSTER_BALL);
public boolean attackable() {
return false;
}

// endregion

// region GeckoLib

@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllerRegistrar) {
controllerRegistrar.add(new AnimationController<>(this, "controller", 0, this::animController));
}

protected <E extends HamsterBall> PlayState animController(final AnimationState<E> event) {

if (event.isMoving() && this.getDeltaMovement().x() != 0 || this.getDeltaMovement().z() != 0) {
event.setAnimation(ROLL);
return PlayState.CONTINUE;
}

return PlayState.STOP;
}
public void registerControllers(AnimatableManager.ControllerRegistrar controllerRegistrar) {}

@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.geoCache;
return GeckoLibUtil.createInstanceCache(this);
}

// endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,12 @@ public TagKey<Block> getBlockTag() {
return HamstersTags.HAMSTER_BOWLS;
}

@Override
public boolean canUse() {
if (HamsterNew.this.getCheekLevel() >= 3 || HamsterNew.this.getEatingCooldownTicks() > 0) return false;
return super.canUse();
}

@Override
public BlockPos getPosWithBlock(BlockPos blockPos, BlockGetter blockGetter) {
BlockState blockState = blockGetter.getBlockState(blockPos);
Expand All @@ -1015,22 +1021,18 @@ public BlockPos getPosWithBlock(BlockPos blockPos, BlockGetter blockGetter) {
@Override
protected boolean isValidTarget(@NotNull LevelReader levelReader, @NotNull BlockPos blockPos) {
BlockState blockState = levelReader.getBlockState(blockPos);
if (blockState.is(this.getBlockTag()) && !HamsterBowlBlock.hasSeeds(blockState) || this.isHamsterFull()) return false;
if (blockState.is(this.getBlockTag()) && !HamsterBowlBlock.hasSeeds(blockState)) return false;
return super.isValidTarget(levelReader, blockPos);
}

private boolean isHamsterFull() {
return HamsterNew.this.getCheekLevel() >= 3 || HamsterNew.this.getEatingCooldownTicks() > 0;
}

@Override
public void tick() {

super.tick();

BlockPos hamsterBowl = this.getPosWithBlock(this.mob.blockPosition(), this.mob.level());

if (hamsterBowl != null && this.mob.position().distanceTo(Vec3.atBottomCenterOf(hamsterBowl)) <= 1.0D && !this.isHamsterFull()) {
if (hamsterBowl != null && this.mob.position().distanceTo(Vec3.atBottomCenterOf(hamsterBowl)) <= 1.0D) {

HamsterNew.this.setDefaultSleepingCooldown();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.SpawnEggItem;
import java.util.function.Supplier;
Expand All @@ -20,7 +19,7 @@ public class HamstersItems {
// region Common Properties

public static Item hamsterBallItem() {
return new HamsterBallItem(new FabricItemSettings().stacksTo(1));
return new HamsterBallItem(new FabricItemSettings().stacksTo(16));
}

// endregion
Expand All @@ -29,13 +28,13 @@ public static Item hamsterBallItem() {

public static final Item HAMSTER_SPAWN_EGG = register("hamster_spawn_egg", new SpawnEggItem(HamstersEntityType.HAMSTER, 16747824, 16775119, new FabricItemSettings()));
public static final Item HAMSTER_NEW_SPAWN_EGG = register("hamster_new_spawn_egg", new SpawnEggItem(HamstersEntityType.HAMSTER_NEW, 16747824, 16775119, new FabricItemSettings()));
// public static final Item TUNNEL = register("tunnel", new BlockItem(HamstersBlocks.TUNNEL, new FabricItemSettings()));
// public static final Item TUNNEL = register("tunnel", new BlockItem(HamstersBlocks.TUNNEL, new FabricItemSettings()));

public static final Item HAMSTER = register("hamster", new HamsterItem(new FabricItemSettings().stacksTo(1)));
public static final Item SEED_MIX = register("seed_mix", new Item(new FabricItemSettings()));

public static final Item CHOCOLATE_HAMSTER = register("chocolate_hamster", new ChocolateHamsterItem(new FabricItemSettings()
.food(new FoodProperties.Builder().nutrition(2).saturationMod(0.1F).build())));
.food(new FoodProperties.Builder().nutrition(2).saturationMod(0.1F).build())));

// RED, ORANGE, YELLOW, LIME, GREEN, CYAN, BLUE, LIGHT BLUE, PINK, MAGENTA, PURPLE, WHITE, LIGHT GRAY, GRAY, BLACK, BROWN

Expand Down Expand Up @@ -64,7 +63,6 @@ public static Item hamsterBallItem() {
// return registerItem(name, () -> new HamsterItem(entitySupplier, fluidSupplier.get(), soundSupplier, variantAmount, new Item.Properties().stacksTo(1)));
// }


@SuppressWarnings("unused")
public static <T extends Item> Supplier<T> registerItem(String id, Supplier<T> item) {
T registry = Registry.register(BuiltInRegistries.ITEM, Hamsters.id(id), item.get());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"hamsters:hamster_ball"
]
}

0 comments on commit 4ccf9c9

Please sign in to comment.