What Is This?

This is a look at how you can access target data from invector at runtime when a damage event is received.

For all of the following examples the function OnDamaged(vDamage damage) will be used. This function can be tied into invector events as you need. Basically any invector delegate or event that exposes vDamage in someway will do. It will always be referenced as damage.

Getting Weapon ID

Lets say you wanted to get the ID of the weapon that delivered the killing hit:

int weapon_id = -1; // -1 = fists/unarmed attack
if (damage.sender.root.GetComponent<vShooterManager>() && 
damage.sender.root.GetComponent<vShooterManager>().CurrentWeapon)
{
    weapon_id = damage.sender.root.GetComponent<vShooterManager>().CurrentWeapon.GetComponent<vShooterEquipment>().referenceItem.id;
}
else if (damage.sender.root.GetComponent<vMeleeManager>() && 
damage.sender.root.GetComponent<vMeleeManager>().leftWeapon)
{
    weapon_id = damage.sender.root.GetComponent<vMeleeManager>().leftWeapon.GetComponent<vMeleeEquipment>().referenceItem.id;
}
else if (damage.sender.root.GetComponent<vMeleeManager>() && 
damage.sender.root.GetComponent<vMeleeManager>().rightWeapon)
{
    weapon_id = damage.sender.root.GetComponent<vMeleeManager>().rightWeapon.GetComponent<vMeleeEquipment>().referenceItem.id;
}

Get The Client Connection For Sender/Receiver

ClientConnection senderClientConn = ClientUtil.GetClientConnection(damage.sender.root.gameObject);
ClientConnection receiverClientConn = ClientUtil.GetClientConnection(damage.receiver.root.gameObject);

Available Data In Client Connection

This isn't a complete list of things exposed by the ClientConnection look at the EMI docs (not UI docs) for more details.

ClientConnection senderClientConn = ClientUtil.GetClientConnection(damage.sender.root.gameObject);
int clientConnection = senderClientConn.connId;
string playerName = senderClientConn.playerName;
bool currentlyDead = senderClientConn.isDead;
int senderHealth = senderClientConn.playerHealth;
int senderMaxHealth = senderClientConn.playerMaxHealth;

Getting Damage Sender Total Kills

If you want to extract the total number of current kills the damage sender has you can do it like the following

NOTE: The following example requires you to use the KillCounterUI component in your game

ClientConnection clientConn = ClientUtil.GetClientConnection(damage.sender.root.gameObject);
int senderKills = KillCounterUI.instance.killCounter[clientConn.connId];

Detecting If It Was A Headshot

If you want to know if it was a headshot you can check the name of the object. Optionally this can also be done by tagging the head or changing its layer and checking it. In the following example we simply check the objects name:

Option 1.

bool wasHeadshot = (damage.receiver.name == "Head");

Option 2.

bool wasHeadshot = (damage.receiver.root.GetComponent<Animator>().GetBoneTransform(HumanBodyBones.Head).Equals(damage.receiver))

Converting Hit Body Parts To IDs

If you want to keep track of each body part and how many times it has been hit, you can do it like the following:

Option 1. Use the transform names

int body_part_id = -1; // start off undefined
if (damage.receiver.name.Contains("RightArm"))
{
    body_part_id = 2;
}
else if (damage.receiver.name.Contains("LeftLeg"))
{
    body_part_id = 3;
}
...
...
return body_part_id;

Option 2. Make a wrapper function to compare the actual GameObject

public static HumanBodyBones TransformToBone(this Transform input)
{
    Animator anim = input.transform.root.GetComponent<Animator>();
    if (input.Equals(anim.GetBoneTransform(HumanBodyBones.Chest)))
    {
        return HumanBodyBones.Chest;
    }
    else if ...
    ...
    ...
    return HumanBodyBones.LeftLittleDistal;
}

Then use it:

HumanBodyBone bone = damage.receiver.TransformToBone();
switch(bone)
{
    case HumanBodyBone.Chest:
        return 1;
        break;
    ...
    ...
}

Storing Number Of Hits

You can keep a component that will keep track of how many times each body part might have been hit like the following (could even reset it on death - or whatever your needs might be). Do the setup for Converting Hit Body Parts To IDs then just keep track of it:

Dictionary<int, int> bone_to_hits = new Dictionary<int, int>();
(Should probably populate the dictionary before hand with bone ids and values starting at zero)
...
...
HumanBodyBone bone = damage.receiver.TransformToBone();
switch(bone)
{
    case HumanBodyBone.Chest:
        bone_to_hits[1] += 1;
        break;
    ...
    ...
}

Generating Custom Damage History

You can combine everything that was learned above to generate your own custom damage history list. It's best to make sure this doesn't get out of hand so probably limit the size of the list to make sure it doesn't ever get too big.

public struct DamageObject
{
    public string sender_name;
    public int weapon_id;
    public bool headshot;
    public int damage_amount;
    public string damage_type;
    public int hit_bone_id;
    public Double network_time;
}
protected List<DamageObject> hit_history = new List<DamageObject>();
...
...
protected virtual void OnDamaged(vDamage damage)
{
    int weapon_id = -1; // -1 = fists/unarmed attack
    if (damage.sender.root.GetComponent<vShooterManager>() && 
    damage.sender.root.GetComponent<vShooterManager>().CurrentWeapon)
    {
        weapon_id = damage.sender.root.GetComponent<vShooterManager>().CurrentWeapon.GetComponent<vShooterEquipment>().referenceItem.id;
    }
    else if (damage.sender.root.GetComponent<vMeleeManager>() && 
    damage.sender.root.GetComponent<vMeleeManager>().leftWeapon)
    {
        weapon_id = damage.sender.root.GetComponent<vMeleeManager>().leftWeapon.GetComponent<vMeleeEquipment>().referenceItem.id;
    }
    else if (damage.sender.root.GetComponent<vMeleeManager>() && 
    damage.sender.root.GetComponent<vMeleeManager>().rightWeapon)
    {
        weapon_id = damage.sender.root.GetComponent<vMeleeManager>().rightWeapon.GetComponent<vMeleeEquipment>().referenceItem.id;
    }

    HumanBodyBone bone = damage.receiver.TransformToBone();

    string sender_name = "Undefined";
    if (damage.sender != null)
    {
        ClientConnection conn = ClientUtil.GetClientConnection(damage.sender.root.gameObject);
        if (conn != null)
            sender_name = conn.playerName;
    }

    hit_history.Add(new DamageObject() {
        sender_name = sender_name,
        weapon_id = weapon_id,
        headshot = (bone == HumanBodyBone.Head),
        damage_amount = damage.damageValue,
        damage_type = damage.damageType,
        hit_bone_id = (int)bone,
        network_time = NetworkTime.time
    });

    if (hit_history.Count > 1000)
        hit_history.RemoveAt(0);
}