diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityModel.java index 48faac72..38798d64 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityModel.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.client.render.entity; +import java.util.List; + import com.minelittlepony.unicopia.entity.mob.CrystalShardsEntity; import net.minecraft.client.model.Dilation; @@ -12,12 +14,15 @@ import net.minecraft.client.model.TexturedModelData; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.entity.model.EntityModel; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; public class CrystalShardsEntityModel extends EntityModel { private final ModelPart part; + private final List crystals; public CrystalShardsEntityModel(ModelPart root) { this.part = root; + this.crystals = List.of(part.getChild("west"), part.getChild("north"), part.getChild("south"), part.getChild("east"), part.getChild("primary")); } public static TexturedModelData getTexturedModelData() { @@ -33,6 +38,22 @@ public class CrystalShardsEntityModel extends EntityModel { @Override public void setAngles(CrystalShardsEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + + float offset = 0; + float amplitude = 0.02F; + + for (ModelPart part : crystals) { + part.resetTransform(); + + if (entity.isShaking()) { + float animationTime = (entity.age + ++offset) * 122F; + float sin = MathHelper.sin(animationTime) * amplitude; + + part.pitch += sin; + part.yaw += MathHelper.cos(animationTime) * amplitude; + part.roll += -sin; + } + } } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityRenderer.java index 3ca4649b..5404bb6e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityRenderer.java @@ -9,8 +9,10 @@ import net.minecraft.client.render.OverlayTexture; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.render.model.ModelLoader; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RotationAxis; public class CrystalShardsEntityRenderer extends EntityRenderer { @@ -37,6 +39,11 @@ public class CrystalShardsEntityRenderer extends EntityRenderer ALL_DIRECTIONS = Set.of(Direction.values()); - private static final TrackedData ATTACHMENT_FACE = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.FACING); - private static final TrackedData GROWTH = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.INTEGER); - private static final TrackedData DECAYING = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.BOOLEAN); - private static final TrackedData CORRUPT = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private static final TrackedData ATTACHMENT_FACE = DataTracker.registerData(CrystalShardsEntity.class, TrackedDataHandlerRegistry.FACING); + private static final TrackedData GROWTH = DataTracker.registerData(CrystalShardsEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final TrackedData DECAYING = DataTracker.registerData(CrystalShardsEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private static final TrackedData CORRUPT = DataTracker.registerData(CrystalShardsEntity.class, TrackedDataHandlerRegistry.BOOLEAN); public static boolean infestBlock(ServerWorld world, BlockPos pos) { if (world.isAir(pos) || !world.getFluidState(pos).isOf(Fluids.EMPTY)) { @@ -89,12 +86,18 @@ public class CrystalShardsEntity extends Entity implements MagicImmune { @Override protected void initDataTracker() { + super.initDataTracker(); dataTracker.startTracking(ATTACHMENT_FACE, Direction.UP); dataTracker.startTracking(GROWTH, 0); dataTracker.startTracking(DECAYING, false); dataTracker.startTracking(CORRUPT, false); } + @Override + public float getMaxHealth() { + return 15F; + } + public float getGrowth(float tickDelta) { int age = getGrowth(); float lerped = MathHelper.clamp(MathHelper.lerp(tickDelta, prevAge, age), 0, FULL_GROWTH_AGE) / (float)FULL_GROWTH_AGE; @@ -157,19 +160,22 @@ public class CrystalShardsEntity extends Entity implements MagicImmune { super.tick(); - if (getGrowth() < FULL_GROWTH_AGE) { - playSound(USounds.Vanilla.BLOCK_AMETHYST_BLOCK_CHIME, 1, 1); + if (ticksShaking > 0 || getGrowth() < FULL_GROWTH_AGE) { + if (age % random.nextBetween(2, 5) == 0) { + playSound(USounds.Vanilla.BLOCK_AMETHYST_BLOCK_HIT, 1, + 1 - MathHelper.clamp(getGrowth(1), 0, 1) * 0.5F); + } } - if (isInvalid(getWorld(), getBlockPos(), getAttachmentFace())) { + if (isDead() || isInvalid(getWorld(), getBlockPos(), getAttachmentFace())) { kill(); } } @Override - public boolean damage(DamageSource source, float amount) { + protected void onHurt() { getWorld().sendEntityStatus(this, SHAKE); - return super.damage(source, amount); + ticksShaking = 10; } @Override @@ -185,7 +191,7 @@ public class CrystalShardsEntity extends Entity implements MagicImmune { public void handleStatus(byte status) { switch (status) { case SHAKE: - ticksShaking = 30; + ticksShaking = 10; break; default: super.handleStatus(status); @@ -194,7 +200,8 @@ public class CrystalShardsEntity extends Entity implements MagicImmune { @Override public void writeCustomDataToNbt(NbtCompound nbt) { - nbt.putFloat("yaw", this.getYaw()); + super.writeCustomDataToNbt(nbt); + nbt.putFloat("yaw", getYaw()); nbt.putInt("growth", getGrowth()); nbt.putString("face", getAttachmentFace().getName()); nbt.putBoolean("decaying", isDecaying()); @@ -203,6 +210,7 @@ public class CrystalShardsEntity extends Entity implements MagicImmune { @Override public void readCustomDataFromNbt(NbtCompound nbt) { + super.readCustomDataFromNbt(nbt); setYaw(nbt.getFloat("yaw")); setGrowth(nbt.getInt("growth")); setAttachmentFace(Direction.byName(nbt.getString("face"))); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/FloatingArtefactEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/FloatingArtefactEntity.java index 40aa822b..26fe7e5d 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/FloatingArtefactEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/FloatingArtefactEntity.java @@ -3,12 +3,9 @@ package com.minelittlepony.unicopia.entity.mob; import java.util.Optional; import com.minelittlepony.unicopia.USounds; -import com.minelittlepony.unicopia.entity.MagicImmune; -import com.minelittlepony.unicopia.entity.damage.UDamageSources; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.server.world.Altar; -import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.data.DataTracker; @@ -21,7 +18,7 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -public class FloatingArtefactEntity extends Entity implements UDamageSources, MagicImmune { +public class FloatingArtefactEntity extends StationaryObjectEntity { private static final TrackedData ITEM = DataTracker.registerData(FloatingArtefactEntity.class, TrackedDataHandlerRegistry.ITEM_STACK); private static final TrackedData STATE = DataTracker.registerData(FloatingArtefactEntity.class, TrackedDataHandlerRegistry.BYTE); private static final TrackedData TARGET_ROTATION_SPEED = DataTracker.registerData(FloatingArtefactEntity.class, TrackedDataHandlerRegistry.FLOAT); @@ -40,7 +37,6 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources, Ma private int boostDuration; - private float health; private int ticksUntilRegen; public final float positionSeed; @@ -50,11 +46,11 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources, Ma super(entityType, world); positionSeed = (float)(Math.random() * Math.PI * 2); - health = getMaxHealth(); } @Override protected void initDataTracker() { + super.initDataTracker(); dataTracker.startTracking(ITEM, ItemStack.EMPTY); dataTracker.startTracking(STATE, (byte)0); dataTracker.startTracking(TARGET_ROTATION_SPEED, 1F); @@ -93,16 +89,9 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources, Ma return MathHelper.lerp(tickDelta, prevRotationSpeed, rotationSpeed); } - public int getMaxHealth() { - return 20; - } - - public void setHealth(float health) { - this.health = MathHelper.clamp(health, 0, getMaxHealth()); - } - - public float getHealth() { - return health; + @Override + public float getMaxHealth() { + return 20F; } @Override @@ -163,16 +152,17 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources, Ma @Override protected void readCustomDataFromNbt(NbtCompound compound) { + super.readCustomDataFromNbt(compound); setStack(ItemStack.fromNbt(compound.getCompound("Item"))); setState(State.valueOf(compound.getInt("State"))); setRotationSpeed(compound.getFloat("spin"), compound.getInt("spinDuration")); - setHealth(compound.getFloat("health")); ticksUntilRegen = compound.getInt("regen"); altar = Altar.SERIALIZER.readOptional("altar", compound); } @Override protected void writeCustomDataToNbt(NbtCompound compound) { + super.writeCustomDataToNbt(compound); ItemStack stack = getStack(); if (!stack.isEmpty()) { compound.put("Item", stack.writeNbt(new NbtCompound())); @@ -180,46 +170,46 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources, Ma compound.putInt("State", getState().ordinal()); compound.putFloat("spin", getRotationSpeed()); compound.putInt("spinDuration", boostDuration); - compound.putFloat("health", getHealth()); compound.putInt("regen", ticksUntilRegen); Altar.SERIALIZER.writeOptional("altar", compound, altar); } @Override - public boolean damage(DamageSource damageSource, float amount) { + public boolean damage(DamageSource source, float damage) { if (getWorld().isClient || isInvulnerable()) { return false; } - if (isInvulnerableTo(damageSource) || !getStack().getItem().damage(damageSource)) { + if (isInvulnerableTo(source) || !getStack().getItem().damage(source)) { return false; } - if (damageSource.isSourceCreativePlayer()) { - health = 0; - } else { - health -= amount; - ticksUntilRegen = REGEN_PAUSE_TICKS; + ticksUntilRegen = REGEN_PAUSE_TICKS; + + if (source.isSourceCreativePlayer()) { + damage = getHealth(); } - if (health <= 0) { - remove(RemovalReason.KILLED); + return super.damage(source, damage); + } - ItemStack stack = getStack(); + @Override + protected void onKilled(DamageSource source) { + ItemStack stack = getStack(); - if (altar.isEmpty()) { - if (!(stack.getItem() instanceof Artifact) || ((Artifact)stack.getItem()).onArtifactDestroyed(this) != ActionResult.SUCCESS) { - if (!damageSource.isSourceCreativePlayer()) { - dropStack(stack); - } + if (altar.isEmpty()) { + if (!(stack.getItem() instanceof Artifact) || ((Artifact)stack.getItem()).onArtifactDestroyed(this) != ActionResult.SUCCESS) { + if (!source.isSourceCreativePlayer()) { + dropStack(stack); } } - } else { - playSound(USounds.ITEM_ICARUS_WINGS_WARN, 1, 1); } + } - return false; + @Override + protected void onHurt() { + playSound(USounds.ITEM_ICARUS_WINGS_WARN, 1, 1); } @Override @@ -228,16 +218,6 @@ public class FloatingArtefactEntity extends Entity implements UDamageSources, Ma altar.ifPresent(altar -> altar.tearDown(this, getWorld())); } - @Override - public boolean canHit() { - return true; - } - - @Override - public World asWorld() { - return getWorld(); - } - public enum State { INITIALISING, RUNNING, diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/StationaryObjectEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/StationaryObjectEntity.java new file mode 100644 index 00000000..3e89f204 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/StationaryObjectEntity.java @@ -0,0 +1,86 @@ +package com.minelittlepony.unicopia.entity.mob; + +import com.minelittlepony.unicopia.entity.MagicImmune; +import com.minelittlepony.unicopia.entity.damage.UDamageSources; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +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.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; + +public abstract class StationaryObjectEntity extends Entity implements UDamageSources, MagicImmune { + private static final TrackedData HEALTH = DataTracker.registerData(StationaryObjectEntity.class, TrackedDataHandlerRegistry.FLOAT); + + public StationaryObjectEntity(EntityType entityType, World world) { + super(entityType, world); + } + + @Override + protected void initDataTracker() { + dataTracker.startTracking(HEALTH, getMaxHealth()); + } + + public abstract float getMaxHealth(); + + public final float setHealth(float health) { + health = MathHelper.clamp(health, 0, getMaxHealth()); + dataTracker.set(HEALTH, health); + return health; + } + + public final float getHealth() { + return dataTracker.get(HEALTH); + } + + public final boolean isDead() { + return isRemoved() || getHealth() <= 0; + } + + @Override + public boolean damage(DamageSource source, float damage) { + if (!isDead()) { + if (setHealth(getHealth() - damage) <= 0) { + kill(); + onKilled(source); + } else { + onHurt(); + } + } + return false; + } + + protected void onKilled(DamageSource source) { + + } + + protected void onHurt() { + + } + + @Override + protected void readCustomDataFromNbt(NbtCompound compound) { + if (compound.contains("health", NbtElement.FLOAT_TYPE)) { + setHealth(compound.getFloat("health")); + } + } + + @Override + protected void writeCustomDataToNbt(NbtCompound compound) { + compound.putFloat("health", getHealth()); + } + + @Override + public final boolean canHit() { + return true; + } + + @Override + public final World asWorld() { + return getWorld(); + } +}