// [MC] Was part of P_Thing_Projectile, now its own function for use in ZScript. // Aims mobj at targ based on speed and targ's velocity. static void VelIntercept(AActor *targ, AActor *mobj, double speed, bool aimpitch = false, bool oldvel = false, bool leadtarget = true) { if (targ == nullptr || mobj == nullptr) return; DVector3 aim = mobj->Vec3To(targ); aim.Z += targ->Height / 2; if (leadtarget && speed > 0 && !targ->Vel.isZero()) { // Aiming at the target's position some time in the future // is basically just an application of the law of sines: // a/sin(A) = b/sin(B) // Thanks to all those on the notgod phorum for helping me // with the math. I don't think I would have thought of using // trig alone had I been left to solve it by myself. DVector3 tvel = targ->Vel; if (!(targ->flags & MF_NOGRAVITY) && targ->waterlevel < 3) { // If the target is subject to gravity and not underwater, // assume that it isn't moving vertically. Thanks to gravity, // even if we did consider the vertical component of the target's // velocity, we would still miss more often than not. tvel.Z = 0.0; if (targ->Vel.X == 0 && targ->Vel.Y == 0) { InterceptDefaultAim(mobj, targ, aim, speed); return; } } double dist = aim.Length(); double targspeed = tvel.Length(); double ydotx = -aim | tvel; double a = g_acos(clamp(ydotx / targspeed / dist, -1.0, 1.0)); double multiplier = double(pr_leadtarget.Random2())*0.1 / 255 + 1.1; double sinb = -clamp(targspeed*multiplier * g_sin(a) / speed, -1.0, 1.0); DVector3 prevel = mobj->Vel; // Use the cross product of two of the triangle's sides to get a // rotation vector. DVector3 rv(tvel ^ aim); // The vector must be normalized. rv.MakeUnit(); // Now combine the rotation vector with angle b to get a rotation matrix. DMatrix3x3 rm(rv, g_cos(g_asin(sinb)), sinb); // And multiply the original aim vector with the matrix to get a // new aim vector that leads the target. DVector3 aimvec = rm * aim; // And make the projectile follow that vector at the desired speed. mobj->Vel = aimvec * (speed / dist); mobj->AngleFromVel(); if (oldvel) { mobj->Vel = prevel; } if (aimpitch) // [MC] Ripped right out of A_FaceMovementDirection { const DVector2 velocity = mobj->Vel.XY(); mobj->Angles.Pitch = -VecToAngle(velocity.Length(), mobj->Vel.Z); } } else { InterceptDefaultAim(mobj, targ, aim, speed); } }
bool APathFollower::Interpolate () { DVector3 dpos(0, 0, 0); FLinkContext ctx; if ((args[2] & 8) && Time > 0.f) { dpos = Pos(); } if (CurrNode->Next==NULL) return false; UnlinkFromWorld (&ctx); DVector3 newpos; if (args[2] & 1) { // linear newpos.X = Lerp(CurrNode->X(), CurrNode->Next->X()); newpos.Y = Lerp(CurrNode->Y(), CurrNode->Next->Y()); newpos.Z = Lerp(CurrNode->Z(), CurrNode->Next->Z()); } else { // spline if (CurrNode->Next->Next==NULL) return false; newpos.X = Splerp(PrevNode->X(), CurrNode->X(), CurrNode->Next->X(), CurrNode->Next->Next->X()); newpos.Y = Splerp(PrevNode->Y(), CurrNode->Y(), CurrNode->Next->Y(), CurrNode->Next->Next->Y()); newpos.Z = Splerp(PrevNode->Z(), CurrNode->Z(), CurrNode->Next->Z(), CurrNode->Next->Next->Z()); } SetXYZ(newpos); LinkToWorld (&ctx); if (args[2] & 6) { if (args[2] & 8) { if (args[2] & 1) { // linear dpos.X = CurrNode->Next->X() - CurrNode->X(); dpos.Y = CurrNode->Next->Y() - CurrNode->Y(); dpos.Z = CurrNode->Next->Z() - CurrNode->Z(); } else if (Time > 0.f) { // spline dpos = newpos - dpos; } else { int realarg = args[2]; args[2] &= ~(2|4|8); Time += 0.1f; dpos = newpos; Interpolate (); Time -= 0.1f; args[2] = realarg; dpos = newpos - dpos; newpos -= dpos; SetXYZ(newpos); } if (args[2] & 2) { // adjust yaw Angles.Yaw = dpos.Angle(); } if (args[2] & 4) { // adjust pitch; use floats for precision double dist = dpos.XY().Length(); Angles.Pitch = dist != 0.f ? VecToAngle(dist, -dpos.Z) : 0.; } } else { if (args[2] & 2) { // interpolate angle DAngle angle1 = CurrNode->Angles.Yaw.Normalized180(); DAngle angle2 = angle1 + deltaangle(angle1, CurrNode->Next->Angles.Yaw); Angles.Yaw = Lerp(angle1.Degrees, angle2.Degrees); } if (args[2] & 1) { // linear if (args[2] & 4) { // interpolate pitch Angles.Pitch = Lerp(CurrNode->Angles.Pitch.Degrees, CurrNode->Next->Angles.Pitch.Degrees); } } else { // spline if (args[2] & 4) { // interpolate pitch Angles.Pitch = Splerp(PrevNode->Angles.Pitch.Degrees, CurrNode->Angles.Pitch.Degrees, CurrNode->Next->Angles.Pitch.Degrees, CurrNode->Next->Next->Angles.Pitch.Degrees); } } } } return true; }