Пример #1
0
        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);
        }
Пример #2
0
        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);
        }
Пример #3
0
 public void NextAttackMode()
 {
     this.amode = MainController.GetNextAttackMode(amode);
     return;
 }