fix: Pride Totem trampling over vanilla

Fixes #65
Fixes #68
This commit is contained in:
Ampflower 🌺 2025-03-14 01:07:46 -07:00
parent 47316b94c6
commit c2269dc07d
No known key found for this signature in database
GPG Key ID: FC0397C90D508D7F
7 changed files with 82 additions and 78 deletions

View File

@ -2,71 +2,48 @@ package gay.pridecraft.joy.mixin.client;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import gay.pridecraft.joy.registry.JoyItems;
import gay.pridecraft.joy.registry.JoyParticles;
import net.minecraft.client.MinecraftClient;
import gay.pridecraft.joy.misc.ParticleEmitter;
import gay.pridecraft.joy.tags.JoyItemTags;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.s2c.play.EntityStatusS2CPacket;
import net.minecraft.sound.SoundEvents;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.util.Hand;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerMixin {
@Shadow
public abstract ClientWorld getWorld();
@WrapOperation(method = "getActiveTotemOfUndying", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isOf(Lnet/minecraft/item/Item;)Z"))
private static boolean modifyTotemOfUndyingAnimation(ItemStack instance, Item item, Operation<Boolean> original) {
return instance.isOf(JoyItems.TOTEM_OF_PRIDE) || original.call(instance, item);
return instance.isIn(JoyItemTags.TOTEM_OF_UNDYING) || original.call(instance, item);
}
@Inject(at = @At("HEAD"), method = "onEntityStatus")
private void onCustomEntityStatus(EntityStatusS2CPacket packet, CallbackInfo ci) {
if (this.getWorld() == null || packet.getStatus() != 36) {
return;
@ModifyArg(method = "onEntityStatus",
at = @At(value = "INVOKE",
target = "Lnet/minecraft/client/particle/ParticleManager;addEmitter(Lnet/minecraft/entity/Entity;Lnet/minecraft/particle/ParticleEffect;I)V"))
private ParticleEffect changeTotemParticle(Entity entity, ParticleEffect original, int maxAge) {
if (entity instanceof LivingEntity living &&
getActiveTotemOfUndying(living).getItem() instanceof ParticleEmitter emitter) {
return emitter.joy$getEffect();
}
final var client = MinecraftClient.getInstance();
//noinspection ResultOfMethodCallIgnored - unnecessary
client.submit(() -> {
final World world = this.getWorld();
final Entity entity = packet.getEntity(world);
if (entity == null) {
return;
}
client.particleManager.addEmitter(entity, JoyParticles.TOTEM_OF_PRIDE_PARTICLE, 30);
world.playSound(entity.getX(), entity.getY(), entity.getZ(), SoundEvents.ITEM_TOTEM_USE, entity.getSoundCategory(), 1.f, 1.f, false);
if (entity == client.player) {
client.gameRenderer.showFloatingItem(modifyTotem(client.player));
}
});
return original;
}
@Unique
private static ItemStack modifyTotem(PlayerEntity player) {
private static ItemStack getActiveTotemOfUndying(LivingEntity player) {
for (Hand hand : Hand.values()) {
ItemStack itemStack = player.getStackInHand(hand);
if (itemStack.isOf(JoyItems.TOTEM_OF_PRIDE)) {
if (itemStack.isIn(JoyItemTags.TOTEM_OF_UNDYING)) {
return itemStack;
}
}
return null;
return ItemStack.EMPTY;
}
}

View File

@ -27,5 +27,9 @@ public class JoyItemTagProvider extends FabricTagProvider.ItemTagProvider {
getOrCreateTagBuilder(JoyItemTags.GLIDERS).addTag(
JoyItemTags.ELYTRA
);
getOrCreateTagBuilder(JoyItemTags.TOTEM_OF_UNDYING).add(
Items.TOTEM_OF_UNDYING,
JoyItems.TOTEM_OF_PRIDE
);
}
}

View File

@ -0,0 +1,23 @@
package gay.pridecraft.joy.item;
import gay.pridecraft.joy.misc.ParticleEmitter;
import net.minecraft.item.Item;
import net.minecraft.particle.ParticleEffect;
/**
* @author Ampflower
* @since 1.0.0
**/
public class ParticleEmittingItem extends Item implements ParticleEmitter {
private final ParticleEffect effect;
public ParticleEmittingItem(final Settings settings, final ParticleEffect effect) {
super(settings);
this.effect = effect;
}
@Override
public ParticleEffect joy$getEffect() {
return effect;
}
}

View File

@ -0,0 +1,11 @@
package gay.pridecraft.joy.misc;
import net.minecraft.particle.ParticleEffect;
/**
* @author Ampflower
* @since 1.0.0
**/
public interface ParticleEmitter {
ParticleEffect joy$getEffect();
}

View File

@ -1,56 +1,43 @@
package gay.pridecraft.joy.mixin;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import gay.pridecraft.joy.registry.JoyItems;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import gay.pridecraft.joy.tags.JoyItemTags;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(LivingEntity.class)
public abstract class LivingEntityMixin extends Entity {
@Shadow public abstract ItemStack getStackInHand(Hand hand);
@Shadow public abstract void setHealth(float health);
@Shadow public abstract boolean clearStatusEffects();
@Shadow public abstract boolean addStatusEffect(StatusEffectInstance effect);
public LivingEntityMixin(EntityType<?> type, World world) {
super(type, world);
}
@WrapMethod(method = "tryUseTotem")
public boolean useCustomTotem(DamageSource source, Operation<Boolean> original) {
LivingEntityMixin entity = this;
@WrapOperation(method = "tryUseTotem",
at = @At(value = "INVOKE",
target = "Lnet/minecraft/item/ItemStack;isOf(Lnet/minecraft/item/Item;)Z"))
private boolean onCheckTotem(ItemStack self, Item other, Operation<Boolean> operation) {
if (self.isIn(JoyItemTags.TOTEM_OF_UNDYING)) {
return true;
}
return operation.call(self, other);
}
ItemStack offhandStack = entity.getStackInHand(Hand.OFF_HAND);
ItemStack mainHandStack = entity.getStackInHand(Hand.MAIN_HAND);
if (!offhandStack.isOf(JoyItems.TOTEM_OF_PRIDE) && !mainHandStack.isOf(JoyItems.TOTEM_OF_PRIDE)) return original.call(source);
if (offhandStack.isOf(JoyItems.TOTEM_OF_PRIDE)) offhandStack.decrement(1);
else if (mainHandStack.isOf(JoyItems.TOTEM_OF_PRIDE)) mainHandStack.decrement(1);
this.setHealth(1.0F);
this.clearStatusEffects();
this.addStatusEffect(new StatusEffectInstance(StatusEffects.REGENERATION, 45 * 20, 1));
this.addStatusEffect(new StatusEffectInstance(StatusEffects.FIRE_RESISTANCE, 40 * 20, 0));
this.addStatusEffect(new StatusEffectInstance(StatusEffects.ABSORPTION, 5 * 20, 1));
this.getWorld().sendEntityStatus(entity, (byte) 36);
return true;
@ModifyArg(method = "tryUseTotem",
at = @At(value = "INVOKE",
target = "Lnet/minecraft/stat/StatType;getOrCreateStat(Ljava/lang/Object;)Lnet/minecraft/stat/Stat;"))
private Object modifyStatArgument(Object original, @Local ItemStack real) {
if (real != null && !real.isEmpty()) {
return real.getItem();
}
return original;
}
}

View File

@ -4,6 +4,7 @@ package gay.pridecraft.joy.registry;
import gay.pridecraft.joy.JoyUtil;
import gay.pridecraft.joy.Pivot;
import gay.pridecraft.joy.item.CustomElytraItem;
import gay.pridecraft.joy.item.ParticleEmittingItem;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.BundleContentsComponent;
import net.minecraft.item.BrushItem;
@ -24,7 +25,7 @@ public final class JoyItems {
public static final Item TOTEM_OF_PRIDE = registerItem(
"totem_of_pride",
new Item(new Item.Settings().maxCount(1))
new ParticleEmittingItem(new Item.Settings().maxCount(1), JoyParticles.TOTEM_OF_PRIDE_PARTICLE)
);
public static final Item PRIDE_BRUSH = registerItem(

View File

@ -13,7 +13,8 @@ import net.minecraft.util.Identifier;
public final class JoyItemTags {
public static final TagKey<Item>
GLIDERS = common("gliders"),
ELYTRA = common("elytra");
ELYTRA = common("elytra"),
TOTEM_OF_UNDYING = common("totem_of_undying");
private static TagKey<Item> joy(String name) {
return TagKey.of(RegistryKeys.ITEM, JoyUtil.id(name));