Author Topic: Player::getDamageLocation  (Read 1806 times)

I feel like I remember this being done before in what is probably a more efficient method but I figured I'd post this here regardless for feedback or something. Some of the code is adapted from weapon_chainsaw

Code: [Select]
function vectorClosestPointAngle(%vector, %startPoint, %point)
{
%vN = vectorNormalize(%vector);
%v2 = vectorSub(%point, %startPoint);
%dot = vectorDot(%vN, vectorNormalize(%v2));

%closestPoint = vectorAdd(%startPoint, vectorScale(%vN, %dot * vectorLen(%v2)));
%angle = mACos(%dot);

return %closestPoint SPC %angle;
}
function Player::getDamageLocation(%obj, %position)
{
if(%obj.getDatablock().shapeFile !$= "base/data/shapes/player/m.dts")
return 0;

// Some information about who we just shot
%scale = getWord(%obj.getScale(), 2);

// vectors
%forwardVector = %obj.getForwardVector();   // direction the bot is facing
%sideVector = vectorCross(%forwardVector, %obj.getUpVector());  // vector facing directly to the sides of the bot

// points
%centerPoint = %obj.getWorldBoxCenter(); // middle reference point
%CPR = vectorClosestPointAngle(%sideVector, %centerPoint, %position); // where we are on the side-to-side vector
%closestPoint = getWords(%CPR, 0, 2); // closest point to where our round hit on the side-to-side vector
%angle = mRadToDeg(getWord(%CPR, 3)); // angle from the reference point. 90 is center, 0 is right, 180 is left.
%distanceFromCenter = vectorDist(%closestPoint, %centerPoint); // distance from the reference point to the side-to-side vector. 0 is center, anything above .4 is an arm.

%zLocation = getWord(%position, 2); // Z position of our impact
%zWorldBox = getword(%obj.getWorldBoxCenter(), 2); // Where the middle of our player is on the z axis

if(%zLocation > %zWorldBox - 3.3 * %scale)
{
return "head";
}
else if(%zLocation > %zWorldBox - 4.5 * %scale)
{
if(%distanceFromCenter < (0.4 * %scale))
{
return "torso";
}
else
{
if(%angle < 90)
{
return "rarm";
}
else
{
return "larm";
}
}
}
else
{
if(%angle < 90)
{
return "rleg";
}
else
{
return "lleg";
}
}
}

Essentially you just call %player.getDamageLocation(%pos);, with %pos referring to where the bullet hit, and it returns either head, torso, rarm, larm, rleg or lleg depending on where it hit

the guy that made slayer made the same thing also

there should probably be a return parameter for the yaw axis so you can get whether a player was hit from the front or the back

this is some top tier utility stuff rally

this is some top tier utility stuff rally

Is it? Glad to hear it lol. I figured we'd probably already had stuff like this.

the guy that made slayer made the same thing also

Do you have the code on-hand? I'd like to reference it

there should probably be a return parameter for the yaw axis so you can get whether a player was hit from the front or the back

Good idea actually. Could probably do it in the same manner as getting the side-to-side position, but just doing it with the forward vector instead.

There was an old mod by Jookia (don't remember what) that ran each projectile hit through paintProjectile::onCollision, which calls ShapeBase::setTempColor, and it compared node colors before and after to determine what area was hit. It's clever, but limited by the fact setTempColor isn't as specific (i.e, left leg and right leg aren't unique).

The two other "hit region" mods I can think of are this and a random script by Port but both are fairly similar to what you made.

As far as a better solution goes, the "correct" way to do hit boxes/regions is by converting the world coordinates of an impact back to the target's model coordinates. To do this just do the reverse of what projection does in rasterization. An object's model coordinates are converted to world space by multiplying them by the object's transform (getTransform()), so to reverse this for a projectile impact in world space, you multiply that point by the inverse of the object's transform. I don't see any implementations of a matrix inverse function out there (besides ones that translate the homogeneous coordinates back to a 4x4 matrix, which is messy) so here's that:

Code: [Select]
function MatrixInverse(%m)
{
    %inv_rot = vectorScale(getWords(%m, 3, 5), -1) SPC getWord(%m, 6);
    %inv_pos_mat = MatrixMultiply("0 0 0" SPC %inv_rot, %m);
   
    return vectorScale(getWords(%inv_pos_mat, 0, 2), -1) SPC %inv_rot;
}

Then you simply do MatrixMulPoint(%inverse, %impact) and write some fun "is point in box" code. An easy way to get the boxes would be loading up a properly scaled player model in blender, since that's the model view!

I think ny old Remmington 700 and Grunt Birthday Skull-esque death mods use Jookia's script to locate the head

It seemed kinda messy, but it worked, guess i now have a reason to go back and update them

Also, is it possible to detect the hands, feet, and pants using this method?

Also, is it possible to detect the hands, feet, and pants using this method?

Pants would be easy, hands not so much. For the pants you'd just have to find the location on the z axis where it goes from torso to pants, and then adjust for player scale.

The problem with hands is that they are often animated, but the collision box is always the same. At that point, you'd have to check for the position's location on the forward-back vector (to determine if they are hitting the back or front of a raised arm) and also find out if that arm is raised.