/// <summary> /// Shifts the original target position in accordance with target leading, range estimation and weather/lighting effects /// </summary> protected virtual void ShiftTarget(ShiftVecReport report, bool calculateMechanicalOnly = false) { if (!calculateMechanicalOnly) { Vector3 u = caster.TrueCenter(); sourceLoc.Set(u.x, u.z); if (numShotsFired == 0) { // On first shot of burst do a range estimate estimatedTargDist = report.GetRandDist(); } Vector3 v = report.target.Thing?.TrueCenter() ?? report.target.Cell.ToVector3Shifted(); //report.targetPawn != null ? report.targetPawn.DrawPos + report.targetPawn.Drawer.leaner.LeanOffset * 0.5f : report.target.Cell.ToVector3Shifted(); if (report.targetPawn != null) { v += report.targetPawn.Drawer.leaner.LeanOffset * 0.5f; } newTargetLoc.Set(v.x, v.z); // ----------------------------------- STEP 1: Actual location + Shift for visibility //FIXME : GetRandCircularVec may be causing recoil to be unnoticeable - each next shot in the burst has a new random circular vector around the target. newTargetLoc += report.GetRandCircularVec(); // ----------------------------------- STEP 2: Estimated shot to hit location newTargetLoc = sourceLoc + (newTargetLoc - sourceLoc).normalized * estimatedTargDist; // Lead a moving target newTargetLoc += report.GetRandLeadVec(); // ----------------------------------- STEP 3: Recoil, Skewing, Skill checks, Cover calculations rotationDegrees = 0f; angleRadians = 0f; GetSwayVec(ref rotationDegrees, ref angleRadians); GetRecoilVec(ref rotationDegrees, ref angleRadians); // Height difference calculations for ShotAngle float targetHeight = 0f; var coverRange = new CollisionVertical(report.cover).HeightRange; //Get " " cover, assume it is the edifice // Projectiles with flyOverhead target the surface in front of the target if (Projectile.projectile.flyOverhead) { targetHeight = coverRange.max; } else { var victimVert = new CollisionVertical(currentTarget.Thing); var targetRange = victimVert.HeightRange; //Get lower and upper heights of the target /*if (currentTarget.Thing is Building && CompFireModes?.CurrentAimMode == AimMode.SuppressFire) * { * targetRange.min = targetRange.max; * targetRange.max = targetRange.min + 1f; * }*/ if (targetRange.min < coverRange.max) //Some part of the target is hidden behind some cover { // - It is possible for targetRange.max < coverRange.max, technically, in which case the shooter will never hit until the cover is gone. // - This should be checked for in LoS -NIA targetRange.min = coverRange.max; // Target fully hidden, shift aim upwards if we're doing suppressive fire if (targetRange.max <= coverRange.max && CompFireModes?.CurrentAimMode == AimMode.SuppressFire) { targetRange.max = coverRange.max * 2; } } else if (currentTarget.Thing is Pawn) { // Aim for center of mass on an exposed target targetRange.min = victimVert.BottomHeight; targetRange.max = victimVert.MiddleHeight; } targetHeight = VerbPropsCE.ignorePartialLoSBlocker ? 0 : targetRange.Average; } angleRadians += ProjectileCE.GetShotAngle(ShotSpeed, (newTargetLoc - sourceLoc).magnitude, targetHeight - ShotHeight, Projectile.projectile.flyOverhead, projectilePropsCE.Gravity); } // ----------------------------------- STEP 4: Mechanical variation // Get shotvariation, in angle Vector2 RADIANS. Vector2 spreadVec = report.GetRandSpreadVec(); // ----------------------------------- STEP 5: Finalization var w = (newTargetLoc - sourceLoc); shotRotation = (-90 + Mathf.Rad2Deg * Mathf.Atan2(w.y, w.x) + rotationDegrees + spreadVec.x) % 360; shotAngle = angleRadians + spreadVec.y * Mathf.Deg2Rad; }
/// <summary> /// Shifts the original target position in accordance with target leading, range estimation and weather/lighting effects /// </summary> protected virtual Vector3 ShiftTarget(ShiftVecReport report) { // ----------------------------------- STEP 0: Actual location Vector3 targetLoc = report.targetPawn != null?Vector3.Scale(report.targetPawn.DrawPos, new Vector3(1, 0, 1)) : report.target.Cell.ToVector3Shifted(); Vector3 sourceLoc = this.CasterPawn != null?Vector3.Scale(this.CasterPawn.DrawPos, new Vector3(1, 0, 1)) : this.caster.Position.ToVector3Shifted(); // ----------------------------------- STEP 1: Shift for visibility Vector2 circularShiftVec = report.GetRandCircularVec(); Vector3 newTargetLoc = targetLoc; newTargetLoc.x += circularShiftVec.x; newTargetLoc.z += circularShiftVec.y; // ----------------------------------- STEP 2: Estimated shot to hit location // On first shot of burst do a range estimate if (this.estimatedTargDist < 0) { this.estimatedTargDist = report.GetRandDist(); } newTargetLoc = sourceLoc + (newTargetLoc - sourceLoc).normalized * this.estimatedTargDist; // Lead a moving target newTargetLoc += report.GetRandLeadVec(); // ----------------------------------- STEP 3: Recoil, Skewing, Skill checks, Cover calculations Vector2 skewVec = new Vector2(0, 0); skewVec += this.GetSwayVec(); skewVec += this.GetRecoilVec(); // Height difference calculations for ShotAngle float heightDifference = 0; float targetableHeight = 0; // Projectiles with flyOverhead target the ground below the target and ignore cover if (!projectileDef.projectile.flyOverhead) { targetableHeight = CE_Utility.GetCollisionHeight(this.currentTarget.Thing); if (report.cover != null) { targetableHeight += CE_Utility.GetCollisionHeight(report.cover); } heightDifference += targetableHeight * 0.5f; //Optimal hit level is halfway } this.shotHeight = CE_Utility.GetCollisionHeight(this.caster); if (this.CasterPawn != null) { this.shotHeight *= shotHeightFactor; } heightDifference -= this.shotHeight; skewVec += new Vector2(0, GetShotAngle(this.shotSpeed, (newTargetLoc - sourceLoc).magnitude, heightDifference) * (180 / (float)Math.PI)); // ----------------------------------- STEP 4: Mechanical variation // Get shotvariation Vector2 spreadVec = report.GetRandSpreadVec(); skewVec += spreadVec; // Skewing - Applied after the leading calculations to not screw them up float distanceTraveled = GetDistanceTraveled(this.shotSpeed, (float)(skewVec.y * (Math.PI / 180)), this.shotHeight); newTargetLoc = sourceLoc + ((newTargetLoc - sourceLoc).normalized * distanceTraveled); newTargetLoc = sourceLoc + (Quaternion.AngleAxis(skewVec.x, Vector3.up) * (newTargetLoc - sourceLoc)); this.shotAngle = (float)(skewVec.y * (Math.PI / 180)); return(newTargetLoc); }