Пример #1
0
        private static float getCacheElementResult(PawnCapacitiesHandler __instance, PawnCapacityDef capacity)
        {
            if (capacity == null) //ADDED
            {
                return(0f);
            }
            CacheElement2 cacheElement = get_cacheElement(__instance, capacity); //ADDED

            lock (cacheElement)                                                  //ADDED
            {
                if (cacheElement.status == CacheStatus.Caching)
                {
                    Log.Error($"Detected infinite stat recursion when evaluating {capacity}");
                    return(0f);
                }

                if (cacheElement.status == CacheStatus.Uncached)
                {
                    cacheElement.status = CacheStatus.Caching;
                    try
                    {
                        cacheElement.value = PawnCapacityUtility.CalculateCapacityLevel(pawn(__instance).health.hediffSet, capacity);
                    }
                    finally
                    {
                        cacheElement.status = CacheStatus.Cached;
                    }
                }
            }
            return(cacheElement.value);
        }
Пример #2
0
        private static string GetCapacityReport(Pawn subject, PawnCapacityDef capacity)
        {
            var payload = $"{capacity.GetLabelFor(subject).CapitalizeFirst()}: ";

            if (!PawnCapacityUtility.BodyCanEverDoCapacity(subject.RaceProps.body, capacity))
            {
                payload += $"{Find.ActiveLanguageWorker.Pluralize(subject.kindDef.race.defName)} ";
                payload += $" are incapable of {capacity.GetLabelFor(subject)}";

                return(payload);
            }

            var impactors = new List <PawnCapacityUtility.CapacityImpactor>();

            payload += PawnCapacityUtility.CalculateCapacityLevel(subject.health.hediffSet, capacity, impactors)
                       .ToStringPercent();
            payload += " | ";

            if (!impactors.Any())
            {
                return($"{payload}No health conditions");
            }

            var segments = new List <string>();

            foreach (var i in impactors)
            {
                if (i is PawnCapacityUtility.CapacityImpactorHediff)
                {
                    segments.Add(i.Readable(subject));
                }
            }

            foreach (var i in impactors)
            {
                if (i is PawnCapacityUtility.CapacityImpactorBodyPartHealth)
                {
                    segments.Add(i.Readable(subject));
                }
            }

            foreach (var i in impactors)
            {
                if (i is PawnCapacityUtility.CapacityImpactorCapacity)
                {
                    segments.Add(i.Readable(subject));
                }
            }

            foreach (var i in impactors)
            {
                if (i is PawnCapacityUtility.CapacityImpactorPain)
                {
                    segments.Add(i.Readable(subject));
                }
            }

            return($"{payload} | {string.Join(", ", segments.ToArray())}");
        }
Пример #3
0
        private static string MyPawnHealthCapacity(Viewer viewer, Pawn pawn, PawnCapacityDef capacityDef)
        {
            if (PawnCapacityUtility.BodyCanEverDoCapacity(pawn.RaceProps.body, capacityDef))
            {
                Pair <string, Color> efficiencyLabel = HealthCardUtility.GetEfficiencyLabel(pawn, capacityDef);

                List <PawnCapacityUtility.CapacityImpactor> impactorList = new List <PawnCapacityUtility.CapacityImpactor>();
                float  fLevel = PawnCapacityUtility.CalculateCapacityLevel(pawn.health.hediffSet, capacityDef, impactorList);
                string sLevel = (fLevel * 100.0f).ToString("F0") + "%";

                string output = $"@{viewer.username} {pawn.Name.ToStringShort.CapitalizeFirst()}'s {capacityDef.LabelCap}: {efficiencyLabel.First} ({sLevel})";
                output += " - " + "AffectedBy".Translate() + " ";
                if (impactorList.Count > 0)
                {
                    for (int i = 0; i < impactorList.Count; i++)
                    {
                        if (impactorList[i] is PawnCapacityUtility.CapacityImpactorHediff)
                        {
                            output += impactorList[i].Readable(pawn) + ", ";
                        }
                    }
                    for (int i = 0; i < impactorList.Count; i++)
                    {
                        if (impactorList[i] is PawnCapacityUtility.CapacityImpactorBodyPartHealth)
                        {
                            output += impactorList[i].Readable(pawn) + ", ";
                        }
                    }
                    for (int i = 0; i < impactorList.Count; i++)
                    {
                        if (impactorList[i] is PawnCapacityUtility.CapacityImpactorCapacity)
                        {
                            output += impactorList[i].Readable(pawn) + ", ";
                        }
                    }
                    for (int i = 0; i < impactorList.Count; i++)
                    {
                        if (impactorList[i] is PawnCapacityUtility.CapacityImpactorPain)
                        {
                            output += impactorList[i].Readable(pawn) + ", ";
                        }
                    }
                    output = output.Substring(0, output.Length - 2);
                }
                else
                {
                    output += $"nothing ";
                }
                return(output);
            }
            else
            {
                return($"@{viewer.username} {pawn.Name.ToStringShort.CapitalizeFirst()} Can not have {capacityDef.LabelCap}.");
            }
        }
Пример #4
0
        // handle downed zombies ====================================================================
        //
        public static bool Downed(Zombie zombie)
        {
            if (zombie.IsDowned() == false)
            {
                return(false);
            }

            if (ZombieSettings.Values.zombiesDieVeryEasily || zombie.IsSuicideBomber || ZombieSettings.Values.doubleTapRequired == false)
            {
                zombie.Kill(null);
                return(true);
            }

            var walkCapacity = PawnCapacityUtility.CalculateCapacityLevel(zombie.health.hediffSet, PawnCapacityDefOf.Moving);
            var missingBrain = zombie.health.hediffSet.GetBrain() == null;

            if (walkCapacity < 0.25f || missingBrain)
            {
                zombie.Kill(null);
                return(true);
            }

            if (++zombie.healCounter >= Constants.ZOMBIE_HEALING_TICKS)
            {
                zombie.healCounter = 0;

                var injuries = zombie.health.hediffSet.GetHediffs <Hediff_Injury>();
                foreach (var injury in injuries)
                {
                    if (ZombieSettings.Values.zombiesDieVeryEasily)
                    {
                        zombie.Kill(null);
                        return(true);
                    }

                    if (Tools.IsCombatExtendedInstalled())
                    {
                        injury.Heal(combatExtendedHealAmount);
                    }
                    else
                    {
                        injury.Heal(injury.Severity + 0.5f);
                    }

                    break;
                }
            }

            return(false);
        }
Пример #5
0
        public static string Postfix(string __result, Pawn pawn, PawnCapacityDef capacity)
        {
            // So this might look a bit like dark magic, but what's happening is that the impactors list is populated
            // in CalculateCapacityLevel. Why isn't it an out parameter? Because it's optional.
            var impactors = new List <PawnCapacityUtility.CapacityImpactor>();

            PawnCapacityUtility.CalculateCapacityLevel(pawn.health.hediffSet, capacity, impactors);
            var sb = new StringBuilder();

            foreach (var impactor in impactors.FindAll(impact => impact is CapacityImpactorPsychic))
            {
                sb.AppendLine($"  {impactor.Readable(pawn)}");
            }

            __result += sb;
            return(__result);
        }
Пример #6
0
        public static string GetPawnCapacityTip(Pawn pawn, PawnCapacityDef capacity)
        {
            List <PawnCapacityUtility.CapacityImpactor> list = new List <PawnCapacityUtility.CapacityImpactor>();
            float eff = PawnCapacityUtility.CalculateCapacityLevel(pawn.health.hediffSet, capacity, list);

            PawnCapacityUtility.CapacityImpactorCapacity capacityImpactorCapacity;
            list.RemoveAll((PawnCapacityUtility.CapacityImpactor x) => (capacityImpactorCapacity = x as PawnCapacityUtility.CapacityImpactorCapacity) != null && !capacityImpactorCapacity.capacity.CanShowOnPawn(pawn));
            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.AppendLine(capacity.GetLabelFor(pawn).CapitalizeFirst() + ": " + GetEfficiencyEstimateLabel(eff));
            if (list.Count > 0)
            {
                stringBuilder.AppendLine();
                stringBuilder.AppendLine("AffectedBy".Translate());
                for (int i = 0; i < list.Count; i++)
                {
                    if (list[i] is PawnCapacityUtility.CapacityImpactorHediff)
                    {
                        stringBuilder.AppendLine($"  {list[i].Readable(pawn)}");
                    }
                }
                for (int j = 0; j < list.Count; j++)
                {
                    if (list[j] is PawnCapacityUtility.CapacityImpactorBodyPartHealth)
                    {
                        stringBuilder.AppendLine($"  {list[j].Readable(pawn)}");
                    }
                }
                for (int k = 0; k < list.Count; k++)
                {
                    if (list[k] is PawnCapacityUtility.CapacityImpactorCapacity)
                    {
                        stringBuilder.AppendLine($"  {list[k].Readable(pawn)}");
                    }
                }
                for (int l = 0; l < list.Count; l++)
                {
                    if (list[l] is PawnCapacityUtility.CapacityImpactorPain)
                    {
                        stringBuilder.AppendLine($"  {list[l].Readable(pawn)}");
                    }
                }
            }
            return(stringBuilder.ToString());
        }
Пример #7
0
        public static void Patch_HealthCardUtility_GetPawnCapacityTip(ref string __result, Pawn pawn, PawnCapacityDef capacity)
        {
            List <PawnCapacityUtility.CapacityImpactor> list = new List <PawnCapacityUtility.CapacityImpactor>();

            PawnCapacityUtility.CalculateCapacityLevel(pawn.health.hediffSet, capacity, list);
            if (list.Count > 0)
            {
                StringBuilder stringBuilder = new StringBuilder();
                for (int i = 0; i < list.Count; i++)
                {
                    if (list[i] is CapacityImpactorRune)
                    {
                        stringBuilder.AppendLine(string.Format("  {0}", list[i].Readable(pawn)));
                    }
                }

                __result += stringBuilder.ToString();
            }
        }
Пример #8
0
        public static string GetPawnCapacityTip(Pawn pawn, PawnCapacityDef capacity)
        {
            List <PawnCapacityUtility.CapacityImpactor> list = new List <PawnCapacityUtility.CapacityImpactor>();
            float         num           = PawnCapacityUtility.CalculateCapacityLevel(pawn.health.hediffSet, capacity, list);
            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.AppendLine(capacity.LabelCap + ": " + ((float)(num * 100.0)).ToString("F0") + "%");
            if (list.Count > 0)
            {
                stringBuilder.AppendLine();
                stringBuilder.AppendLine("AffectedBy".Translate());
                for (int i = 0; i < list.Count; i++)
                {
                    if (list[i] is PawnCapacityUtility.CapacityImpactorHediff)
                    {
                        stringBuilder.AppendLine(string.Format("  {0}", list[i].Readable(pawn)));
                    }
                }
                for (int j = 0; j < list.Count; j++)
                {
                    if (list[j] is PawnCapacityUtility.CapacityImpactorBodyPartHealth)
                    {
                        stringBuilder.AppendLine(string.Format("  {0}", list[j].Readable(pawn)));
                    }
                }
                for (int k = 0; k < list.Count; k++)
                {
                    if (list[k] is PawnCapacityUtility.CapacityImpactorCapacity)
                    {
                        stringBuilder.AppendLine(string.Format("  {0}", list[k].Readable(pawn)));
                    }
                }
                for (int l = 0; l < list.Count; l++)
                {
                    if (list[l] is PawnCapacityUtility.CapacityImpactorPain)
                    {
                        stringBuilder.AppendLine(string.Format("  {0}", list[l].Readable(pawn)));
                    }
                }
            }
            return(stringBuilder.ToString());
        }
Пример #9
0
        public static float PawnQualityPriceFactor(Pawn pawn, StringBuilder explanation = null)
        {
            float num = 1f;

            num *= Mathf.Lerp(0.199999988f, 1f, pawn.health.summaryHealth.SummaryHealthPercent);
            List <PawnCapacityDef> allDefsListForReading = DefDatabase <PawnCapacityDef> .AllDefsListForReading;

            for (int i = 0; i < allDefsListForReading.Count; i++)
            {
                if (!pawn.health.capacities.CapableOf(allDefsListForReading[i]))
                {
                    num *= 0.6f;
                    continue;
                }
                float t = PawnCapacityUtility.CalculateCapacityLevel(pawn.health.hediffSet, allDefsListForReading[i], null, forTradePrice: true);
                num *= Mathf.Lerp(0.5f, 1f, t);
            }
            if (pawn.skills != null)
            {
                num *= AverageSkillCurve.Evaluate((float)pawn.skills.skills.Average((SkillRecord sk) => sk.Level));
            }
            num *= pawn.ageTracker.CurLifeStage.marketValueFactor;
            if (pawn.story != null && pawn.story.traits != null)
            {
                for (int j = 0; j < pawn.story.traits.allTraits.Count; j++)
                {
                    Trait trait = pawn.story.traits.allTraits[j];
                    num += trait.CurrentData.marketValueFactorOffset;
                }
            }
            num += pawn.GetStatValue(StatDefOf.PawnBeauty) * 0.2f;
            if (num < 0.1f)
            {
                num = 0.1f;
            }
            explanation?.AppendLine("StatsReport_CharacterQuality".Translate() + ": x" + num.ToStringPercent());
            return(num);
        }
Пример #10
0
        private static string HealthCapacityReport([NotNull] Pawn pawn, [NotNull] PawnCapacityDef capacity)
        {
            if (!PawnCapacityUtility.BodyCanEverDoCapacity(pawn.RaceProps.body, capacity))
            {
                return("TKUtils.PawnHealth.IncapableOfCapacity".LocalizeKeyed(capacity.GetLabelFor(pawn)));
            }

            var impactors = new List <PawnCapacityUtility.CapacityImpactor>();

            var segments = new List <string>
            {
                ResponseHelper.JoinPair(
                    RichTextHelper.StripTags(capacity.LabelCap),
                    PawnCapacityUtility.CalculateCapacityLevel(pawn.health.hediffSet, capacity, impactors).ToStringPercent()
                    ),
                impactors.Any()
                    ? ResponseHelper.JoinPair("TKUtils.PawnHealth.AffectedBy".Localize(), GetImpactorsForPawn(pawn, impactors).SectionJoin())
                    : "NoHealthConditions".Localize().CapitalizeFirst()
            };


            return(segments.GroupedJoin());
        }
Пример #11
0
        public static int CapableColonists(Map map)
        {
            var colonists = map.mapPawns.FreeHumanlikesSpawnedOfFaction(Faction.OfPlayer);

            return(colonists.Count(pawn =>
            {
                if (pawn.Spawned == false || pawn.Downed || pawn.Dead)
                {
                    return false;
                }
                if (pawn.health.HasHediffsNeedingTend(true))
                {
                    return false;
                }
                if (pawn.equipment.Primary == null)
                {
                    return false;
                }
                if (pawn.InMentalState)
                {
                    return false;
                }
                if (pawn.InContainerEnclosed)
                {
                    return false;
                }

                var walkCapacity = PawnCapacityUtility.CalculateCapacityLevel(pawn.health.hediffSet, PawnCapacityDefOf.Moving);
                if (walkCapacity < 0.25f)
                {
                    return false;
                }

                return true;
            }));
        }
Пример #12
0
 public static bool GetLevel(PawnCapacitiesHandler __instance, ref float __result, PawnCapacityDef capacity)
 {
     if (__instance.pawn.health.Dead)
     {
         __result = 0f;
         return(false);
     }
     if (__instance.cachedCapacityLevels == null)
     {
         __instance.Notify_CapacityLevelsDirty();
     }
     lock (__instance)
     {
         CacheElement cacheElement = __instance.cachedCapacityLevels[capacity];
         if (cacheElement.status == CacheStatus.Caching)
         {
             Log.Error($"Detected infinite stat recursion when evaluating {capacity}");
             __result = 0f;
             return(false);
         }
         if (cacheElement.status == CacheStatus.Uncached)
         {
             cacheElement.status = CacheStatus.Caching;
             try
             {
                 cacheElement.value = PawnCapacityUtility.CalculateCapacityLevel(__instance.pawn.health.hediffSet, capacity);
             }
             finally
             {
                 cacheElement.status = CacheStatus.Cached;
             }
         }
         __result = cacheElement.value;
         return(false);
     }
 }
Пример #13
0
        public static Pair <string, Color> GetEfficiencyLabel(Pawn pawn, PawnCapacityDef activity)
        {
            float level = pawn.health.capacities.GetLevel(activity);

            return(new Pair <string, Color>(PawnCapacityUtility.CalculateCapacityLevel(pawn.health.hediffSet, activity).ToStringPercent(), efficiencyToColor[EfficiencyValueToEstimate(level)]));
        }
Пример #14
0
        void TickAction()
        {
            var fadeOff               = Tools.PheromoneFadeoff();
            var agitatedFadeoff       = fadeOff / 4;
            var checkSmashableFadeoff = agitatedFadeoff / 2;

            var zombie = (Zombie)pawn;

            if (zombie.state == ZombieState.Emerging)
            {
                return;
            }
            var map = zombie.Map;

            if (zombie.Dead || zombie.Destroyed)
            {
                EndJobWith(JobCondition.InterruptForced);
                return;
            }

            if (zombie.state == ZombieState.ShouldDie)
            {
                EndJobWith(JobCondition.InterruptForced);
                zombie.Kill(null);
                return;
            }

            if (ZombieSettings.Values.zombiesDieVeryEasily)
            {
                if (zombie.health.hediffSet.GetHediffs <Hediff_Injury>().Any())
                {
                    zombie.Kill(null);
                    return;
                }
            }

            if (zombie.Downed)
            {
                if (ZombieSettings.Values.zombiesDieVeryEasily || ZombieSettings.Values.doubleTapRequired == false)
                {
                    zombie.Kill(null);
                    return;
                }

                var walkCapacity = PawnCapacityUtility.CalculateCapacityLevel(zombie.health.hediffSet, PawnCapacityDefOf.Moving);
                var missingBrain = zombie.health.hediffSet.GetBrain() == null;
                if (walkCapacity < 0.25f || missingBrain)
                {
                    zombie.Kill(null);
                    return;
                }

                var injuries = zombie.health.hediffSet.GetHediffs <Hediff_Injury>();
                foreach (var injury in injuries)
                {
                    if (ZombieSettings.Values.zombiesDieVeryEasily)
                    {
                        zombie.Kill(null);
                        return;
                    }

                    if (injury.IsOld() == false)
                    {
                        injury.Heal(injury.Severity + 0.5f);
                        break;
                    }
                }

                if (zombie.Downed)
                {
                    return;
                }
            }

            // handling invalid destinations
            //
            if (destination.x == 0 && destination.z == 0)
            {
                destination = IntVec3.Invalid;
            }
            if (zombie.HasValidDestination(destination))
            {
                return;
            }

            // if we are near targets then attack them
            //
            var enemy = CanAttack();

            if (enemy != null)
            {
                destination = enemy.Position;

                zombie.state = ZombieState.Tracking;
                if (Constants.USE_SOUND)
                {
                    var info = SoundInfo.InMap(enemy);
                    SoundDef.Named("ZombieHit").PlayOneShot(info);
                }

                AttackThing(enemy, JobDefOf.AttackMelee);
                return;
            }

            // eat a downed or dead pawn
            //
            if (eatTarget == null)
            {
                eatTarget = CanIngest(out eatTargetIsCorpse);
                if (eatTarget != null)
                {
                    eatTargetDied = eatTarget.Dead;
                }
            }

            if (eatTarget != null)
            {
                if (eatDelayCounter == 0)
                {
                    if (eatTarget != lastEatTarget)
                    {
                        lastEatTarget = eatTarget;
                        zombie.Drawer.rotator.FaceCell(eatTarget.Position);
                        var zombieLeaner = zombie.Drawer.leaner as ZombieLeaner;
                        if (zombieLeaner != null)
                        {
                            zombieLeaner.extraOffset = (eatTarget.Position.ToVector3() - zombie.Position.ToVector3()) * 0.5f;
                        }

                        Tools.CastThoughtBubble(pawn, Constants.EATING);
                    }
                    CastEatingSound();
                }

                eatDelayCounter++;
                if (eatDelayCounter <= EatDelay)
                {
                    return;
                }
                eatDelayCounter = 0;

                var bodyPartRecord = FirstEatablePart(eatTarget);
                if (bodyPartRecord != null)
                {
                    var hediff_MissingPart = (Hediff_MissingPart)HediffMaker.MakeHediff(HediffDefOf.MissingBodyPart, eatTarget, bodyPartRecord);
                    hediff_MissingPart.lastInjury = HediffDefOf.Bite;
                    hediff_MissingPart.IsFresh    = true;
                    eatTarget.health.AddHediff(hediff_MissingPart, null, null);

                    if (eatTargetIsCorpse == false && eatTargetDied == false && eatTarget.Dead)
                    {
                        Tools.DoWithAllZombies(map, z =>
                        {
                            if (z.jobs != null)
                            {
                                var driver = z.jobs.curDriver as JobDriver_Stumble;
                                if (driver != null && driver.eatTarget == eatTarget)
                                {
                                    driver.eatTargetDied     = true;
                                    driver.eatTargetIsCorpse = true;
                                }
                            }
                        });

                        if (PawnUtility.ShouldSendNotificationAbout(eatTarget) && eatTarget.RaceProps.Humanlike)
                        {
                            Messages.Message("MessageEatenByPredator".Translate(new object[]
                            {
                                eatTarget.LabelShort,
                                zombie.LabelIndefinite()
                            }).CapitalizeFirst(), zombie, MessageSound.Negative);
                        }

                        eatTarget.Strip();
                    }

                    return;
                }
                else
                {
                    var corpse = map.thingGrid
                                 .ThingsListAt(eatTarget.Position)
                                 .OfType <Corpse>()
                                 .FirstOrDefault(c => c.InnerPawn == eatTarget);
                    if (corpse != null)
                    {
                        corpse.Destroy(DestroyMode.Vanish);
                    }

                    Tools.DoWithAllZombies(map, z =>
                    {
                        if (z.jobs != null)
                        {
                            var driver = z.jobs.curDriver as JobDriver_Stumble;
                            if (driver != null && driver.eatTarget == eatTarget)
                            {
                                driver.eatTarget       = null;
                                driver.lastEatTarget   = null;
                                driver.eatDelayCounter = 0;
                            }
                        }
                    });
                }
            }
            else
            {
                var zombieLeaner = zombie.Drawer.leaner as ZombieLeaner;
                if (zombieLeaner != null)
                {
                    zombieLeaner.extraOffset = Vector3.zero;
                }
            }

            var basePos = zombie.Position;

            // calculate possible moves, sort by pheromone value and take top 3
            // then choose the one with the lowest zombie count
            // also, emit a circle of timestamps when discovering a pheromone
            // trace so nearby zombies pick it up too (leads to a chain reaction)
            //
            var grid = zombie.Map.GetGrid();
            var possibleTrackingMoves = new List <IntVec3>();
            var currentTicks          = Tools.Ticks();
            var timeDelta             = long.MaxValue;

            for (int i = 0; i < 8; i++)
            {
                var pos = basePos + GenAdj.AdjacentCells[i];
                if (currentTicks - grid.Get(pos, false).timestamp < fadeOff && zombie.HasValidDestination(pos))
                {
                    possibleTrackingMoves.Add(pos);
                }
            }
            if (possibleTrackingMoves.Count > 0)
            {
                possibleTrackingMoves.Sort((p1, p2) => SortByTimestamp(grid, p1, p2));
                possibleTrackingMoves = possibleTrackingMoves.Take(Constants.NUMBER_OF_TOP_MOVEMENT_PICKS).ToList();
                possibleTrackingMoves = possibleTrackingMoves.OrderBy(p => grid.Get(p, false).zombieCount).ToList();
                var nextMove = possibleTrackingMoves.First();
                timeDelta = currentTicks - grid.Get(nextMove, false).timestamp;

                destination = nextMove;
                if (zombie.state == ZombieState.Wandering)
                {
                    Tools.ChainReact(zombie.Map, basePos, nextMove);
                    if (timeDelta <= agitatedFadeoff)
                    {
                        CastBrainzThought();
                    }
                }
                zombie.state = ZombieState.Tracking;
            }
            if (destination.IsValid == false)
            {
                zombie.state = ZombieState.Wandering;
            }

            bool checkSmashable = timeDelta >= checkSmashableFadeoff;

            if (ZombieSettings.Values.smashOnlyWhenAgitated)
            {
                checkSmashable &= zombie.state == ZombieState.Tracking;
            }

            if (destination.IsValid == false || checkSmashable)
            {
                var building = CanSmash();
                if (building != null)
                {
                    destination = building.Position;

                    if (Constants.USE_SOUND)
                    {
                        var info = SoundInfo.InMap(enemy);
                        SoundDef.Named("ZombieHit").PlayOneShot(info);
                    }

                    AttackThing(building, JobDefOf.AttackStatic);
                    return;
                }
            }

            if (destination.IsValid == false)
            {
                var hour = GenLocalDate.HourOfDay(Find.VisibleMap);

                // check for day/night and dust/dawn
                //
                var moveTowardsCenter = false;
                if (map.areaManager.Home[basePos] == false)
                {
                    if (hour < 12)
                    {
                        hour += 24;
                    }
                    if (hour > Constants.HOUR_START_OF_NIGHT && hour < Constants.HOUR_END_OF_NIGHT)
                    {
                        moveTowardsCenter = true;
                    }
                    else if (hour >= Constants.HOUR_START_OF_DUSK && hour <= Constants.HOUR_START_OF_NIGHT)
                    {
                        moveTowardsCenter = Rand.RangeInclusive(hour, Constants.HOUR_START_OF_NIGHT) == Constants.HOUR_START_OF_NIGHT;
                    }
                    else if (hour >= Constants.HOUR_END_OF_NIGHT && hour <= Constants.HOUR_START_OF_DAWN)
                    {
                        moveTowardsCenter = Rand.RangeInclusive(Constants.HOUR_END_OF_NIGHT, hour) == Constants.HOUR_END_OF_NIGHT;
                    }
                }

                var possibleMoves = new List <IntVec3>();
                for (int i = 0; i < 8; i++)
                {
                    var pos = basePos + GenAdj.AdjacentCells[i];
                    if (zombie.HasValidDestination(pos))
                    {
                        possibleMoves.Add(pos);
                    }
                }
                if (possibleMoves.Count > 0)
                {
                    // during night, zombies drift towards the colonies center
                    //
                    if (moveTowardsCenter)
                    {
                        var center = zombie.wanderDestination.IsValid ? zombie.wanderDestination : map.Center;
                        possibleMoves.Sort((p1, p2) => SortByDirection(center, p1, p2));
                        possibleMoves = possibleMoves.Take(Constants.NUMBER_OF_TOP_MOVEMENT_PICKS).ToList();
                        possibleMoves = possibleMoves.OrderBy(p => grid.Get(p, false).zombieCount).ToList();
                        destination   = possibleMoves.First();
                    }
                    else
                    {
                        // otherwise they sometimes stand or walk towards a random direction
                        //
                        if (Rand.Chance(Constants.STANDING_STILL_CHANCE))
                        {
                            var n = possibleMoves.Count();
                            destination = possibleMoves[Constants.random.Next(n)];
                        }
                    }
                }
            }

            // if we have a valid destination, go there
            //
            if (destination.IsValid)
            {
                MoveToCell(destination);
            }
        }