// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2022
//                www.magicbane.com


package engine.net.client.msg;


import engine.net.AbstractConnection;
import engine.net.ByteBufferReader;
import engine.net.ByteBufferWriter;
import engine.net.client.Protocol;
import engine.objects.AbstractCharacter;
import engine.objects.AbstractWorldObject;
import engine.objects.PlayerCharacter;

public class TargetedActionMsg extends ClientNetMsg {

    public static int un2cnt = 65;

    //attack animations
    //64: overhead RH swing											1h RH axe?
    //65: overhead LH swing
    //66: underhand RH uppercut
    //67: shoulder high RH swing
    //68: underhand RH swing
    //69: sweeping LH swing
    //70: overhead circle RH swing
    //71: RH across body and back swing
    //72: RH 1h overhead to 2h swing
    //73: RH overhead to cross body swing (bm?)
    //74: 2h low stab to cross body slash
    //75: unarmed punch												unarmed RH
    //76: unarmed RH punch LH punch
    //77: unarmed LH jab
    //78: unarmed LH jab, RH uppercut
    //79: kick
    //80: roundhouse kick
    //81: dagger RH stab											dagger RH
    //82: dagger LH stab
    //83: dagger slash
    //84: dagger hard stab
    //85: Polearm/staff overhead swing								Polearm, Staff
    //86: Polearm/staff side swing
    //87: Polearm/staff step into overhead swing
    //88: swinging RH stab
    //89: swinging LF stab
    //90: swinging RH stab (faster)
    //91: 1H slash across body and back (sword?)
    //92: spear stab												spear
    //93: spear low stab step into
    //94: spear swing leg stab
    //95: unarmed overhead swing RH, underhand swing LH
    //96: inverted weapon across body followed by roundhouse LH swing
    //97: step back followed by overhead roundhouse swing 2H
    //98: underhand slash (1h sword)								1H RH Sword
    //99: fast LH swing (dagger or sword?)
    //100: 1h swing RH (sword)										1h axe?
    //101: 1h overhead swing (club or sword)
    //102: fast 1h underhand swing (club or sword)
    //103: step into RH slash 1h
    //104: 1h overhead to cross body slash RH
    //105: 2h overhead swing (axe, hammer, sword)					2H Axe, Hammer, Sword
    //106: step into 2h swing
    //107: step int 2h overhead swing
    //108: step into 2h swing
    //109: bow draw and fire										bow
    //110: crossbow draw and fire									crossbow
    //115: throwing axe/hammer?
    //116: overhand throwing dagger?
    //117: throwing dagger

    private int sourceType;
    private int sourceID;
    private int targetType;
    private int targetID;
    private float locX;
    private float locZ;
    private int unknown01 = 14;
    private int unknown02 = 100; //source animation
    private float unknown03 = 1f;
    private float sourceStamina = 1f; // attackers stamina after attack
    private int unknown05 = 6;    //signify passive defense
    private int unknown06 = 10; //target animation
    private float newHealth = 10f; // health after damage
    private float damage = 0f; // damage, 0 for miss

    /**
     * This is the general purpose constructor.
     */
    public TargetedActionMsg(int sourceType, int sourceID, int targetType, int targetID, float locX, float locZ, float sourceStamina,
                             float newHealth, float damage) {
        super(Protocol.TARGETEDACTION);
        this.sourceType = sourceType;
        this.sourceID = sourceID;
        this.targetType = targetType;
        this.targetID = targetID;
        this.sourceStamina = sourceStamina;
        this.locX = locX;
        this.locZ = locZ;
        this.newHealth = newHealth;
        this.damage = damage;
        //this.unknown02 = TargetedActionMsg.un2cnt;
    }

    /**
     * This is a helper constructor. Designed to send an UPDATE only. Damage for
     * this constructor is hard coded to ZERO.
     */
    public TargetedActionMsg(PlayerCharacter pc) {
        super(Protocol.TARGETEDACTION);
        this.sourceType = pc.getObjectType().ordinal();
        this.sourceID = pc.getObjectUUID();
        this.targetType = pc.getObjectType().ordinal();
        this.targetID = pc.getObjectUUID();
        this.sourceStamina = pc.getStamina();
        this.locX = pc.getLoc().x;
        this.locZ = pc.getLoc().z;
        this.newHealth = pc.getCurrentHitpoints();
        this.damage = 0.0f;
    }

    public TargetedActionMsg(PlayerCharacter pc, int unknown06) {
        super(Protocol.TARGETEDACTION);
        this.sourceType = pc.getObjectType().ordinal();
        this.sourceID = pc.getObjectUUID();
        this.targetType = pc.getObjectType().ordinal();
        this.targetID = pc.getObjectUUID();
        this.sourceStamina = pc.getStamina();
        this.locX = pc.getLoc().x;
        this.locZ = pc.getLoc().z;
        this.newHealth = pc.getCurrentHitpoints();
        this.damage = 0.0f;
    }

    public TargetedActionMsg(AbstractCharacter source, AbstractWorldObject target, Float damage, int swingAnimation) {
        super(Protocol.TARGETEDACTION);
        if (source != null) {
            this.sourceType = source.getObjectType().ordinal();
            this.sourceID = source.getObjectUUID();
            this.sourceStamina = source.getStamina();
        } else {
            this.sourceType = 0;
            this.sourceID = 0;
            this.sourceStamina = 0;
        }
        if (target != null) {
            this.targetType = target.getObjectType().ordinal();
            this.targetID = target.getObjectUUID();
            this.locX = target.getLoc().x;
            this.locZ = target.getLoc().z;
            this.newHealth = target.getHealth();
            this.damage = damage;

        } else {
            this.targetType = 0;
            this.targetID = 0;
            this.locX = 50000f;
            this.locZ = -50000f;
            this.newHealth = 1f;
            this.damage = damage;
        }
        this.unknown02 = swingAnimation;
        //this.unknown02 = TargetedActionMsg.un2cnt;
    }

    public TargetedActionMsg(AbstractCharacter source, AbstractWorldObject target, Float damage, int swingAnimation, int dead) {
        super(Protocol.TARGETEDACTION);
        if (source != null) {
            this.sourceType = source.getObjectType().ordinal();
            this.sourceID = source.getObjectUUID();
            this.sourceStamina = source.getStamina();
        } else {
            this.sourceType = 0;
            this.sourceID = 0;
            this.sourceStamina = 0;
        }
        if (target != null) {
            this.targetType = target.getObjectType().ordinal();
            this.targetID = target.getObjectUUID();
            this.locX = target.getLoc().x;
            this.locZ = target.getLoc().z;
            this.newHealth = target.getCurrentHitpoints();
            this.damage = damage;
            this.unknown06 = dead;

        } else {
            this.targetType = 0;
            this.targetID = 0;
            this.locX = 50000f;
            this.locZ = -50000f;
            this.newHealth = 1f;
            this.damage = damage;
        }
        this.unknown02 = swingAnimation;
        //this.unknown02 = TargetedActionMsg.un2cnt;
    }

    /**
     * Added in an attempt to have mobs fall over after death.
     */

    public TargetedActionMsg(AbstractWorldObject target, boolean kill) {
        this(null, target, 0f, 75);
        if (kill) {
            this.newHealth = -1f;
            this.sourceType = 0x101;
            this.sourceID = 0x101;
        }
    }

    /**
     * This constructor can be used to create CombatMessages that indicate a block or parry has occurred.<br>
     * <br>
     * Set passiveAnimation to 21 for block.<br>
     * Set passiveAnimation to 22 for parry.<br>
     */
    public TargetedActionMsg(AbstractCharacter source, int swingAnimation, AbstractWorldObject target, int passiveAnimation) {
        this(source, target, 0.0f, swingAnimation);
        this.unknown05 = passiveAnimation;
        this.unknown06 = 0;
    }

    /**
     * This constructor is used by NetMsgFactory. It attempts to deserialize the
     * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading
     * past the limit) then this constructor Throws that Exception to the
     * caller.
     */
    public TargetedActionMsg(AbstractConnection origin, ByteBufferReader reader) {
        super(Protocol.TARGETEDACTION, origin, reader);
    }

    /**
     * Serializes the subclass specific items to the supplied NetMsgWriter.
     */
    @Override
    protected void _serialize(ByteBufferWriter writer) {
        writer.putInt(this.sourceType);
        writer.putInt(this.sourceID);
        writer.putInt(this.targetType);
        writer.putInt(this.targetID);
        writer.putFloat(this.locX);
        writer.putFloat(this.locZ);
        writer.putInt(this.unknown01);
        writer.putInt(this.unknown02);
        writer.putFloat(this.unknown03);
        writer.putFloat(this.sourceStamina);
        writer.putInt(this.unknown05);
        // writer.putInt(this.unknown06);

        if (this.newHealth < 0)
            writer.putInt(55);
        else if (damage != 0 && this.unknown05 < 20)
            writer.putInt(60);
        else
            writer.putInt(this.unknown06);
        writer.putFloat(this.newHealth);
        writer.putFloat(this.damage);
    }

    /**
     * Deserializes the subclass specific items from the supplied NetMsgReader.
     */
    @Override
    protected void _deserialize(ByteBufferReader reader) {
        this.sourceType = reader.getInt();
        this.sourceID = reader.getInt();
        this.targetType = reader.getInt();
        this.targetID = reader.getInt();
        this.locX = reader.getFloat();
        this.locZ = reader.getFloat();
        this.unknown01 = reader.getInt();
        this.unknown02 = reader.getInt();
        this.unknown03 = reader.getFloat();
        this.sourceStamina = reader.getFloat();
        this.unknown05 = reader.getInt();
        this.unknown06 = reader.getInt();
        this.newHealth = reader.getFloat();
        this.damage = reader.getFloat();
    }

    /**
     * @return the sourceType
     */
    public int getSourceType() {
        return sourceType;
    }

    public void setSourceType(int value) {
        this.sourceType = value;
    }

    /**
     * @return the sourceID
     */
    public int getSourceID() {
        return sourceID;
    }

    public void setSourceID(int value) {
        this.sourceID = value;
    }

    /**
     * @return the targetType
     */
    public int getTargetType() {
        return targetType;
    }

    public void setTargetType(int value) {
        this.targetType = value;
    }

    /**
     * @return the targetID
     */
    public int getTargetID() {
        return targetID;
    }

    public void setTargetID(int value) {
        this.targetID = value;
    }

    public float getDamage() {
        return damage;
    }
}
