diff --git a/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java index 40799135..cd376305 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java @@ -78,13 +78,13 @@ public class BatEeeeAbility implements Ability { Vec3d origin = player.getOriginVector(); if (rng.nextInt(20000) == 0) { - player.getMaster().damage(MagicalDamageSource.create("eeee", player.getMaster()), 0.1F); + player.getMaster().damage(MagicalDamageSource.create("eeee", player), 0.1F); } player.findAllEntitiesInRange(5).forEach(e -> { if (e instanceof LivingEntity && !HAS_SHIELD.test(e)) { boolean isEarthPony = EquinePredicates.PLAYER_EARTH.test(e); - e.damage(MagicalDamageSource.create("eeee", player.getMaster()), isEarthPony ? 0.1F : 0.3F); + e.damage(MagicalDamageSource.create("eeee", player), isEarthPony ? 0.1F : 0.3F); Vec3d knockVec = origin.subtract(e.getPos()); ((LivingEntity) e).takeKnockback(isEarthPony ? 0.3F : 0.5F, knockVec.getX(), knockVec.getZ()); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java index 4e345f09..b664b3e7 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java @@ -108,7 +108,7 @@ public class EarthPonyStompAbility implements Ability { -(player.getY() - i.getY() - liftAmount) / inertia + (dist < 1 ? dist : 0), -(player.getZ() - i.getZ()) / inertia); - DamageSource damage = MagicalDamageSource.create("smash", player); + DamageSource damage = MagicalDamageSource.create("smash", iplayer); double amount = (1.5F * player.getAttributeInstance(EntityAttributes.GENERIC_ATTACK_DAMAGE).getValue() + heavyness * 0.4) / (float)(dist * 1.3F); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/InfernoSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/InfernoSpell.java index 10535113..857bc824 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/InfernoSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/InfernoSpell.java @@ -61,7 +61,7 @@ public class InfernoSpell extends FireSpell { @Override protected DamageSource getDamageCause(Entity target, LivingEntity attacker) { if (attacker != null && attacker.getUuid().equals(target.getUuid())) { - return MagicalDamageSource.create("fire.own", null); + return MagicalDamageSource.create("fire.own"); } return MagicalDamageSource.create("fire", attacker); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/JoustingSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/JoustingSpell.java index 6a277647..0ba94b2c 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/JoustingSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/JoustingSpell.java @@ -51,7 +51,7 @@ public class JoustingSpell extends AbstractSpell implements Attached { LivingEntity owner = source.getMaster(); source.findAllEntitiesInRange(rad).forEach(e -> { - e.damage(MagicalDamageSource.create("rainboom", owner), 6); + e.damage(MagicalDamageSource.create("rainboom", source), 6); }); PosHelper.getAllInRegionMutable(source.getOrigin(), effect_range).forEach(pos -> { BlockState state = source.getWorld().getBlockState(pos); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SiphoningSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SiphoningSpell.java index c9b6711c..ac018c3a 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SiphoningSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SiphoningSpell.java @@ -76,8 +76,7 @@ public class SiphoningSpell extends AbstractPlacedSpell { } private void distributeHealth(Caster source) { - LivingEntity owner = source.getMaster(); - DamageSource damage = MagicalDamageSource.create("drain", owner); + DamageSource damage = MagicalDamageSource.create("drain", source); getTargets(source).forEach(e -> { float maxHealthGain = e.getMaxHealth() - e.getHealth(); @@ -114,7 +113,7 @@ public class SiphoningSpell extends AbstractPlacedSpell { float attackAmount = Math.max(maxHealthGain / targets.size(), 0.5F); - DamageSource damage = MagicalDamageSource.create("drain", owner); + DamageSource damage = MagicalDamageSource.create("drain", source); float healthGain = 0; diff --git a/src/main/java/com/minelittlepony/unicopia/entity/CastSpellEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/CastSpellEntity.java index 4db5d4b5..1e13de21 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/CastSpellEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/CastSpellEntity.java @@ -21,6 +21,8 @@ import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.Packet; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -51,6 +53,15 @@ public class CastSpellEntity extends Entity implements Caster { getDataTracker().startTracking(SPELL, Optional.empty()); } + @Override + public Text getName() { + Entity master = getMaster(); + if (master != null) { + return new TranslatableText("entity.unicopia.cast_spell.by", master.getName()); + } + return super.getName(); + } + @Override public void tick() { super.tick(); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Equine.java b/src/main/java/com/minelittlepony/unicopia/entity/Equine.java index c4976927..e859714f 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Equine.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Equine.java @@ -20,6 +20,11 @@ public interface Equine extends NbtSerialisable, Tickable, Pro void setSpecies(Race race); + /** + * Gets the last magical entity to attack us. + */ + Entity getAttacker(); + /** * Returns true if this player is fully invisible. * Held items and other effects will be hidden as well. diff --git a/src/main/java/com/minelittlepony/unicopia/entity/ItemImpl.java b/src/main/java/com/minelittlepony/unicopia/entity/ItemImpl.java index a8657377..894f6fee 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/ItemImpl.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/ItemImpl.java @@ -41,6 +41,11 @@ public class ItemImpl implements Equine, Owned { owner.getDataTracker().startTracking(ITEM_RACE, Race.HUMAN.ordinal()); } + @Override + public Entity getAttacker() { + return null; + } + @Override public boolean beforeUpdate() { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java index ebe0b972..37382f28 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java @@ -12,7 +12,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.SpellType; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.network.EffectSync; import com.minelittlepony.unicopia.projectile.ProjectileImpactListener; +import com.minelittlepony.unicopia.util.MagicalDamageSource; +import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.data.TrackedData; @@ -33,6 +35,9 @@ public abstract class Living implements Equine, Caste @Nullable private Runnable landEvent; + @Nullable + private Entity attacker; + private int invinsibilityTicks; private final Enchantments enchants = new Enchantments(this); @@ -108,6 +113,12 @@ public abstract class Living implements Equine, Caste } } + @Nullable + @Override + public final Entity getAttacker() { + return attacker; + } + @Override public Optional onDamage(DamageSource source, float amount) { @@ -117,6 +128,13 @@ public abstract class Living implements Equine, Caste } } + if (source instanceof MagicalDamageSource) { + Entity attacker = ((MagicalDamageSource)source).getSpell(); + if (attacker != null) { + this.attacker = attacker; + } + } + return Optional.empty(); } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinDamageSource.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinDamageSource.java new file mode 100644 index 00000000..f6bd3c41 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinDamageSource.java @@ -0,0 +1,37 @@ +package com.minelittlepony.unicopia.mixin; + +import javax.annotation.Nullable; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.minelittlepony.unicopia.Owned; +import com.minelittlepony.unicopia.entity.Equine; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; + +@Mixin(DamageSource.class) +abstract class MixinDamageSource { + @Inject(method = "getDeathMessage", at = @At("RETURN"), cancellable = true) + private void onGetDeathMessage(LivingEntity entity, CallbackInfoReturnable info) { + Equine eq = Equine.of(entity); + @Nullable + Entity attacker = eq == null ? null : eq.getAttacker(); + DamageSource self = (DamageSource)(Object)this; + + if (attacker != null) { + Entity prime = entity.getPrimeAdversary(); + if (prime != null && !(attacker instanceof Owned && ((Owned)attacker).getMaster() == prime)) { + info.setReturnValue(new TranslatableText("death.attack.generic.and_also", info.getReturnValue(), attacker.getDisplayName())); + return; + } + + info.setReturnValue(new TranslatableText("death.attack." + self.getName() + ".player", entity.getDisplayName(), attacker.getDisplayName())); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/util/MagicalDamageSource.java b/src/main/java/com/minelittlepony/unicopia/util/MagicalDamageSource.java index 0f7f7fa9..331a6248 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/MagicalDamageSource.java +++ b/src/main/java/com/minelittlepony/unicopia/util/MagicalDamageSource.java @@ -5,6 +5,8 @@ import java.util.List; import javax.annotation.Nullable; +import com.minelittlepony.unicopia.ability.magic.Caster; + import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.damage.DamageSource; @@ -15,7 +17,7 @@ import net.minecraft.text.TranslatableText; public class MagicalDamageSource extends EntityDamageSource { - public static final DamageSource EXHAUSTION = new MagicalDamageSource("magical_exhaustion", true, true); + public static final DamageSource EXHAUSTION = new MagicalDamageSource("magical_exhaustion", null, true, true); public static final DamageSource FOOD_POISONING = mundane("food_poisoning"); public static final DamageSource TRIBE_SWAP = mundane("tribe_swap"); public static final DamageSource ZAP_APPLE = create("zap"); @@ -25,19 +27,26 @@ public class MagicalDamageSource extends EntityDamageSource { } public static DamageSource create(String type) { - return new MagicalDamageSource(type, false, false); + return new MagicalDamageSource(type, null, null, false, false); } public static DamageSource create(String type, LivingEntity source) { - return new MagicalDamageSource(type, source, false, false); + return new MagicalDamageSource(type, source, null, false, false); } - protected MagicalDamageSource(String type, boolean direct, boolean unblockable) { - this(type, null, direct, unblockable); + public static DamageSource create(String type, Caster caster) { + return new MagicalDamageSource(type, caster.getMaster(), caster.getEntity(), false, false); } - protected MagicalDamageSource(String type, @Nullable Entity source, boolean direct, boolean unblockable) { + private Entity spell; + + protected MagicalDamageSource(String type, @Nullable Entity spell, boolean direct, boolean unblockable) { + this(type, null, spell, direct, unblockable); + } + + protected MagicalDamageSource(String type, @Nullable Entity source, @Nullable Entity spell, boolean direct, boolean unblockable) { super(type, source); + this.spell = spell; setUsesMagic(); if (direct) { setBypassesArmor(); @@ -47,6 +56,11 @@ public class MagicalDamageSource extends EntityDamageSource { } } + @Nullable + public Entity getSpell() { + return spell; + } + @Override public Text getDeathMessage(LivingEntity target) { @@ -56,21 +70,22 @@ public class MagicalDamageSource extends EntityDamageSource { params.add(target.getDisplayName()); @Nullable - Entity attacker = source instanceof LivingEntity ? source : target.getPrimeAdversary(); + Entity attacker = source != null ? source : target.getPrimeAdversary(); - if (attacker instanceof LivingEntity) { + if (attacker != null) { if (attacker == target) { basic += ".self"; } else { basic += ".attacker"; - params.add(((LivingEntity)attacker).getDisplayName()); + params.add(attacker.getDisplayName()); } + } - ItemStack item = ((LivingEntity)attacker).getMainHandStack(); - if (!item.isEmpty() && item.hasCustomName()) { - basic += ".item"; - params.add(item.toHoverableText()); - } + ItemStack item = attacker instanceof LivingEntity ? ((LivingEntity)attacker).getMainHandStack() : ItemStack.EMPTY; + + if (!item.isEmpty() && item.hasCustomName()) { + basic += ".item"; + params.add(item.toHoverableText()); } return new TranslatableText(basic, params.toArray()); diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 49f8fbad..e3c401f7 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -52,6 +52,9 @@ "item.unicopia.music_disc_funk": "Music Disc", "item.unicopia.music_disc_funk.desc": "funk, just funk", + "entity.unicopia.cast_spell": "Cast Spell", + "entity.unicopia.cast_spell.by": "a spell cast by %s", + "unicopia.effect.tribe.stage.initial": "It appears to have no effect.", "unicopia.effect.tribe.stage.crawling": "You feel the skin crawling on your back.", "unicopia.effect.tribe.stage.determination": "As your bones realign you are filled determination.", @@ -272,12 +275,16 @@ "unicopia.race.bat": "Bat Pony", "unicopia.race.bat.alt": "Bat Ponies", + "death.attack.generic.and_also": "%1$s and %2$s", "death.attack.tribe_swap": "%1$s was reborn into a different tribe", "death.attack.magical_exhaustion": "%1$s exhausted themselves", "death.attack.alicorn_amulet": "%1$s was driven insane", "death.attack.darkness": "%1$s went missing", "death.attack.feed": "%1$s was drained of all life", "death.attack.feed.attacker": "%1$s died to feed %2$s", + "death.attack.drain": "%1$s was drained of all life", + "death.attack.drain.self": "%1$s was killed by their own spell", + "death.attack.drain.attacker": "%1$s was killed by a spell cast by %2$s", "death.attack.eeee": "%1$s was firghtened to death", "death.attack.eeee.attacker": "%2$s scared %1$s", "death.attack.eeee.attacker.item": "%1$s was frightened to death by %2$s using %3$s", diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json index 7cfbfd9d..c088db71 100644 --- a/src/main/resources/unicopia.mixin.json +++ b/src/main/resources/unicopia.mixin.json @@ -8,6 +8,7 @@ "MixinBlockEntity", "MixinBlockItem", "MixinBrain", + "MixinDamageSource", "MixinFallingBlock", "MixinItem", "MixinItemEntity",