public static bool Prefix(Verb_MeleeAttack __instance, ref bool __result) { var verbMA = Traverse.Create(__instance); if (__instance.CasterPawn.stances.FullBodyBusy) { __result = false; return(false); } LocalTargetInfo target = verbMA.Field("currentTarget").GetValue <LocalTargetInfo>(); Thing thing = target.Thing; if (!__instance.CanHitTarget(thing)) { Log.Warning(string.Concat(new object[] { __instance.CasterPawn, " meleed ", thing, " from out of melee position." })); } __instance.CasterPawn.Drawer.rotator.Face(thing.DrawPos); if (!verbMA.Method("IsTargetImmobile", target).GetValue <bool>() && __instance.CasterPawn.skills != null) { __instance.CasterPawn.skills.Learn(SkillDefOf.Melee, 250f, false); } SoundDef soundDef; // We register the attacker and reset its ParryCounters MainController.ResetParryCounter(__instance.CasterPawn); switch (ResolveMeleeAttack(__instance.CasterPawn, thing, verbMA.Field("surpriseAttack").GetValue <bool>())) // 0 Miss, 1 Hit, 2 Parry, 3 Critical { case 1: // Hit __result = true; ApplyNormalDamageToTarget(__instance, __instance.CasterPawn, target); if (thing.def.category == ThingCategory.Building) { soundDef = verbMA.Method("SoundHitBuilding").GetValue <SoundDef>(); } else { soundDef = verbMA.Method("SoundHitPawn").GetValue <SoundDef>(); } break; case 2: // Parry ApplyParryDamageToTarget(__instance, __instance.CasterPawn, target); __result = false; soundDef = SoundDefOf.MetalHitImportant; break; case 3: // Critical ApplyCriticalDamageToTarget(__instance, __instance.CasterPawn, target); __result = true; soundDef = SoundDefOf.Thunder_OnMap; break; default: // Miss __result = false; soundDef = verbMA.Method("SoundMiss").GetValue <SoundDef>(); break; } soundDef.PlayOneShot(new TargetInfo(thing.Position, __instance.CasterPawn.Map, false)); __instance.CasterPawn.Drawer.Notify_MeleeAttackOn(thing); Pawn pawntemp = thing as Pawn; if (pawntemp != null && !pawntemp.Dead) { pawntemp.stances.StaggerFor(95); if (__instance.CasterPawn.MentalStateDef != MentalStateDefOf.SocialFighting || pawntemp.MentalStateDef != MentalStateDefOf.SocialFighting) { pawntemp.mindState.meleeThreat = __instance.CasterPawn; pawntemp.mindState.lastMeleeThreatHarmTick = Find.TickManager.TicksGame; } } __instance.CasterPawn.Drawer.rotator.FaceCell(thing.Position); if (__instance.CasterPawn.caller != null) { __instance.CasterPawn.caller.Notify_DidMeleeAttack(); } return(false); }
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); }
public void NextAttackMode() { this.amode = MainController.GetNextAttackMode(amode); return; }