/// <summary> /// Checks if the shooter can hit the target from a certain position with regards to cover height /// </summary> /// <param name="root">The position from which to check</param> /// <param name="targ">The target to check for line of sight</param> /// <returns>True if shooter can hit target from root position, false otherwise</returns> public override bool CanHitTargetFrom(IntVec3 root, LocalTargetInfo targ) { //Sanity check for flyOverhead projectiles, they should not attack things under thick roofs if (projectileDef.projectile.flyOverhead) { RoofDef roofDef = caster.Map.roofGrid.RoofAt(targ.Cell); if (roofDef != null && roofDef.isThickRoof) { return(false); } return(base.CanHitTargetFrom(root, targ)); } if (base.CanHitTargetFrom(root, targ)) { //Check if target is obstructed behind cover Thing coverTarg; if (this.GetPartialCoverBetween(root.ToVector3Shifted(), targ.Cell.ToVector3Shifted(), out coverTarg)) { float targetHeight = CR_Utility.GetCollisionHeight(targ.Thing); if (targetHeight <= CR_Utility.GetCollisionHeight(coverTarg)) { return(false); } } //Check if shooter is obstructed by cover Thing coverShoot; if (this.GetPartialCoverBetween(targ.Cell.ToVector3Shifted(), root.ToVector3Shifted(), out coverShoot)) { float shotHeight = CR_Utility.GetCollisionHeight(this.caster); if (this.CasterPawn != null) { shotHeight *= shotHeightFactor; } if (shotHeight <= CR_Utility.GetCollisionHeight(coverShoot)) { return(false); } } return(true); } return(false); }
/// <summary> /// Takes into account the target being downed and the projectile having been fired while the target was downed, and /// the target's bodySize /// </summary> private bool ImpactThroughBodySize(Thing thing, float height) { Pawn pawn = thing as Pawn; if (pawn != null) { PersonalShield shield = null; if (pawn.RaceProps.Humanlike) { // check for shield user List <Apparel> wornApparel = pawn.apparel.WornApparel; for (int i = 0; i < wornApparel.Count; i++) { if (wornApparel[i] is PersonalShield) { shield = (PersonalShield)wornApparel[i]; break; } } } //Add suppression CompSuppressable compSuppressable = pawn.TryGetComp <CompSuppressable>(); if (compSuppressable != null) { if (shield == null || (shield != null && shield?.ShieldState == ShieldState.Resetting)) { /* * if (pawn.skills.GetSkill(SkillDefOf.Shooting).level >= 1) * { * suppressionAmount = (def.projectile.damageAmountBase * (1f - ((pawn.skills.GetSkill(SkillDefOf.Shooting).level) / 100) * 3)); * } * else suppressionAmount = def.projectile.damageAmountBase; */ suppressionAmount = def.projectile.damageAmountBase; ProjectilePropertiesCR propsCR = def.projectile as ProjectilePropertiesCR; float penetrationAmount = propsCR == null ? 0f : propsCR.armorPenetration; suppressionAmount *= 1 - Mathf.Clamp(compSuppressable.parentArmor - penetrationAmount, 0, 1); compSuppressable.AddSuppression(suppressionAmount, origin.ToIntVec3()); } } //Check horizontal distance Vector3 dest = destination; Vector3 orig = origin; Vector3 pawnPos = pawn.DrawPos; float closestDistToPawn = Math.Abs((dest.z - orig.z) * pawnPos.x - (dest.x - orig.x) * pawnPos.z + dest.x * orig.z - dest.z * orig.x) / (float) Math.Sqrt((dest.z - orig.z) * (dest.z - orig.z) + (dest.x - orig.x) * (dest.x - orig.x)); if (closestDistToPawn <= CR_Utility.GetCollisionWidth(pawn)) { //Check vertical distance float pawnHeight = CR_Utility.GetCollisionHeight(pawn); if (height < pawnHeight) { Impact(thing); return(true); } } } if (thing.def.fillPercent > 0 || thing.def.Fillage == FillCategory.Full) { if (height < CR_Utility.GetCollisionHeight(thing) || thing.def.Fillage == FillCategory.Full) { Impact(thing); return(true); } } return(false); }
/// <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 = CR_Utility.GetCollisionHeight(this.currentTarget.Thing); if (report.cover != null) { targetableHeight += CR_Utility.GetCollisionHeight(report.cover); } heightDifference += targetableHeight * 0.5f; //Optimal hit level is halfway } this.shotHeight = CR_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); }
public string GetTextReadout() { StringBuilder stringBuilder = new StringBuilder(); if (visibilityShift > 0) { stringBuilder.AppendLine(" " + "CR_VisibilityError".Translate() + "\t" + GenText.ToStringByStyle(visibilityShift, ToStringStyle.FloatTwo) + " c"); if (lightingShift > 0) { stringBuilder.AppendLine(" " + "Darkness".Translate() + "\t" + AsPercent(lightingShift)); } if (weatherShift > 0) { stringBuilder.AppendLine(" " + "Weather".Translate() + "\t" + AsPercent(weatherShift)); } } if (leadShift > 0) { stringBuilder.AppendLine(" " + "CR_LeadError".Translate() + "\t" + GenText.ToStringByStyle(leadShift, ToStringStyle.FloatTwo) + " c"); } if (distShift > 0) { stringBuilder.AppendLine(" " + "CR_RangeError".Translate() + "\t" + GenText.ToStringByStyle(distShift, ToStringStyle.FloatTwo) + " c"); } if (swayDegrees > 0) { stringBuilder.AppendLine(" " + "CR_Sway".Translate() + "\t\t" + GenText.ToStringByStyle(swayDegrees, ToStringStyle.FloatTwo) + "°"); } if (spreadDegrees > 0) { stringBuilder.AppendLine(" " + "CR_Spread".Translate() + "\t\t" + GenText.ToStringByStyle(spreadDegrees, ToStringStyle.FloatTwo) + "°"); } // Don't display cover and target size if our weapon has a CEP if (circularMissRadius > 0) { stringBuilder.AppendLine(" " + "CR_MissRadius".Translate() + "\t" + GenText.ToStringByStyle(circularMissRadius, ToStringStyle.FloatTwo) + " c"); if (indirectFireShift > 0) { stringBuilder.AppendLine(" " + "CR_IndirectFire".Translate() + "\t" + GenText.ToStringByStyle(indirectFireShift, ToStringStyle.FloatTwo) + " c"); } } else { if (cover != null) { stringBuilder.AppendLine(" " + "CR_CoverHeight".Translate() + "\t" + GenText.ToStringByStyle(CR_Utility.GetCollisionHeight(cover), ToStringStyle.FloatTwo) + " c"); } if (target.Thing != null) { stringBuilder.AppendLine(" " + "CR_TargetHeight".Translate() + "\t" + GenText.ToStringByStyle(CR_Utility.GetCollisionHeight(target.Thing), ToStringStyle.FloatTwo) + " c"); stringBuilder.AppendLine(" " + "CR_TargetWidth".Translate() + "\t" + GenText.ToStringByStyle(CR_Utility.GetCollisionWidth(target.Thing) * 2, ToStringStyle.FloatTwo) + " c"); } } return(stringBuilder.ToString()); }