MixinLivingEntity.java 5 KB
Newer Older
1 2
package com.owlmaddie.mixin;

3
import com.owlmaddie.chat.ChatDataManager;
4
import com.owlmaddie.chat.EntityChatData;
5
import com.owlmaddie.chat.PlayerData;
6 7
import com.owlmaddie.network.ServerPackets;
import net.minecraft.entity.Entity;
8
import net.minecraft.entity.LivingEntity;
9 10
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.mob.MobEntity;
11
import net.minecraft.entity.passive.TameableEntity;
12
import net.minecraft.entity.player.PlayerEntity;
13 14
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity;
15 16
import net.minecraft.text.Text;
import net.minecraft.world.World;
17 18 19
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
20
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
21 22
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

23 24 25 26 27
/**
 * The {@code MixinLivingEntity} class modifies the behavior of {@link LivingEntity} to integrate
 * custom friendship, chat, and death message mechanics. It prevents friendly entities from targeting players,
 * generates contextual chat messages on attacks, and broadcasts custom death messages for named entities.
 */
28
@Mixin(LivingEntity.class)
29 30
public class MixinLivingEntity {

31
    private EntityChatData getChatData(LivingEntity entity) {
32
        ChatDataManager chatDataManager = ChatDataManager.getServerInstance();
33
        return chatDataManager.getOrCreateChatData(entity.getUuidAsString());
34
    }
35 36 37

    @Inject(method = "canTarget(Lnet/minecraft/entity/LivingEntity;)Z", at = @At("HEAD"), cancellable = true)
    private void modifyCanTarget(LivingEntity target, CallbackInfoReturnable<Boolean> cir) {
38 39
        if (target instanceof PlayerEntity) {
            LivingEntity thisEntity = (LivingEntity) (Object) this;
40
            EntityChatData entityData = getChatData(thisEntity);
41
            PlayerData playerData = entityData.getPlayerData(target.getDisplayName().getString());
42
            if (playerData.friendship > 0) {
43 44 45
                // Friendly creatures can't target a player
                cir.setReturnValue(false);
            }
46 47 48
        }
    }

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
    @Inject(method = "damage", at = @At(value = "RETURN"))
    private void onDamage(DamageSource source, float amount, CallbackInfoReturnable<Boolean> cir) {
        if (!cir.getReturnValue()) {
            // If damage method returned false, it means the damage was not applied (possibly due to invulnerability).
            return;
        }

        // Get attacker and entity objects
        Entity attacker = source.getAttacker();
        LivingEntity thisEntity = (LivingEntity) (Object) this;

        // If PLAYER attacks MOB then
        if (attacker instanceof PlayerEntity && thisEntity instanceof MobEntity && !thisEntity.isDead()) {
            // Generate attacked message (only if the previous user message was not an attacked message)
            // We don't want to constantly generate messages during a prolonged, multi-damage event
64
            ServerPlayerEntity player = (ServerPlayerEntity) attacker;
65
            EntityChatData chatData = getChatData(thisEntity);
66
            if (!chatData.characterSheet.isEmpty() && chatData.auto_generated < ChatDataManager.MAX_AUTOGENERATE_RESPONSES) {
67 68 69 70 71 72 73 74 75 76
                // Only auto-generate a response to being attacked if chat data already exists
                // and this is the first attack event.
                ItemStack weapon = player.getMainHandStack();
                String weaponName = weapon.isEmpty() ? "with fists" : "with " + weapon.getItem().toString();

                // Determine if the damage was indirect
                boolean isIndirect = source.isIndirect();
                String directness = isIndirect ? "indirectly" : "directly";

                String attackedMessage = "<" + player.getName().getString() + " attacked you " + directness + " with " + weaponName + ">";
77
                ServerPackets.generate_chat("N/A", chatData, player, (MobEntity) thisEntity, attackedMessage, true);
78 79 80 81
            }
        }
    }

82 83 84 85 86 87
    @Inject(method = "onDeath", at = @At("HEAD"))
    private void onDeath(DamageSource source, CallbackInfo info) {
        LivingEntity entity = (LivingEntity) (Object) this;
        World world = entity.getWorld();

        if (!world.isClient() && entity.hasCustomName()) {
88 89 90 91 92 93 94 95 96
            // Skip tamed entities and players
            if (entity instanceof TameableEntity && ((TameableEntity) entity).isTamed()) {
                return;
            }

            if (entity instanceof PlayerEntity) {
                return;
            }

97 98 99 100 101 102 103 104
            // Get chatData for the entity
            EntityChatData chatData = getChatData(entity);
            if (chatData != null && !chatData.characterSheet.isEmpty()) {
                // Get the original death message
                Text deathMessage = entity.getDamageTracker().getDeathMessage();
                // Broadcast the death message to all players in the world
                ServerPackets.BroadcastMessage(deathMessage);
            }
105 106
        }
    }
107
}