diff --git a/MODIFIERS.md b/MODIFIERS.md index 35f5602..9ef2b10 100644 --- a/MODIFIERS.md +++ b/MODIFIERS.md @@ -104,6 +104,10 @@ These effects are applied when holding the tool. **id:** `spawner` | **crafting:** `minecraft:mossy_cobblestone` ![mossy_cobblestone](https://raw.githubusercontent.com/anish-shanbhag/minecraft-api/master/public/images/items/mossy_cobblestone.png) **Decription:** While holding the spawners around you will glow. +### Vampiric +**id:** `vampiric` | **crafting:** `minecraft:fermented_spider_eye` ![fermented_spider_eye](https://raw.githubusercontent.com/anish-shanbhag/minecraft-api/master/public/images/items/fermented_spider_eye.png) + +**Decription:** Heals player for 5% of damage dealt to enemies, but prevents natural health regeneration and causes 0.5 hearts damage per 30 seconds when not hitting enemies. ## Users These effects are applied when right clicking. ### Fire Starter @@ -224,6 +228,10 @@ These effects are applied when hurting enemies. **id:** `soulbound` | **crafting:** `minecraft:nether_star` ![nether_star](https://raw.githubusercontent.com/anish-shanbhag/minecraft-api/master/public/images/items/nether_star.png) **Decription:** Grants 15% bonus damage and mining speed when wielded by the original owner. +### Vampiric +**id:** `vampiric` | **crafting:** `minecraft:fermented_spider_eye` ![fermented_spider_eye](https://raw.githubusercontent.com/anish-shanbhag/minecraft-api/master/public/images/items/fermented_spider_eye.png) + +**Decription:** Heals player for 5% of damage dealt to enemies, but prevents natural health regeneration and causes 0.5 hearts damage per 30 seconds when not hitting enemies. ### Withering **id:** `wither` | **crafting:** `minecraft:wither_rose` ![wither_rose](https://raw.githubusercontent.com/anish-shanbhag/minecraft-api/master/public/images/items/wither_rose.png) diff --git a/src/main/java/dev/marston/randomloot/loot/modifiers/ModifierRegistry.java b/src/main/java/dev/marston/randomloot/loot/modifiers/ModifierRegistry.java index b4627d2..b92ef99 100644 --- a/src/main/java/dev/marston/randomloot/loot/modifiers/ModifierRegistry.java +++ b/src/main/java/dev/marston/randomloot/loot/modifiers/ModifierRegistry.java @@ -3,6 +3,7 @@ import dev.marston.randomloot.loot.modifiers.breakers.*; import dev.marston.randomloot.loot.modifiers.holders.*; import dev.marston.randomloot.loot.modifiers.hurter.*; +import dev.marston.randomloot.loot.modifiers.hurter.Vampiric; import dev.marston.randomloot.loot.modifiers.hurter.Pummeling; import dev.marston.randomloot.loot.modifiers.hurter.Soulbound; import dev.marston.randomloot.loot.modifiers.stats.Busted; @@ -56,6 +57,7 @@ public static Modifier getModifier(String name) { public static Modifier CHARGING = register(new Charging()); public static Modifier COMBO = register(new Combo()); public static Modifier DRAINING = register(new Draining()); + public static Modifier VAMPIRIC = register(new Vampiric()); public static Modifier POISONOUS = register(new HurtEffect("Poisonous", "poison", 5, MobEffects.POISON)); public static Modifier WITHERING = register(new HurtEffect("Withering", "wither", 3, MobEffects.WITHER)); public static Modifier BLINDING = register(new HurtEffect("Blinding", "blinding", 4, MobEffects.BLINDNESS)); @@ -102,10 +104,10 @@ public static Modifier getModifier(String name) { public static final Set BREAKERS = Set.of(EXPLODE, LEARNING, ATTRACTING, VEINY, MELTING, EXCAVATOR, PROSPECTOR, MUNCHIES, FRAGILE, LUMBERING); public static final Set USERS = Set.of(TORCH_PLACE, DIRT_PLACE, FIRE_PLACE, FIRE_BALL, VOID_TOUCHED); - public static final Set HURTERS = Set.of(CRITICAL, CHARGING, FLAMING, COMBO, DRAINING, POISONOUS, + public static final Set HURTERS = Set.of(CRITICAL, CHARGING, FLAMING, COMBO, DRAINING, VAMPIRIC, POISONOUS, WITHERING, BLINDING, BEZERK, NEMESIS, SOULBOUND, SCORCHED, FROZEN, OVERGROWN, FIERCE, FEASTING, EXECUTIONER, CROWD_PLEASER, PUMMELING, HAILEYS_WRATH, MUNCHIES, CHAOTIC, FRAGILE, CLUNKY, EARLY_BIRD); public static final Set HOLDERS = Set.of(HASTY, ABSORBTION, FILLING, RAINY, ORE_FINDER, SPAWNER_FINDER, - LIVING, REGENERATING, RESISTANT, FIRE_RESISTANT, AQUATIC, HUNTER, FEASTING, NATURALIST, CLUNKY); + LIVING, REGENERATING, RESISTANT, FIRE_RESISTANT, AQUATIC, HUNTER, FEASTING, NATURALIST, CLUNKY, VAMPIRIC); public static final Set STATS = Set.of(BUSTED, FIERCE, MUNCHIES, CHAOTIC, FRAGILE); diff --git a/src/main/java/dev/marston/randomloot/loot/modifiers/hurter/Vampiric.java b/src/main/java/dev/marston/randomloot/loot/modifiers/hurter/Vampiric.java new file mode 100644 index 0000000..987111f --- /dev/null +++ b/src/main/java/dev/marston/randomloot/loot/modifiers/hurter/Vampiric.java @@ -0,0 +1,167 @@ +package dev.marston.randomloot.loot.modifiers.hurter; + +import dev.marston.randomloot.loot.LootItem; +import dev.marston.randomloot.loot.LootItem.ToolType; +import dev.marston.randomloot.loot.LootUtils; +import dev.marston.randomloot.loot.modifiers.EntityHurtModifier; +import dev.marston.randomloot.loot.modifiers.HoldModifier; +import dev.marston.randomloot.loot.modifiers.Modifier; +import net.minecraft.ChatFormatting; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import java.util.List; + +public class Vampiric implements EntityHurtModifier, HoldModifier { + + private String name; + private long lastHit; + private static final float HEAL_PERCENT = 0.05f; // 5% of damage dealt + private static final int DRAIN_INTERVAL = 600; // 30 seconds (600 ticks) + private static final float DRAIN_DAMAGE = 1.0f; // 0.5 hearts + + public Vampiric() { + this("Vampiric", 0L); + } + + public Vampiric(String name, long lastHit) { + this.name = name; + this.lastHit = lastHit; + } + + public Modifier clone() { + return new Vampiric(); + } + + @Override + public CompoundTag toNBT() { + CompoundTag tag = new CompoundTag(); + tag.putString(NAME, name); + tag.putLong("lastHit", lastHit); + return tag; + } + + @Override + public Modifier fromNBT(CompoundTag tag) { + return new Vampiric( + tag.getStringOr(NAME, "Vampiric"), + tag.getLongOr("lastHit", 0L) + ); + } + + @Override + public String name() { + return name; + } + + @Override + public String tagName() { + return "vampiric"; + } + + @Override + public String color() { + return ChatFormatting.DARK_RED.getName(); + } + + @Override + public String description() { + return "Heals player for 5% of damage dealt to enemies, but prevents natural health regeneration and causes 0.5 hearts damage per 30 seconds when not hitting enemies."; + } + + @Override + public void writeToLore(List list, boolean shift) { + MutableComponent comp = Modifier.makeComp(this.name(), this.color()); + list.add(comp); + } + + @Override + public Component writeDetailsToLore(Level level) { + if (level != null && lastHit > 0) { + long timeSinceHit = level.getGameTime() - lastHit; + long secondsSinceHit = timeSinceHit / 20; + + if (secondsSinceHit < 30) { + return Modifier.makeComp("Fed (" + (30 - secondsSinceHit) + "s)", ChatFormatting.GREEN); + } else { + return Modifier.makeComp("Hungry!", ChatFormatting.DARK_RED); + } + } + return null; + } + + @Override + public boolean forTool(ToolType type) { + return type.equals(ToolType.SWORD) || type.equals(ToolType.AXE); + } + + @Override + public boolean hurtEnemy(ItemStack itemstack, LivingEntity hurtee, LivingEntity hurter) { + Level level = hurtee.level(); + float damage = LootItem.getAttackDamage(itemstack, LootUtils.getToolType(itemstack)); + + // Heal the player for 5% of damage dealt + float healAmount = damage * HEAL_PERCENT; + hurter.heal(healAmount); + + // Update last hit time + this.lastHit = level.getGameTime(); + LootUtils.updateModifier(itemstack, this); + + // Visual feedback - red particles for lifesteal + if (!level.isClientSide()) { + Modifier.TrackEntityParticle(level, hurter, ParticleTypes.DAMAGE_INDICATOR); + } + + return false; + } + + @Override + public void hold(ItemStack stack, Level level, Entity holder) { + if (!(holder instanceof LivingEntity livingEntity)) { + return; + } + + // Prevent natural regeneration + MobEffectInstance hungerEffect = new MobEffectInstance(MobEffects.HUNGER, 3, 0, false, false); + livingEntity.addEffect(hungerEffect); + + // If we haven't hit anything recently, drain health + if (lastHit > 0) { + long timeSinceHit = level.getGameTime() - lastHit; + + // Every DRAIN_INTERVAL ticks after the last hit, apply damage + if (timeSinceHit > 0 && timeSinceHit % DRAIN_INTERVAL == 0) { + if (holder instanceof Player player) { + // Create a custom damage source that bypasses armor + DamageSource vampiricDrain = level.damageSources().magic(); + player.hurt(vampiricDrain, DRAIN_DAMAGE); + + // Visual feedback - dark red particles + if (!level.isClientSide()) { + Modifier.TrackEntityParticle(level, player, ParticleTypes.SOUL); + } + } + } + } + } + + @Override + public boolean compatible(Modifier mod) { + // Not compatible with Necrotic (Draining) to avoid lifesteal stacking + if (mod.tagName().equals("necrotic")) { + return false; + } + return true; + } +} diff --git a/src/main/resources/data/randomloot/recipe/trait_vampiric.json b/src/main/resources/data/randomloot/recipe/trait_vampiric.json new file mode 100644 index 0000000..29d5ec7 --- /dev/null +++ b/src/main/resources/data/randomloot/recipe/trait_vampiric.json @@ -0,0 +1,8 @@ +{ + "type": "randomloot:trait_change", + "item": { + "count": 1, + "id": "minecraft:fermented_spider_eye" + }, + "trait": "vampiric" +}