static void Postfix(ToHit __instance, ref string __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { Mod.Log.Trace?.Write("TH:GAMD entered"); if (attacker.HasMovedThisRound && attacker.JumpedLastRound || (SharedState.CombatHUD?.SelectionHandler?.ActiveState != null && SharedState.CombatHUD?.SelectionHandler?.ActiveState is SelectionStateJump)) { string localText = new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Attacker_Jumped]).ToString(); __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, Mod.Config.ToHitSelfJumped); } // Check melee patches MeleeAttack selectedAttack = ModState.GetSelectedAttack(attacker); if (selectedAttack != null && weapon.Type == WeaponType.Melee) { foreach (KeyValuePair <string, int> kvp in selectedAttack.AttackModifiers) { string localText = new Text(Mod.LocalizedText.Labels[kvp.Key]).ToString(); Mod.Log.Info?.Write($" - Found attack modifier for desc: {localText} = {kvp.Value}"); __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, kvp.Value); } } }
public static void SaveRangedModifierState(ref float __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { try { LineOfFire = lofLevel; IsMoraleAttack = isCalledShot; SaveStates(attacker, target, weapon); } catch (Exception ex) { Error(ex); } }
private static float GetWeaponDamage(AbstractActor target, WeaponHitInfo hitInfo, Weapon weapon) { float damage = weapon.parent == null ? weapon.DamagePerShot : weapon.DamagePerShotAdjusted(weapon.parent.occupiedDesignMask); AbstractActor attacker = Combat.FindActorByGUID(hitInfo.attackerId); LineOfFireLevel lineOfFireLevel = attacker.VisibilityCache.VisibilityToTarget(target).LineOfFireLevel; return(target.GetAdjustedDamage(damage, weapon.Category, target.occupiedDesignMask, lineOfFireLevel, false)); }
public static void SaveRangedToolTipState(CombatHUDWeaponSlot __instance, ICombatant target) { try { CombatHUDWeaponSlot slot = __instance; LineOfFire = ActiveState.FiringPreview.GetPreviewInfo(target as AbstractActor).LOFLevel; IsMoraleAttack = ActiveState.SelectionType == SelectionType.FireMorale; SaveStates(HUD.SelectedActor, target, slot.DisplayedWeapon); } catch (Exception ex) { Error(ex); } }
static void Postfix(AbstractActor source, ICombatant target, LineOfFireLevel __result) { if (ModState.CurrentTurretForLOF != null) { ModState.CurrentTurretForLOF = null; } if (!source.team.IsLocalPlayer && !(target is BattleTech.Building building)) { Mod.Log.Trace?.Write($"== LOF RESULT: {__result}"); } }
static void Prefix(AbstractActor source, ICombatant target, LineOfFireLevel __result) { if (!source.team.IsLocalPlayer && !(target is BattleTech.Building building)) { Mod.Log.Trace?.Write($"== CALCULATING LOF FROM {CombatantUtils.Label(source)} TO TARGET: {CombatantUtils.Label(source)}"); } if (source is Turret turret && ModState.IsUrbanBiome && ModState.AmbushTurretGUIDtoBuilding.Keys.Contains(turret.GUID)) { Mod.Log.Trace?.Write($"Turret {CombatantUtils.Label(turret)} is calculating it's LOF"); ModState.CurrentTurretForLOF = turret; } }
private static void Postfix(ToHit __instance, ref float __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel) { string cacheKey = StraightTonnageCalculator.CacheKey(attacker, target); bool keyExists = ModState.CachedComparisonMods.TryGetValue(cacheKey, out int modifier); if (!keyExists) { modifier = StraightTonnageCalculator.Modifier(attacker, target); ModState.CachedComparisonMods.Add(cacheKey, modifier); } __result += modifier; }
private static bool CanSmartIndirect(AbstractActor attacker, Vector3 attackPosition, Quaternion attackRotation, ICombatant target, bool checkWeapon = true) { bool pointless = CGS.ToHit.GetIndirectModifier(attacker) >= CGC.ToHit.ToHitCoverObstructed; bool unreachable = !attacker.IsTargetPositionInFiringArc(target, attackPosition, attackRotation, target.CurrentPosition); bool impossible = checkWeapon && !CanFireIndirectWeapon(attacker, Vector3.Distance(attackPosition, target.CurrentPosition)); if (pointless || unreachable || impossible) { return(false); } LineOfFireLevel lof = CGS.LOFCache.GetLineOfFire(attacker, attackPosition, target, target.CurrentPosition, target.CurrentRotation, out _); return(lof == LineOfFireLevel.LOFObstructed); }
public bool HasZoomVisionToTarget(Weapon weapon, float distance, LineOfFireLevel lofLevel) { // If we're firing indirectly, zoom doesn't count if (weapon.IndirectFireCapable && lofLevel < LineOfFireLevel.LOFObstructed) { Mod.Log.Debug?.Write("Line of fire is indirect - cannot use zoom!"); return(false); } if (zoomVision == null || weapon.Type == WeaponType.Melee || weapon.Type == WeaponType.NotSet) { Mod.Log.Debug?.Write("Zoom vision is null, weaponType is melee or unset - cannot use zoom!"); return(false); } return(distance < zoomVision.MaximumRange); }
// The default method assumes an absractActor exists, and tries to draw a line of fire. We don't have that, so skip it. public static void ResolveSourcelessWeaponDamage(this Mech mech, WeaponHitInfo hitInfo, Weapon weapon, MeleeAttackType meleeAttackType) { AttackDirector.AttackSequence attackSequence = ModState.Combat.AttackDirector.GetAttackSequence(hitInfo.attackSequenceId); float damagePerShot = weapon.DamagePerShot; float structureDamagePerShot = weapon.StructureDamagePerShot; LineOfFireLevel lineOfFireLevel = LineOfFireLevel.LOFClear; damagePerShot = mech.GetAdjustedDamage(damagePerShot, weapon.WeaponCategoryValue, mech.occupiedDesignMask, lineOfFireLevel, false); structureDamagePerShot = mech.GetAdjustedDamage(structureDamagePerShot, weapon.WeaponCategoryValue, mech.occupiedDesignMask, lineOfFireLevel, false); foreach (KeyValuePair <int, float> keyValuePair in hitInfo.ConsolidateCriticalHitInfo(mech.GUID, damagePerShot)) { if (keyValuePair.Key != 0 && keyValuePair.Key != 65536 && (mech.ArmorForLocation(keyValuePair.Key) <= 0f || structureDamagePerShot > 0f)) { ChassisLocations chassisLocationFromArmorLocation = MechStructureRules.GetChassisLocationFromArmorLocation((ArmorLocation)keyValuePair.Key); if (!mech.IsLocationDestroyed(chassisLocationFromArmorLocation)) { Traverse checkForCritT = Traverse.Create(mech).Method("CheckForCrit", new Type[] { typeof(WeaponHitInfo), typeof(ChassisLocations), typeof(Weapon) }); checkForCritT.GetValue(new object[] { hitInfo, chassisLocationFromArmorLocation, weapon }); } } } if (weapon.HeatDamagePerShot > 0f) { bool flag = false; for (int i = 0; i < hitInfo.numberOfShots; i++) { if (hitInfo.DidShotHitTarget(mech.GUID, i) && hitInfo.ShotHitLocation(i) != 0 && hitInfo.ShotHitLocation(i) != 65536) { flag = true; mech.AddExternalHeat(string.Format("Heat Damage from {0}", weapon.Description.Name), (int)weapon.HeatDamagePerShotAdjusted(hitInfo.hitQualities[i])); } } if (flag && attackSequence != null) { attackSequence.FlagAttackDidHeatDamage(mech.GUID); } } float num3 = hitInfo.ConsolidateInstability(mech.GUID, weapon.Instability(), mech.Combat.Constants.ResolutionConstants.GlancingBlowDamageMultiplier, mech.Combat.Constants.ResolutionConstants.NormalBlowDamageMultiplier, mech.Combat.Constants.ResolutionConstants.SolidBlowDamageMultiplier); num3 *= mech.StatCollection.GetValue <float>("ReceivedInstabilityMultiplier"); num3 *= mech.EntrenchedMultiplier; mech.AddAbsoluteInstability(num3, StabilityChangeSource.Attack, hitInfo.attackerId); }
static void Postfix(ToHit __instance, ref float __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { Mod.Log.Trace?.Write("TH:GAM entered"); if (__instance == null || weapon == null) { return; } if ( (attacker.HasMovedThisRound && attacker.JumpedLastRound) || (SharedState.CombatHUD?.SelectionHandler?.ActiveState != null && SharedState.CombatHUD?.SelectionHandler?.ActiveState is SelectionStateJump) ) { __result += (float)Mod.Config.ToHitSelfJumped; } }
private static bool CanSmartIndirect(AbstractActor attacker, Vector3 attackPosition, Quaternion attackRotation, ICombatant target, bool checkWeapon = true) { if (Combat.ToHit.GetIndirectModifier(attacker) >= CombatConstants.ToHit.ToHitCoverObstructed) { return(false); // Abort if it is pointless } Vector3 targetPos = target.CurrentPosition; if (!attacker.IsTargetPositionInFiringArc(target, attackPosition, attackRotation, targetPos)) { return(false); // Abort if can't shot } if (checkWeapon && !CanFireIndirectWeapon(attacker, Vector3.Distance(attackPosition, targetPos))) { return(false); // Can't indirect } LineOfFireLevel lof = Combat.LOFCache.GetLineOfFire(attacker, attackPosition, target, targetPos, target.CurrentRotation, out _); return(lof == LineOfFireLevel.LOFObstructed); }
public static void Postfix(ToHit __instance, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot, ref string __result) { string str = string.Empty; bool flag = lofLevel < LineOfFireLevel.LOFObstructed && (CustomAmmoCategories.getIndirectFireCapable(weapon)); float weaponDirectFireModifier = CustomAmmoCategories.getDirectFireModifier(weapon); if (flag == false) { //CustomAmmoCategoriesLog.Log.LogWrite(attacker.DisplayName + " has LOS on " + target.DisplayName + ". Apply DirectFireModifier " + weaponDirectFireModifier + "\n"); if (!NvMath.FloatIsNearZero(weaponDirectFireModifier)) { __result = string.Format("{0}WEAPON-DIRECT-FIRE {1:+#;-#}; ", (object)__result, (object)(int)weaponDirectFireModifier); } } CombatGameState combat = (CombatGameState)typeof(ToHit).GetField("combat", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); return; }
public static bool OverrideRangedModifiers(ref float __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { try { __result = SumModifiers(RangedModifiers); return(false); } catch (Exception ex) { return(Error(new ApplicationException("Error in ranged modifier '" + thisModifier + "'", ex))); } }
public static void Postfix(LineOfSight __instance, AbstractActor source, Vector3 sourcePosition, ICombatant target, ref LineOfFireLevel __result) { if (target is AbstractActor a && a.HasIndirectFireImmunity && __instance.GetVisibilityToTargetWithPositionsAndRotations(source, sourcePosition, a) != VisibilityLevel.LOSFull) { __result = LineOfFireLevel.LOFBlocked; } }
/*static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) * { * var targetPropertyGetter = AccessTools.Property(typeof(Weapon), "IndirectFireCapable").GetGetMethod(); * var replacementMethod = AccessTools.Method(typeof(ToHit_GetAllModifiersDescription), nameof(IndirectFireCapable)); * return Transpilers.MethodReplacer(instructions, targetPropertyGetter, replacementMethod); * } * * private static bool IndirectFireCapable(Weapon weapon) * { * //CustomAmmoCategoriesLog.Log.LogWrite("get ToHit_GetAllModifiersDescription IndirectFireCapable\n"); * return CustomAmmoCategories.getIndirectFireCapable(weapon); * }*/ public static bool Prefix(ToHit __instance, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot, ref string __result) { string str = string.Empty; bool flag = lofLevel < LineOfFireLevel.LOFObstructed && (CustomAmmoCategories.getIndirectFireCapable(weapon)); float rangeModifier = __instance.GetRangeModifier(weapon, attackPosition, targetPosition); float coverModifier = __instance.GetCoverModifier(attacker, target, lofLevel); float selfSpeedModifier = __instance.GetSelfSpeedModifier(attacker); float sprintedModifier = __instance.GetSelfSprintedModifier(attacker); float armMountedModifier = __instance.GetSelfArmMountedModifier(weapon); float stoodUpModifier = __instance.GetStoodUpModifier(attacker); float heightModifier = __instance.GetHeightModifier(attackPosition.y, targetPosition.y); float heatModifier = __instance.GetHeatModifier(attacker); float targetTerrainModifier = __instance.GetTargetTerrainModifier(target, targetPosition, false); float selfTerrainModifier = __instance.GetSelfTerrainModifier(attackPosition, false); float targetSpeedModifier = __instance.GetTargetSpeedModifier(target, weapon); float selfDamageModifier = __instance.GetSelfDamageModifier(attacker, weapon); float targetSizeModifier = __instance.GetTargetSizeModifier(target); float shutdownModifier = __instance.GetTargetShutdownModifier(target, false); float targetProneModifier = __instance.GetTargetProneModifier(target, false); float accuracyModifier1 = __instance.GetWeaponAccuracyModifier(attacker, weapon); float accuracyModifier2 = __instance.GetAttackerAccuracyModifier(attacker); float enemyEffectModifier = __instance.GetEnemyEffectModifier(target); float refireModifier = __instance.GetRefireModifier(weapon); float directFireModifier = __instance.GetTargetDirectFireModifier(target, flag); float indirectModifier = __instance.GetIndirectModifier(attacker, flag); float moraleAttackModifier = __instance.GetMoraleAttackModifier(target, isCalledShot); float weaponDirectFireModifier = CustomAmmoCategories.getDirectFireModifier(weapon); if (!NvMath.FloatIsNearZero(rangeModifier)) { str = string.Format("{0}RANGE {1:+#;-#}; ", (object)str, (object)(int)rangeModifier); } if (!NvMath.FloatIsNearZero(coverModifier)) { str = string.Format("{0}COVER {1:+#;-#}; ", (object)str, (object)(int)rangeModifier); } if (!NvMath.FloatIsNearZero(selfSpeedModifier)) { str = string.Format("{0}SELF-MOVED {1:+#;-#}; ", (object)str, (object)(int)selfSpeedModifier); } if (!NvMath.FloatIsNearZero(sprintedModifier)) { str = string.Format("{0}SELF-SPRINTED {1:+#;-#}; ", (object)str, (object)(int)sprintedModifier); } if (!NvMath.FloatIsNearZero(armMountedModifier)) { str = string.Format("{0}SELF-ARM MOUNTED {1:+#;-#}; ", (object)str, (object)(int)armMountedModifier); } if (!NvMath.FloatIsNearZero(stoodUpModifier)) { str = string.Format("{0}STOOD UP {1:+#;-#}; ", (object)str, (object)(int)stoodUpModifier); } if (!NvMath.FloatIsNearZero(heightModifier)) { str = string.Format("{0}HEIGHT {1:+#;-#}; ", (object)str, (object)(int)heightModifier); } if (!NvMath.FloatIsNearZero(heatModifier)) { str = string.Format("{0}HEAT {1:+#;-#}; ", (object)str, (object)(int)heatModifier); } if (!NvMath.FloatIsNearZero(targetTerrainModifier)) { str = string.Format("{0}TERRAIN {1:+#;-#}; ", (object)str, (object)(int)targetTerrainModifier); } if (!NvMath.FloatIsNearZero(selfTerrainModifier)) { str = string.Format("{0}TERRAIN SELF {1:+#;-#}; ", (object)str, (object)(int)selfTerrainModifier); } if (!NvMath.FloatIsNearZero(targetSpeedModifier)) { str = string.Format("{0}TARGET-SPEED {1:+#;-#}; ", (object)str, (object)(int)targetSpeedModifier); } if (!NvMath.FloatIsNearZero(selfDamageModifier)) { str = string.Format("{0}SELF-DAMAGE {1:+#;-#}; ", (object)str, (object)(int)selfDamageModifier); } if (!NvMath.FloatIsNearZero(targetSizeModifier)) { str = string.Format("{0}TARGET-SIZE {1:+#;-#}; ", (object)str, (object)(int)targetSizeModifier); } if (!NvMath.FloatIsNearZero(shutdownModifier)) { str = string.Format("{0}TARGET-SHUTDOWN {1:+#;-#}; ", (object)str, (object)(int)shutdownModifier); } if (!NvMath.FloatIsNearZero(targetProneModifier)) { str = string.Format("{0}TARGET-PRONE {1:+#;-#}; ", (object)str, (object)(int)targetProneModifier); } if (!NvMath.FloatIsNearZero(accuracyModifier1)) { str = string.Format("{0}ATTACKER-EFFECTS {1:+#;-#}; ", (object)str, (object)(int)accuracyModifier1); } if (!NvMath.FloatIsNearZero(accuracyModifier2)) { str = string.Format("{0}ATTACKER-SELF-EFFECTS {1:+#;-#}; ", (object)str, (object)(int)accuracyModifier2); } if (!NvMath.FloatIsNearZero(enemyEffectModifier)) { str = string.Format("{0}ENEMY-EFFECTS {1:+#;-#}; ", (object)str, (object)(int)enemyEffectModifier); } if (!NvMath.FloatIsNearZero(refireModifier)) { str = string.Format("{0}REFIRE {1:+#;-#}; ", (object)str, (object)(int)refireModifier); } if (!NvMath.FloatIsNearZero(directFireModifier)) { str = string.Format("{0}DIRECT-FIRE {1:+#;-#}; ", (object)str, (object)(int)directFireModifier); } if (!NvMath.FloatIsNearZero(indirectModifier)) { str = string.Format("{0}INDIRECT-FIRE {1:+#;-#}; ", (object)str, (object)(int)indirectModifier); } if (!NvMath.FloatIsNearZero(moraleAttackModifier)) { str = string.Format("{0}CALLED-SHOT {1:+#;-#}; ", (object)str, (object)(int)moraleAttackModifier); } float b = rangeModifier + coverModifier + selfSpeedModifier + sprintedModifier + armMountedModifier + stoodUpModifier + heightModifier + heatModifier + targetTerrainModifier + selfTerrainModifier + targetSpeedModifier + selfDamageModifier + targetSizeModifier + shutdownModifier + targetProneModifier + accuracyModifier1 + accuracyModifier2 + enemyEffectModifier + refireModifier + directFireModifier + indirectModifier + moraleAttackModifier; if (flag == false) { CustomAmmoCategoriesLog.Log.LogWrite(attacker.DisplayName + " has LOS on " + target.DisplayName + ". Apply DirectFireModifier " + weaponDirectFireModifier + "\n"); if (!NvMath.FloatIsNearZero(weaponDirectFireModifier)) { str = string.Format("{0}WEAPON-DIRECT-FIRE {1:+#;-#}; ", (object)str, (object)(int)weaponDirectFireModifier); } b += weaponDirectFireModifier; } CombatGameState combat = (CombatGameState)typeof(ToHit).GetField("combat", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); if ((double)b < 0.0 && !combat.Constants.ResolutionConstants.AllowTotalNegativeModifier) { b = 0.0f; } float allModifiers = __instance.GetAllModifiers(attacker, weapon, target, attackPosition, targetPosition, lofLevel, isCalledShot); if (!NvMath.FloatsAreEqual(allModifiers, b)) { CustomAmmoCategoriesLog.Log.LogWrite("Strange behavior calced modifier " + b + " not equal geted " + allModifiers + "\n"); AttackDirector.attackLogger.LogError((object)("ERROR!!! breakdown of Universal Modifier didn't match actual Universal Modifier. Check TargetingRules! current modifier: " + (object)b + ", doubleCheck modifier: " + (object)allModifiers)); } __result = str; return(false); }
public static bool Prefix(AttackDirector.AttackSequence __instance, Weapon weapon, int groupIdx, int weaponIdx, int numberOfShots, bool indirectFire, float dodgedDamage, ref WeaponHitInfo __result) { WeaponHitInfo hitInfo = new WeaponHitInfo(); hitInfo.attackerId = __instance.attacker.GUID; hitInfo.targetId = __instance.target.GUID; hitInfo.numberOfShots = numberOfShots; hitInfo.stackItemUID = __instance.stackItemUID; hitInfo.attackSequenceId = __instance.id; hitInfo.attackGroupIndex = groupIdx; hitInfo.attackWeaponIndex = weaponIdx; hitInfo.toHitRolls = new float[numberOfShots]; hitInfo.locationRolls = new float[numberOfShots]; hitInfo.dodgeRolls = new float[numberOfShots]; hitInfo.dodgeSuccesses = new bool[numberOfShots]; hitInfo.hitLocations = new int[numberOfShots]; hitInfo.hitPositions = new Vector3[numberOfShots]; hitInfo.hitVariance = new int[numberOfShots]; hitInfo.hitQualities = new AttackImpactQuality[numberOfShots]; if (AttackDirector.hitLogger.IsLogEnabled) { Vector3 collisionWorldPos; LineOfFireLevel lineOfFire = __instance.Director.Combat.LOS.GetLineOfFire(__instance.attacker, __instance.attackPosition, __instance.target, __instance.target.CurrentPosition, __instance.target.CurrentRotation, out collisionWorldPos); float allModifiers = __instance.Director.Combat.ToHit.GetAllModifiers(__instance.attacker, weapon, __instance.target, __instance.attackPosition + __instance.attacker.HighestLOSPosition, __instance.target.TargetPosition, lineOfFire, __instance.isMoraleAttack); string modifiersDescription = __instance.Director.Combat.ToHit.GetAllModifiersDescription(__instance.attacker, weapon, __instance.target, __instance.attackPosition + __instance.attacker.HighestLOSPosition, __instance.target.TargetPosition, lineOfFire, __instance.isMoraleAttack); Pilot pilot = __instance.attacker.GetPilot(); AttackDirector.hitLogger.Log((object)string.Format("======================================== Unit Firing: {0} | Weapon: {1} | Shots: {2}", (object)__instance.attacker.DisplayName, (object)weapon.Name, (object)numberOfShots)); AttackDirector.hitLogger.Log((object)string.Format("======================================== Hit Info: GROUP {0} | ID {1}", (object)groupIdx, (object)weaponIdx)); AttackDirector.hitLogger.Log((object)string.Format("======================================== MODIFIERS: {0}... FINAL: [[ {1} ]] ", (object)modifiersDescription, (object)allModifiers)); if (pilot != null) { AttackDirector.hitLogger.Log((object)__instance.Director.Combat.ToHit.GetBaseToHitChanceDesc(__instance.attacker)); } else { AttackDirector.hitLogger.Log((object)string.Format("======================================== Gunnery Check: NO PILOT")); } } float toHitChance = __instance.Director.Combat.ToHit.GetToHitChance(__instance.attacker, weapon, __instance.target, __instance.attackPosition, __instance.target.CurrentPosition, __instance.numTargets, __instance.meleeAttackType, __instance.isMoraleAttack); if (Mech.TEST_KNOCKDOWN) { toHitChance = 1f; } if (AttackDirector.hitLogger.IsLogEnabled) { AttackDirector.hitLogger.Log((object)string.Format("======================================== HIT CHANCE: [[ {0:P2} ]]", (object)toHitChance)); } hitInfo.attackDirection = __instance.Director.Combat.HitLocation.GetAttackDirection(__instance.attackPosition, __instance.target); hitInfo.attackDirectionVector = __instance.Director.Combat.HitLocation.GetAttackDirectionVector(__instance.attackPosition, __instance.target); object[] args = new object[6]; HitGeneratorType hitGenType = HitGeneratorType.NotSet; if (weapon.weaponDef.ComponentTags.Contains("wr-clustered_shots")) { hitGenType = HitGeneratorType.Individual; } if (hitGenType == HitGeneratorType.NotSet) { if (CustomAmmoCategories.checkExistance(weapon.StatCollection, CustomAmmoCategories.AmmoIdStatName) == true) { string ammoId = weapon.StatCollection.GetStatistic(CustomAmmoCategories.AmmoIdStatName).Value <string>(); ExtAmmunitionDef extAmmo = CustomAmmoCategories.findExtAmmo(ammoId); hitGenType = extAmmo.HitGenerator; } if (hitGenType == HitGeneratorType.NotSet) { ExtWeaponDef extWeapon = CustomAmmoCategories.getExtWeaponDef(weapon.weaponDef.Description.Id); hitGenType = extWeapon.HitGenerator; } } if (hitGenType != HitGeneratorType.NotSet) { switch (hitGenType) { case HitGeneratorType.Individual: args[0] = hitInfo; args[1] = groupIdx; args[2] = weaponIdx; args[3] = weapon; args[4] = toHitChance; args[5] = dodgedDamage; typeof(AttackDirector.AttackSequence).GetMethod("GetIndividualHits", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, args); hitInfo = (WeaponHitInfo)args[0]; break; case HitGeneratorType.Cluster: args[0] = hitInfo; args[1] = groupIdx; args[2] = weaponIdx; args[3] = weapon; args[4] = toHitChance; args[5] = dodgedDamage; typeof(AttackDirector.AttackSequence).GetMethod("GetClusteredHits", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, args); hitInfo = (WeaponHitInfo)args[0]; //__instance.GetClusteredHits(ref hitInfo, groupIdx, weaponIdx, weapon, toHitChance, dodgedDamage); break; case HitGeneratorType.Streak: //args[0] = hitInfo; args[1] = groupIdx; args[2] = weaponIdx; args[3] = weapon; args[4] = toHitChance; args[5] = dodgedDamage; //typeof(AttackDirector.AttackSequence).GetMethod("GetClusteredHits", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, args); //hitInfo = (WeaponHitInfo)args[0]; AttackSequence_GenerateHitInfo.GetStreakHits(__instance, ref hitInfo, groupIdx, weaponIdx, weapon, toHitChance, dodgedDamage); //__instance.GetClusteredHits(ref hitInfo, groupIdx, weaponIdx, weapon, toHitChance, dodgedDamage); break; default: AttackDirector.attackLogger.LogError((object)string.Format("GenerateHitInfo found invalid weapon type: {0}, using basic hit info", (object)hitGenType)); args[0] = hitInfo; args[1] = groupIdx; args[2] = weaponIdx; args[3] = weapon; args[4] = toHitChance; args[5] = dodgedDamage; typeof(AttackDirector.AttackSequence).GetMethod("GetIndividualHits", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, args); hitInfo = (WeaponHitInfo)args[0]; //__instance.GetIndividualHits(ref hitInfo, groupIdx, weaponIdx, weapon, toHitChance, dodgedDamage); break; } } else { switch (weapon.Type) { case WeaponType.Autocannon: case WeaponType.Gauss: case WeaponType.Laser: case WeaponType.PPC: case WeaponType.Flamer: case WeaponType.Melee: args[0] = hitInfo; args[1] = groupIdx; args[2] = weaponIdx; args[3] = weapon; args[4] = toHitChance; args[5] = dodgedDamage; typeof(AttackDirector.AttackSequence).GetMethod("GetIndividualHits", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, args); hitInfo = (WeaponHitInfo)args[0]; //__instance.GetIndividualHits(ref hitInfo, groupIdx, weaponIdx, weapon, toHitChance, dodgedDamage); break; case WeaponType.LRM: args[0] = hitInfo; args[1] = groupIdx; args[2] = weaponIdx; args[3] = weapon; args[4] = toHitChance; args[5] = dodgedDamage; typeof(AttackDirector.AttackSequence).GetMethod("GetClusteredHits", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, args); hitInfo = (WeaponHitInfo)args[0]; //__instance.GetClusteredHits(ref hitInfo, groupIdx, weaponIdx, weapon, toHitChance, dodgedDamage); break; case WeaponType.SRM: args[0] = hitInfo; args[1] = groupIdx; args[2] = weaponIdx; args[3] = weapon; args[4] = toHitChance; args[5] = dodgedDamage; typeof(AttackDirector.AttackSequence).GetMethod("GetIndividualHits", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, args); hitInfo = (WeaponHitInfo)args[0]; //__instance.GetIndividualHits(ref hitInfo, groupIdx, weaponIdx, weapon, toHitChance, dodgedDamage); break; case WeaponType.MachineGun: args[0] = hitInfo; args[1] = groupIdx; args[2] = weaponIdx; args[3] = weapon; args[4] = toHitChance; args[5] = dodgedDamage; typeof(AttackDirector.AttackSequence).GetMethod("GetIndividualHits", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, args); hitInfo = (WeaponHitInfo)args[0]; //__instance.GetIndividualHits(ref hitInfo, groupIdx, weaponIdx, weapon, toHitChance, dodgedDamage); break; default: AttackDirector.attackLogger.LogError((object)string.Format("GenerateHitInfo found invalid weapon type: {0}, using basic hit info", (object)weapon.Type)); args[0] = hitInfo; args[1] = groupIdx; args[2] = weaponIdx; args[3] = weapon; args[4] = toHitChance; args[5] = dodgedDamage; typeof(AttackDirector.AttackSequence).GetMethod("GetIndividualHits", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, args); hitInfo = (WeaponHitInfo)args[0]; //__instance.GetIndividualHits(ref hitInfo, groupIdx, weaponIdx, weapon, toHitChance, dodgedDamage); break; } } __result = hitInfo; return(false); //return hitInfo; }
private static float CounterNarc(ToHit tohit, AbstractActor attacker, Weapon wep, ICombatant target, Vector3 apos, Vector3 tpos, LineOfFireLevel lof, MeleeAttackType mat, bool calledshot) { AbstractActor at = target as AbstractActor; if (at != null && at.HasIndirectFireImmunity && at.Combat.EffectManager.GetAllEffectsTargetingWithBaseID(at, "StatusEffect-NARC-IncomingAttBonus").Count > 0) { return(3); } return(0); }
/*static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) * { * var targetPropertyGetter = AccessTools.Property(typeof(Weapon), "IndirectFireCapable").GetGetMethod(); * var replacementMethod = AccessTools.Method(typeof(ToHit_GetAllModifiers), nameof(IndirectFireCapable)); * return Transpilers.MethodReplacer(instructions, targetPropertyGetter, replacementMethod); * } * * private static bool IndirectFireCapable(Weapon weapon) * { * //CustomAmmoCategoriesLog.Log.LogWrite("get ToHit_GetAllModifiers IndirectFireCapable\n"); * return CustomAmmoCategories.getIndirectFireCapable(weapon); * }*/ public static bool Prefix(ToHit __instance, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot, ref float __result) { bool flag = lofLevel < LineOfFireLevel.LOFObstructed && (CustomAmmoCategories.getIndirectFireCapable(weapon)); float num = __instance.GetRangeModifier(weapon, attackPosition, targetPosition) + __instance.GetCoverModifier(attacker, target, lofLevel) + __instance.GetSelfSpeedModifier(attacker) + __instance.GetSelfSprintedModifier(attacker) + __instance.GetSelfArmMountedModifier(weapon) + __instance.GetStoodUpModifier(attacker) + __instance.GetHeightModifier(attackPosition.y, targetPosition.y) + __instance.GetHeatModifier(attacker) + __instance.GetTargetTerrainModifier(target, targetPosition, false) + __instance.GetSelfTerrainModifier(attackPosition, false) + __instance.GetTargetSpeedModifier(target, weapon) + __instance.GetSelfDamageModifier(attacker, weapon) + __instance.GetTargetSizeModifier(target) + __instance.GetTargetShutdownModifier(target, false) + __instance.GetTargetProneModifier(target, false) + __instance.GetWeaponAccuracyModifier(attacker, weapon) + __instance.GetAttackerAccuracyModifier(attacker) + __instance.GetEnemyEffectModifier(target) + __instance.GetRefireModifier(weapon) + __instance.GetTargetDirectFireModifier(target, flag) + __instance.GetIndirectModifier(attacker, flag) + __instance.GetMoraleAttackModifier(target, isCalledShot); if (flag == false) { float directFireModifier = CustomAmmoCategories.getDirectFireModifier(weapon); CustomAmmoCategoriesLog.Log.LogWrite(attacker.DisplayName + " has LOS on " + target.DisplayName + ". Apply DirectFireModifier " + directFireModifier + "\n"); num += CustomAmmoCategories.getDirectFireModifier(weapon); } CombatGameState combat = (CombatGameState)typeof(ToHit).GetField("combat", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); if ((double)num < 0.0 && !combat.Constants.ResolutionConstants.AllowTotalNegativeModifier) { num = 0.0f; } __result = num; return(false); }
// Token: 0x06000025 RID: 37 private static void Postfix(ToHit __instance, ref float __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { if (UnityGameInstance.BattleTechGame.Simulation != null && weapon != null) { Mod.Log.Trace("TH:GAM entered"); bool flag3; if (!attacker.HasMovedThisRound || !attacker.JumpedLastRound || attacker.SkillTactics >= Mod.Config.TacticsSkillNegateJump) { CombatHUD combatHUD = ModState.CombatHUD; bool flag2; if (combatHUD == null) { flag2 = false; } else { CombatSelectionHandler selectionHandler = combatHUD.SelectionHandler; flag2 = (((selectionHandler != null) ? selectionHandler.ActiveState : null) != null); } if (flag2) { CombatHUD combatHUD2 = ModState.CombatHUD; object obj; if (combatHUD2 == null) { obj = null; } else { CombatSelectionHandler selectionHandler2 = combatHUD2.SelectionHandler; obj = ((selectionHandler2 != null) ? selectionHandler2.ActiveState : null); } if (obj is SelectionStateJump) { flag3 = (attacker.SkillTactics < Mod.Config.TacticsSkillNegateJump); goto IL_D8; } } flag3 = false; } else { flag3 = true; } IL_D8: if (flag3) { __result += (float)Mod.Config.ToHitSelfJumped; } } }
private static void Postfix(ToHit __instance, ref string __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { Mod.Log.Trace("TH:GAMD entered"); if (attacker.HasMovedThisRound && attacker.JumpedLastRound) { __result = string.Format("{0}JUMPED {1:+#;-#}; ", __result, Mod.Config.ToHitSelfJumped); } }
private static void Postfix(ToHit __instance, ref float __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { Pilot pilot = attacker.GetPilot(); try { Pilot TargetPilot = target.GetPilot(); if (TargetPilot.pilotDef.PilotTags.Contains("pilot_reckless")) { __result = __result + (float)settings.pilot_reckless_ToBeHitBonus; } if (TargetPilot.pilotDef.PilotTags.Contains("pilot_cautious")) { __result = __result + (float)settings.pilot_reckless_ToBeHitBonus; } if (TargetPilot.pilotDef.PilotTags.Contains("pilot_jinxed")) { __result = __result + (float)settings.pilot_jinxed_ToBeHitBonus; } if (TargetPilot.pilotDef.PilotTags.Contains("pilot_jinxed")) { __result = __result + (float)settings.pilot_reckless_ToBeHitBonus; } } catch (Exception) { } if (pilot.pilotDef.PilotTags.Contains("pilot_reckless")) { __result = __result + (float)settings.pilot_reckless_ToHitBonus; } if (pilot.pilotDef.PilotTags.Contains("pilot_cautious")) { __result = __result + (float)settings.pilot_reckless_ToHitBonus; } if (pilot.pilotDef.PilotTags.Contains("pilot_drunk") && pilot.pilotDef.TimeoutRemaining > 0) { __result = __result + (float)settings.pilot_drunk_ToHitBonus; } if (pilot.pilotDef.PilotTags.Contains("pilot_lostech") && weapon.componentDef.ComponentTags.Contains("component_type_lostech")) { __result = __result + (float)settings.pilot_lostech_ToHitBonus; } }
private static void Postfix(ToHit __instance, ref string __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { Pilot pilot = attacker.GetPilot(); if (pilot.pilotDef.PilotTags.Contains("pilot_reckless")) { __result = string.Format("{0}RECKLESS {1:+#;-#}; ", __result, settings.pilot_reckless_ToHitBonus); } if (pilot.pilotDef.PilotTags.Contains("pilot_cautious")) { __result = string.Format("{0}CAUTIOUS {1:+#;-#}; ", __result, settings.pilot_cautious_ToHitBonus); } if (pilot.pilotDef.PilotTags.Contains("pilot_drunk") && pilot.pilotDef.TimeoutRemaining > 0) { __result = string.Format("{0}DRUNK {1:+#;-#}; ", __result, settings.pilot_drunk_ToHitBonus); } if (pilot.pilotDef.PilotTags.Contains("pilot_lostech") && weapon.componentDef.ComponentTags.Contains("component_type_lostech")) { __result = string.Format("{0}LOSTECH TECHNICIAN {1:+#;-#}; ", __result, settings.pilot_lostech_ToHitBonus); } if (pilot.pilotDef.PilotTags.Contains("pilot_jinxed")) { __result = string.Format("{0}JINXED {1:+#;-#}; ", __result, settings.pilot_reckless_ToHitBonus); } }
public static void Postfix(ToHit __instance, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot, ref float __result) { bool flag = lofLevel < LineOfFireLevel.LOFObstructed && (CustomAmmoCategories.getIndirectFireCapable(weapon)); float num = __result; if (flag == false) { //float directFireModifier = CustomAmmoCategories.getDirectFireModifier(weapon); //CustomAmmoCategoriesLog.Log.LogWrite(attacker.DisplayName+" has LOS on "+target.DisplayName+ ". Apply DirectFireModifier "+directFireModifier+"\n"); num += CustomAmmoCategories.getDirectFireModifier(weapon); } CombatGameState combat = (CombatGameState)typeof(ToHit).GetField("combat", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); if ((double)num < 0.0 && !combat.Constants.ResolutionConstants.AllowTotalNegativeModifier) { num = 0.0f; } __result = num; return; }
//[HarmonyBefore(new string[] { "Sheepy.BattleTechMod.AttackImprovementMod" })] private static void Postfix(ToHit __instance, ref string __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { //Mod.Log.Debug?.Write($"Getting modifier descriptions for attacker:{CombatantUtils.Label(attacker)} " + // $"using weapon:{weapon.Name} vs target:{CombatantUtils.Label(target)}"); //AbstractActor targetActor = target as AbstractActor; //if (__instance != null && attacker != null && weapon != null && target != null && targetActor != null) { // float distance = Vector3.Distance(attackPosition, targetPosition); // EWState attackerState = new EWState(attacker); // EWState targetState = new EWState(targetActor); // // Vision modifiers // int zoomVisionMod = attackerState.GetZoomVisionAttackMod(weapon, distance); // int heatVisionMod = attackerState.GetHeatVisionAttackMod(targetActor, distance, weapon); // int mimeticMod = targetState.MimeticAttackMod(attackerState); // bool canSpotTarget = VisualLockHelper.CanSpotTarget(attacker, attacker.CurrentPosition, target, target.CurrentPosition, target.CurrentRotation, attacker.Combat.LOS); // // Sensor modifiers // SensorScanType sensorScan = SensorLockHelper.CalculateSharedLock(targetActor, attacker); // int ecmShieldMod = targetState.ECMAttackMod(attackerState); // int stealthMod = targetState.StealthAttackMod(attackerState, weapon, distance); // int narcMod = targetState.NarcAttackMod(attackerState); // int tagMod = targetState.TagAttackMod(attackerState); // if (Mod.Config.Attack.NoSensorInfoPenalty > (ecmShieldMod + stealthMod + narcMod + tagMod)) { sensorScan = SensorScanType.NoInfo; } // if (sensorScan == SensorScanType.NoInfo && !canSpotTarget) { // string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_FIRING_BLIND]).ToString(); // __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, Mod.Config.Attack.BlindFirePenalty); // } else { // if (!canSpotTarget) { // string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_NO_VISUALS]).ToString(); // __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, Mod.Config.Attack.NoVisualsPenalty); // } else { // if (zoomVisionMod != 0) { // string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_ZOOM_VISION]).ToString(); // __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, zoomVisionMod); // } // if (heatVisionMod != 0) { // string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_HEAT_VISION]).ToString(); // __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, heatVisionMod); // } // if (mimeticMod != 0) { // string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_MIMETIC]).ToString(); // __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, mimeticMod); // } // } // if (sensorScan == SensorScanType.NoInfo) { // string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_NO_SENSORS]).ToString(); // __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, Mod.Config.Attack.NoSensorInfoPenalty); // } else { // if (ecmShieldMod != 0) { // string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_ECM_SHEILD]).ToString(); // __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, ecmShieldMod); // } // if (stealthMod != 0) { // string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_STEALTH]).ToString(); // __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, stealthMod); // } // if (ecmShieldMod != 0) { // string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_NARCED]).ToString(); // __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, narcMod); // } // if (stealthMod != 0) { // string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_TAGGED]).ToString(); // __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, tagMod); // } // } // } //} }
private static void Postfix(ToHit __instance, ref float __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel) { //Mod.Log.Debug?.Write($"Getting modifiers for attacker:{CombatantUtils.Label(attacker)} " + // $"using weapon:{weapon.Name} vs target:{CombatantUtils.Label(target)} with initial result:{__result}"); AbstractActor targetActor = target as AbstractActor; if (__instance != null && attacker != null && targetActor != null) { float distance = Vector3.Distance(attackPosition, targetPosition); // Cache these EWState attackerState = new EWState(attacker); EWState targetState = new EWState(targetActor); // If we can't see the target, apply the No Visuals penalty bool canSpotTarget = VisualLockHelper.CanSpotTarget(attacker, attacker.CurrentPosition, target, target.CurrentPosition, target.CurrentRotation, attacker.Combat.LOS); int mimeticMod = targetState.MimeticAttackMod(attackerState); int eyeballAttackMod = canSpotTarget ? mimeticMod : Mod.Config.Attack.NoVisualsPenalty; // Zoom applies independently of visibility (request from Harkonnen) int zoomVisionMod = attackerState.GetZoomVisionAttackMod(weapon, distance); int zoomAttackMod = attackerState.HasZoomVisionToTarget(weapon, distance, lofLevel) ? zoomVisionMod - mimeticMod : Mod.Config.Attack.NoVisualsPenalty; bool hasVisualAttack = (eyeballAttackMod < Mod.Config.Attack.NoVisualsPenalty || zoomAttackMod < Mod.Config.Attack.NoVisualsPenalty); // Sensor attack bucket. Sensors always fallback, so roll everything up and cap int narcAttackMod = targetState.NarcAttackMod(attackerState); int tagAttackMod = targetState.TagAttackMod(attackerState); int ecmShieldAttackMod = targetState.ECMAttackMod(attackerState); int stealthAttackMod = targetState.StealthAttackMod(attackerState, weapon, distance); bool hasSensorAttack = SensorLockHelper.CalculateSharedLock(targetActor, attacker) > SensorScanType.NoInfo; int sensorsAttackMod = Mod.Config.Attack.NoSensorsPenalty; if (hasSensorAttack) { sensorsAttackMod = 0; sensorsAttackMod -= narcAttackMod; sensorsAttackMod -= tagAttackMod; sensorsAttackMod += ecmShieldAttackMod; sensorsAttackMod += stealthAttackMod; } if (sensorsAttackMod > Mod.Config.Attack.NoSensorsPenalty) { sensorsAttackMod = Mod.Config.Attack.NoSensorsPenalty; hasSensorAttack = false; } // Check firing blind if (!hasVisualAttack && !hasSensorAttack) { __result += Mod.Config.Attack.FiringBlindPenalty; } else { __result += (zoomAttackMod < eyeballAttackMod) ? zoomAttackMod : eyeballAttackMod; if (attackerState.HasHeatVisionToTarget(weapon, distance)) { __result += attackerState.GetHeatVisionAttackMod(targetActor, distance, weapon); } __result += sensorsAttackMod; } } }
private static void Postfix(ToHit __instance, ref float __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { if (attacker.HasMovedThisRound && attacker.JumpedLastRound) { __result = __result + (float)CBTMovement.Settings.ToHitSelfJumped; } }
private static void Postfix(ToHit __instance, ref float __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { Mod.Log.Trace("TH:GAM entered"); if (attacker.HasMovedThisRound && attacker.JumpedLastRound) { __result = __result + (float)Mod.Config.ToHitSelfJumped; } }
public static bool Prefix(LineOfSight __instance, ref LineOfFireLevel __result, CombatGameState ___Combat, AbstractActor source, Vector3 sourcePosition, ICombatant target, Vector3 targetPosition, Quaternion targetRotation, out Vector3 collisionWorldPos) { Mod.Log.Trace?.Write($"LOS:GLOFU entered. "); Vector3 forward = targetPosition - sourcePosition; forward.y = 0f; Quaternion rotation = Quaternion.LookRotation(forward); Vector3[] lossourcePositions = source.GetLOSSourcePositions(sourcePosition, rotation); Vector3[] lostargetPositions = target.GetLOSTargetPositions(targetPosition, targetRotation); List <AbstractActor> allActors = new List <AbstractActor>(___Combat.AllActors); allActors.Remove(source); AbstractActor abstractActor = target as AbstractActor; string targetedBuildingGuid = null; if (abstractActor != null) { allActors.Remove(abstractActor); } else { targetedBuildingGuid = target.GUID; } LineSegment lineSegment = new LineSegment(sourcePosition, targetPosition); // Sort the target actors by distance from the source allActors.Sort((AbstractActor x, AbstractActor y) => Vector3.Distance(x.CurrentPosition, sourcePosition).CompareTo(Vector3.Distance(y.CurrentPosition, sourcePosition)) ); float targetPositionDistance = Vector3.Distance(sourcePosition, targetPosition); for (int i = allActors.Count - 1; i >= 0; i--) { if (allActors[i].IsDead || Vector3.Distance(allActors[i].CurrentPosition, sourcePosition) > targetPositionDistance || lineSegment.DistToPoint(allActors[i].CurrentPosition) > allActors[i].Radius * 5f) { // If the actor is // 1) dead // 2) the distance from actor to source is greater than targetPos distance // 3) the distance to the actor is greater than the radious of all actors (?!?) // remove the actor from consideration allActors.RemoveAt(i); } } float sourcePositionsWithLineOfFireToTargetPositions = 0f; // num2 float losTargetPositionsCount = 0f; // num3 float weaponsWithUnobstructedLOF = 0f; // num4 collisionWorldPos = targetPosition; float shortestDistanceFromVectorToIteratedActor = 999999.9f; // num5 Weapon longestRangeWeapon = source.GetLongestRangeWeapon(false, false); float maximumWeaponRangeForSource = (longestRangeWeapon != null) ? longestRangeWeapon.MaxRange : 0f; // MY CHANGE: float adjustedSpotterRange = ___Combat.LOS.GetAdjustedSpotterRange(source, abstractActor); float adjustedSensorRange = ___Combat.LOS.GetAdjustedSensorRange(source, abstractActor); //LowVisibility.Logger.Log($"LineOfSight:GetLineOfFireUncached:pre - using sensorRange:{adjustedSensorRange} instead of spotterRange:{adjustedSpotterRange}. Max weapon range is:{maximumWeaponRangeForSource} "); maximumWeaponRangeForSource = Mathf.Max(maximumWeaponRangeForSource, adjustedSensorRange, adjustedSpotterRange); for (int j = 0; j < lossourcePositions.Length; j++) { // Iterate the source positions (presumably each weapon has different source locations) for (int k = 0; k < lostargetPositions.Length; k++) { // Iterate the target positions (presumably each build/mech has differnet locations) losTargetPositionsCount += 1f; float distanceFromSourceToTarget = Vector3.Distance(lossourcePositions[j], lostargetPositions[k]); if (distanceFromSourceToTarget <= maximumWeaponRangeForSource) { // Possible match, check for collisions lineSegment = new LineSegment(lossourcePositions[j], lostargetPositions[k]); bool canUseDirectAttack = false; Vector3 vector; if (targetedBuildingGuid == null) { // Not a building, so check for compatible actors for (int l = 0; l < allActors.Count; l++) { if (lineSegment.DistToPoint(allActors[l].CurrentPosition) < allActors[l].Radius) { vector = NvMath.NearestPointStrict(lossourcePositions[j], lostargetPositions[k], allActors[l].CurrentPosition); float distanceFromVectorToIteratedActor = Vector3.Distance(vector, allActors[l].CurrentPosition); if (distanceFromVectorToIteratedActor < allActors[l].HighestLOSPosition.y) { // TODO: Could I have this flipped, and .y is the highest y in the path? This is checking for indirect fire? // If the height of the attack is less than the HighestLOSPosition.y value, we have found the match? canUseDirectAttack = true; weaponsWithUnobstructedLOF += 1f; if (distanceFromVectorToIteratedActor < shortestDistanceFromVectorToIteratedActor) { shortestDistanceFromVectorToIteratedActor = distanceFromVectorToIteratedActor; collisionWorldPos = vector; } break; } } } } // If there is a source position with LOS to the target, record it if (__instance.HasLineOfFire(lossourcePositions[j], lostargetPositions[k], targetedBuildingGuid, maximumWeaponRangeForSource, out vector)) { sourcePositionsWithLineOfFireToTargetPositions += 1f; if (targetedBuildingGuid != null) { break; } } else { // There is no LineOfFire between the source and targert position if (canUseDirectAttack) { weaponsWithUnobstructedLOF -= 1f; } float distanceFromVectorToSourcePosition = Vector3.Distance(vector, sourcePosition); if (distanceFromVectorToSourcePosition < shortestDistanceFromVectorToIteratedActor) { shortestDistanceFromVectorToIteratedActor = distanceFromVectorToSourcePosition; // There is a collection somewhere in the path (MAYBE?) collisionWorldPos = vector; } } } } if (targetedBuildingGuid != null && sourcePositionsWithLineOfFireToTargetPositions > 0.5f) { break; } } // If a building, ignore the various positions (WHY?) float ratioSourcePosToTargetPos = (targetedBuildingGuid != null) ? sourcePositionsWithLineOfFireToTargetPositions : (sourcePositionsWithLineOfFireToTargetPositions / losTargetPositionsCount); // "MinRatioFromActors": 0.2, float b = ratioSourcePosToTargetPos - ___Combat.Constants.Visibility.MinRatioFromActors; float ratioDirectAttacksToTargetPositions = Mathf.Min(weaponsWithUnobstructedLOF / losTargetPositionsCount, b); if (ratioDirectAttacksToTargetPositions > 0.001f) { ratioSourcePosToTargetPos -= ratioDirectAttacksToTargetPositions; } //LowVisibility.Logger.Log($"LineOfSight:GetLineOfFireUncached:pre - ratio is:{ratioSourcePosToTargetPos} / direct:{ratioDirectAttacksToTargetPositions} / b:{b}"); // "RatioFullVis": 0.79, // "RatioObstructedVis": 0.41, if (ratioSourcePosToTargetPos >= ___Combat.Constants.Visibility.RatioFullVis) { __result = LineOfFireLevel.LOFClear; } else if (ratioSourcePosToTargetPos >= ___Combat.Constants.Visibility.RatioObstructedVis) { __result = LineOfFireLevel.LOFObstructed; } else { __result = LineOfFireLevel.LOFBlocked; } Mod.Log.Trace?.Write($"LOS:GLOFU LOS result is:{__result}"); return(false); }
public static string GetRangeModifierName(ToHit instance, AbstractActor attacker, Weapon w, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, MeleeAttackType meleeAttackType, bool isCalledShot, int modifier) { if (attacker.EncounterTags.ContainsAny(Core.Settings._C3NetworkEncounterTags) == false) { if (modifier == 0) { return(string.Empty); } return(ToHitModifiersHelper.GetRangeModifierName(instance, attacker, w, target, attackPosition, targetPosition, lofLevel, meleeAttackType, isCalledShot)); } float real_range = Vector3.Distance(attackPosition, targetPosition); float range = real_range; float MinRange = w.MinRange; float ShortRange = w.ShortRange; float MediumRange = w.MediumRange; float LongRange = w.LongRange; float MaxRange = w.MaxRange; string c3_prefix = string.Empty; Vector3 alternateAttackPos = GetC3CachedPos(attacker, target); if (alternateAttackPos != Vector3.zero) { float alternateDist = Vector3.Distance(alternateAttackPos, targetPosition); //Log.Debug?.TWL(0, "GetRangeModifierName "+attacker.PilotableActorDef.ChassisID+" weapon:"+w.defId+" target:"+target.PilotableActorDef.ChassisID+" real_dist:"+ real_range+" alt dist:"+ alternateDist+" max range:"+MaxRange+" modifier:"+modifier); if ((alternateDist < real_range) && (alternateDist < MaxRange)) { c3_prefix = "(C3)"; if (alternateDist < MinRange) { range = MinRange; } else { range = alternateDist; } } } if (string.IsNullOrEmpty(c3_prefix)) { if (modifier == 0) { return(string.Empty); } } if (range < MinRange) { return(c3_prefix + "__/AIM.MIN_RANGE/__ (<" + MinRange + "m)"); } if (range < ShortRange) { return(c3_prefix + "__/AIM.SHORT_RANGE/__" + SmartRange(MinRange, range, ShortRange)); } if (range < MediumRange) { return(c3_prefix + "__/AIM.MED_RANGE/__" + SmartRange(ShortRange, range, MediumRange)); } if (range < LongRange) { return(c3_prefix + "__/AIM.LONG_RANGE/__" + SmartRange(MediumRange, range, LongRange)); } if (range < MaxRange) { return(c3_prefix + "__/AIM.MAX_RANGE/__" + SmartRange(LongRange, range, MaxRange)); } return(c3_prefix + "__/AIM.OUT_OF_RANGE/__ (>" + MaxRange + "m)"); }