private static void ApplyNormalDamageToTarget(Verb_MeleeAttack vMA, Pawn attacker, LocalTargetInfo target) { MRpawntoken token = MainController.GetPawnToken(attacker); Pawn tpawn = target.Thing as Pawn; // Immobile targets receive full damage on normal hits (The only possible result against them) with independence of the Attack Mode selected bool immobile = (target.Thing.def.category != ThingCategory.Pawn) || tpawn.Downed || (tpawn.GetPosture() != PawnPosture.Standing); if (token.amode.solver == 3 && !immobile) { return; // On Disarm Mode we do no damage at all } var DamageInfosToApply = Traverse.Create(vMA).Method("DamageInfosToApply", new Type[] { typeof(LocalTargetInfo) }); foreach (DamageInfo current in (DamageInfosToApply.GetValue <IEnumerable <DamageInfo> >(target))) { if (target.ThingDestroyed) { break; } if (!immobile && (token.amode.solver == 1 || token.amode.solver == 2)) { // On Capture & Stun we do half the damage on normal hits current.SetAmount(Mathf.Max(1, Mathf.RoundToInt(current.Amount * 0.5f))); } target.Thing.TakeDamage(current); } }
private static void ApplyParryDamageToTarget(Verb_MeleeAttack vMA, Pawn attacker, LocalTargetInfo target) { // The main differences between a regular hit and a parried one regarding damage are the following: // - If the target has an active shield with energy it will absorb the FULL damage of the attack // - If the target is wielding a melee weapon it will absorb all the damage of the attack suffering // Original Damage * ParryReduction * (1-Melee Skill) (minimum of 1) of damage // - If the target is unarmed, the damage received will be reduced by ParryReduction MRpawntoken token = MainController.GetPawnToken(attacker); if (token.amode.solver == 3) { return; // On Disarm Mode we do no damage at all on parried hits } bool rollback = false; Pawn tpawn = target.Thing as Pawn; var DamageInfosToApply = Traverse.Create(vMA).Method("DamageInfosToApply", new Type[] { typeof(LocalTargetInfo) }); foreach (DamageInfo current in (DamageInfosToApply.GetValue <IEnumerable <DamageInfo> >(target))) { if (target.ThingDestroyed) { break; } if (token.amode.solver == 1 || token.amode.solver == 2) { // On Capture & Stun we do half the damage on parry hits current.SetAmount(Mathf.Max(1, Mathf.RoundToInt(current.Amount * 0.5f))); } rollback = current.Def.isExplosive; // We briefly active isExplosive to make Vanilla Personal Shields able to intercept melee damage current.Def.isExplosive = true; if (PawnHasActiveAbsorber(current, tpawn)) // We have to do a double check so Apparel absorbers take precedence over wield weapon damage { target.Thing.TakeDamage(current); } else if (tpawn.equipment != null && tpawn.equipment.Primary != null && tpawn.equipment.Primary.def.IsMeleeWeapon) { // The target has an equiped melee weapon after a parry, it will deny any damage to the target but the weapon will suffer some damage itself current.SetAmount(Mathf.Max(1, Mathf.RoundToInt(current.Amount * Constants.ParryReduction * (1f - tpawn.GetStatValue(StatDefOf.MeleeHitChance, true))))); // If the current damage is enough to destroy the weapon we try to drop it instead. If there is not enough room nearby, it will be destroyed if (tpawn.equipment.Primary.HitPoints <= current.Amount) { ThingWithComps thingwithcomps = new ThingWithComps(); if (tpawn.equipment.TryDropEquipment(tpawn.equipment.Primary, out thingwithcomps, tpawn.Position)) { current.Def.isExplosive = rollback; break; } } tpawn.equipment.Primary.TakeDamage(current); } else { // Unarmed defender, it just takes reduced damage current.SetAmount(Mathf.Max(1, Mathf.RoundToInt(current.Amount * Constants.ParryReduction))); target.Thing.TakeDamage(current); } current.Def.isExplosive = rollback; } }
public static void Postfix(Pawn_DraftController controller, ref IEnumerable <Gizmo> __result) { // We add the command toggle corresponding to the token in the value MRpawntoken token = MainController.GetPawnToken(controller.pawn); AttackModeCommand optF = new AttackModeCommand(token); List <Gizmo> list = __result.ToList <Gizmo>(); list.Add(optF); __result = (IEnumerable <Gizmo>)list; }
public AttackModeCommand(MRpawntoken token) { this.Token = token; this.icon = token.amode.icontex; this.defaultLabel = token.amode.label; this.defaultDesc = token.amode.desc; this.hotKey = KeyBindingDefOf.Misc7; this.activateSound = SoundDef.Named("Click"); this.action = token.NextAttackMode; this.groupKey = Constants.CommandGroupKey; }
private static void ApplyCriticalDamageToTarget(Verb_MeleeAttack vMA, Pawn attacker, LocalTargetInfo target) { Pawn tpawn = target.Thing as Pawn; var DamageInfosToApply = Traverse.Create(vMA).Method("DamageInfosToApply", new Type[] { typeof(LocalTargetInfo) }); MRpawntoken token = MainController.GetPawnToken(attacker); switch (token.amode.solver) { case 0: // Kill // Double damage on all applied damage foreach (DamageInfo current in (DamageInfosToApply.GetValue <IEnumerable <DamageInfo> >(target))) { if (target.ThingDestroyed) { break; } current.SetAmount(Mathf.Max(1, Mathf.RoundToInt(current.Amount * 2f))); target.Thing.TakeDamage(current); } break; case 1: // Capture // We replace all damage by an anesthesize effect if (target.Thing.def.category == ThingCategory.Pawn) { HealthUtility.TryAnesthesize(tpawn); } break; case 2: // Stun // We replace ALL damage by a short stun effect instead target.Thing.TakeDamage(new DamageInfo(DamageDefOf.Stun, 20)); break; case 3: // Disarm // We replace ALL damage by removing currently equiped weapon if (tpawn.equipment != null && tpawn.equipment.Primary != null) { ThingWithComps thingwithcomps = new ThingWithComps(); tpawn.equipment.TryDropEquipment(tpawn.equipment.Primary, out thingwithcomps, tpawn.Position); } break; default: Log.Warning(string.Concat(new object[] { "Melee Rebalance: Critical Special effect not registered for ", attacker })); break; } }
private static int ResolveMeleeAttack(Pawn attacker, Thing defender, bool surprise) { // We generate the result of a melee attack based on skills involved and some extra factors // 0: Miss, 1: Hit, 2: Parry, 3: Critical // Surprise attacks ALWAYS hit MRpawntoken atoken = MainController.GetPawnToken(attacker); if (surprise) { atoken.ehc = 1f; return(1); } // Attacks against immobile/downed targets or things that aren't pawns ALWAYS hit and can't trigger special effects Pawn tpawn = defender as Pawn; if (defender.def.category != ThingCategory.Pawn || tpawn.Downed || tpawn.GetPosture() != PawnPosture.Standing) { atoken.ehc = 1f; return(1); } // Against regular targets the effective melee skill of the defender reduces multiplicatively the chance to hit of the attacker. // Max Parry Chances are capped at MaxParryChance. float roll = Rand.Value; float abh = attacker.GetStatValue(StatDefOf.MeleeHitChance, true); float dbh = tpawn.GetStatValue(StatDefOf.MeleeHitChance, true); MRpawntoken dtoken = MainController.GetPawnToken(tpawn); // We reduce the base defensive skill based on the registered parry counters for target pawn. dbh -= Constants.ParryCounterPenalty * (float)dtoken.counter; if (dbh < 0f) { dbh = 0f; } else if (dbh > Constants.MaxParryChance) { dbh = Constants.MaxParryChance; } atoken.ehc = abh * (1f - dbh); //Log.Warning(string.Concat(new object[] // { // attacker,"(BHC ",abh,") tried to hit ",tpawn,"(BHC ",dbh,") with effective hit chance ",atoken.ehc," and rolled ",roll // })); // If the attacker is NOT a player controller entity we choose the attack mode if (!attacker.IsColonistPlayerControlled) { atoken.ChooseAttackMode(tpawn); } if (roll > abh) { return(0); } else { // The attempt was not a miss so we increase the parry counters of the target dtoken.counter++; // Now we check for critical effects if ((atoken.ehc > atoken.amode.threshold) && (roll <= (atoken.ehc * atoken.amode.chance))) { return(3); } if (roll <= atoken.ehc) { return(1); } } return(2); }