예제 #1
0
        // smash nearby build stuff =================================================================
        //
        public static bool Smash(this JobDriver_Stumble driver, Zombie zombie, bool checkSmashable, bool onylWhenNotRaging)
        {
            if (zombie.wasMapPawnBefore == false && zombie.IsSuicideBomber == false && zombie.IsTanky == false)
            {
                if (driver.destination.IsValid && checkSmashable == false)
                {
                    return(false);
                }

                if (onylWhenNotRaging && zombie.raging > 0)
                {
                    return(false);
                }
            }

            var building = CanSmash(zombie);

            if (building == null)
            {
                return(false);
            }

            driver.destination = building.Position;

            if (Constants.USE_SOUND && Prefs.VolumeAmbient > 0f)
            {
                var info = SoundInfo.InMap(building);
                SoundDef.Named("ZombieHit").PlayOneShot(info);
            }

            AttackThing(zombie, building, JobDefOf.AttackStatic);
            return(true);
        }
예제 #2
0
        //
        static bool LeanAndDelay(this JobDriver_Stumble driver, Zombie zombie, Pawn eatTargetPawn)
        {
            if (driver.eatDelayCounter == 0)
            {
                if (eatTargetPawn != driver.lastEatTarget)
                {
                    driver.lastEatTarget = eatTargetPawn;
                    zombie.rotationTracker.FaceCell(driver.eatTarget.Position);
                    if (zombie.Drawer.leaner is ZombieLeaner zombieLeaner)
                    {
                        var offset = (driver.eatTarget.Position.ToVector3() - zombie.Position.ToVector3()) * 0.5f;
                        if (offset.magnitude < 1f)
                        {
                            zombieLeaner.extraOffset = offset;
                        }
                    }

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

            driver.eatDelayCounter++;
            if (driver.eatDelayCounter <= EatDelay(driver, zombie))
            {
                return(true);
            }

            driver.eatDelayCounter = 0;
            zombie.raging          = 0;
            return(false);
        }
예제 #3
0
 // invalidate destination if necessary ======================================================
 //
 public static bool ValidDestination(this JobDriver_Stumble driver, Zombie zombie)
 {
     // find out if we still need to check for 0,0 as an invalid location
     if (driver.destination.x == 0 && driver.destination.z == 0)
     {
         driver.destination = IntVec3.Invalid;
     }
     return(zombie.HasValidDestination(driver.destination));
 }
예제 #4
0
        // if we have a valid destination, go there =================================================
        //
        public static void ExecuteMove(this JobDriver_Stumble driver, Zombie zombie, PheromoneGrid grid)
        {
            if (driver.destination.IsValid)
            {
                grid.ChangeZombieCount(zombie.lastGotoPosition, -1);
                grid.ChangeZombieCount(driver.destination, 1);
                zombie.lastGotoPosition = driver.destination;

                zombie.pather.StartPath(driver.destination, PathEndMode.OnCell);
            }
        }
예제 #5
0
        public static bool Mine(this JobDriver_Stumble driver, Zombie zombie, bool allDirections = false)
        {
            if (zombie.miningCounter > 0)
            {
                zombie.miningCounter--;
                return(true);
            }

            var map     = zombie.Map;
            var basePos = zombie.Position;

            var delta = (zombie.wanderDestination.IsValid ? zombie.wanderDestination : zombie.Map.Center) - basePos;
            var idx   = Tools.CellsAroundIndex(delta);

            if (idx == -1)
            {
                return(false);
            }
            var adjacted = GenAdj.AdjacentCellsAround;
            var cells    = allDirections ? adjacted.ToList() : new List <IntVec3>()
            {
                adjacted[idx], adjacted[(idx + 1) % 8], adjacted[(idx + 7) % 8]
            };

            var mineable = cells
                           .Select(c => basePos + c)
                           .Where(c => c.InBounds(map))
                           .Select(c => c.GetFirstThing <Mineable>(map))
                           .FirstOrDefault();

            if (mineable == null)
            {
                return(false);
            }

            zombie.rotationTracker.FaceCell(mineable.Position);
            effecter.Trigger(zombie, mineable);
            var baseDamage = (int)GenMath.LerpDouble(1, 5, 1, 10, Math.Max(1, Tools.StoryTellerDifficulty));
            var damage     = (!mineable.def.building.isNaturalRock) ? baseDamage : baseDamage * 2;

            if (mineable.HitPoints > damage)
            {
                mineable.TakeDamage(new DamageInfo(DamageDefOf.Mining, damage));
            }
            else
            {
                mineable.Destroy(DestroyMode.KillFinalize);
            }

            zombie.miningCounter = (int)GenMath.LerpDouble(1, 5, 180, 90, Math.Max(1, Tools.StoryTellerDifficulty));
            return(true);
        }
예제 #6
0
        // make zombies die if necessary ============================================================
        //
        public static bool ShouldDie(this JobDriver_Stumble driver, Zombie zombie)
        {
            if (zombie.Dead || zombie.Spawned == false)
            {
                driver.EndJobWith(JobCondition.InterruptForced);
                return(true);
            }

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

            if (zombie.IsSuicideBomber)
            {
                if (zombie.bombWillGoOff && zombie.EveryNTick(NthTick.Every10))
                {
                    zombie.bombTickingInterval -= 2f;
                }
                if (zombie.bombTickingInterval <= 0f)
                {
                    zombie.Kill(null);
                    return(true);
                }
            }

            if (zombie.EveryNTick(NthTick.Every10))
            {
                if (ZombieSettings.Values.zombiesDieVeryEasily)
                {
                    if (zombie.hasTankySuit <= 0f && zombie.health.hediffSet.GetHediffs <Hediff_Injury>().Any())
                    {
                        zombie.Kill(null);
                        return(true);
                    }
                }
                else
                {
                    var hediffs = zombie.health.hediffSet.hediffs
                                  .Where(hediff => hediff.def == HediffDefOf.WoundInfection)
                                  .ToArray();
                    foreach (var hediff in hediffs)
                    {
                        zombie.health.RemoveHediff(hediff);
                    }
                }
            }

            return(false);
        }
예제 #7
0
        // during night, drift towards colony =======================================================
        //
        public static void Wander(this JobDriver_Stumble driver, Zombie zombie, PheromoneGrid grid, List <IntVec3> possibleMoves)
        {
            if (driver.destination.IsValid)
            {
                return;
            }

            // check for day/night and dust/dawn
            // during night, zombies drift towards the colonies center
            //
            if (zombie.Map.areaManager.Home[zombie.Position] == false)
            {
                var moveTowardsCenter = false;

                var hour = GenLocalDate.HourOfDay(Find.CurrentMap);
                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;
                }

                if (moveTowardsCenter)
                {
                    var center = zombie.wanderDestination.IsValid ? zombie.wanderDestination : zombie.Map.Center;
                    possibleMoves.Sort((p1, p2) => p1.DistanceToSquared(center).CompareTo(p2.DistanceToSquared(center)));
                    possibleMoves      = possibleMoves.Take(Constants.NUMBER_OF_TOP_MOVEMENT_PICKS).ToList();
                    possibleMoves      = possibleMoves.OrderBy(p => grid.GetZombieCount(p)).ToList();
                    driver.destination = possibleMoves.First();
                    return;
                }
            }

            // random wandering
            var n = possibleMoves.Count();

            driver.destination = possibleMoves[Constants.random.Next(n)];
        }
예제 #8
0
        //
        static bool EatBodyPart(this JobDriver_Stumble driver, Zombie zombie, Pawn eatTargetPawn)
        {
            var bodyPartRecord = FirstEatablePart(eatTargetPawn);

            if (bodyPartRecord == null)
            {
                driver.eatTarget.Destroy(DestroyMode.Vanish);
                return(false);
            }

            var eatTargetAlive     = driver.eatTarget is Pawn && ((Pawn)driver.eatTarget).Dead == false;
            var hediff_MissingPart = (Hediff_MissingPart)HediffMaker.MakeHediff(HediffDefOf.MissingBodyPart, eatTargetPawn, bodyPartRecord);

            hediff_MissingPart.lastInjury = HediffDefOf.Bite;
            hediff_MissingPart.IsFresh    = true;
            eatTargetPawn.health.AddHediff(hediff_MissingPart, null, null);

            var eatTargetStillAlive = driver.eatTarget is Pawn && ((Pawn)driver.eatTarget).Dead == false;

            if (eatTargetAlive && eatTargetStillAlive == false)
            {
                if (PawnUtility.ShouldSendNotificationAbout(eatTargetPawn) && eatTargetPawn.RaceProps.Humanlike)
                {
                    MethodInfo translate;
                    object[]   parameters;
                    var        type = AccessTools.TypeByName("Verse.TranslatorFormattedStringExtensions");
                    if (type != null)
                    {
                        // 1.0
                        translate  = AccessTools.Method(type, "Translate", new Type[] { typeof(string), typeof(NamedArgument), typeof(NamedArgument), typeof(NamedArgument) });
                        parameters = new object[] { "MessageEatenByPredator", new NamedArgument(driver.eatTarget.LabelShort, null), zombie.LabelIndefinite().Named("PREDATOR"), driver.eatTarget.Named("EATEN") };
                    }
                    else
                    {
                        // B19
                        translate  = AccessTools.Method(type, "Translate", new Type[] { typeof(string), typeof(object[]) });
                        parameters = new object[] { "MessageEatenByPredator", new object[] { driver.eatTarget.LabelShort, zombie.LabelIndefinite() } };
                    }
                    var msg = (string)translate.Invoke(null, parameters);
                    Messages.Message(msg.CapitalizeFirst(), zombie, MessageTypeDefOf.NegativeEvent);
                }

                eatTargetPawn.Strip();
            }

            return(true);
        }
예제 #9
0
        // lean in and eat bodies made out of flesh =================================================
        //
        public static bool Eat(this JobDriver_Stumble driver, Zombie zombie, PheromoneGrid grid)
        {
            if (zombie.hasTankyShield != -1f || zombie.hasTankyHelmet != -1f || zombie.hasTankySuit != -1f)
            {
                return(false);
            }

            if (driver.eatTarget != null && driver.eatTarget.Spawned == false)
            {
                driver.eatTarget       = null;
                driver.lastEatTarget   = null;
                driver.eatDelayCounter = 0;
            }
            if (driver.eatTarget == null && grid.GetZombieCount(zombie.Position) <= 2)
            {
                driver.eatTarget = CanIngest(zombie);
            }

            var eatTargetPawn = driver.eatTarget as Pawn ?? (driver.eatTarget as Corpse)?.InnerPawn;

            if (eatTargetPawn != null)
            {
                if (driver.LeanAndDelay(zombie, eatTargetPawn))
                {
                    return(true);
                }

                if (driver.EatBodyPart(zombie, eatTargetPawn))
                {
                    return(true);
                }
            }
            else
            {
                if (zombie.Drawer.leaner is ZombieLeaner zombieLeaner)
                {
                    zombieLeaner.extraOffset = Vector3.zero;
                }
            }

            return(false);
        }
예제 #10
0
        // calculate possible moves =================================================================
        //
        public static List <IntVec3> PossibleMoves(this JobDriver_Stumble driver, Zombie zombie)
        {
            if (driver.destination.IsValid)
            {
                return(new List <IntVec3>());
            }

            var result = new List <IntVec3>(8);
            var pos    = zombie.Position;

            foreach (var vec in GenAdj.AdjacentCells)
            {
                var cell = pos + vec;
                if (zombie.HasValidDestination(cell))
                {
                    result.Add(cell);
                }
            }
            return(result);
        }
예제 #11
0
 static int EatDelay(this JobDriver_Stumble driver, Zombie zombie)
 {
     if (driver.eatDelay == 0)
     {
         driver.eatDelay = Constants.EAT_DELAY_TICKS;
         var bodyType = zombie.story.bodyType;
         if (bodyType == BodyTypeDefOf.Thin)
         {
             driver.eatDelay *= 3;
         }
         else if (bodyType == BodyTypeDefOf.Hulk)
         {
             driver.eatDelay /= 2;
         }
         else if (bodyType == BodyTypeDefOf.Fat)
         {
             driver.eatDelay /= 4;
         }
     }
     return(driver.eatDelay);
 }
예제 #12
0
        // attack nearby enemies ====================================================================
        //
        public static bool Attack(this JobDriver_Stumble driver, Zombie zombie)
        {
            var enemy = CanAttack(zombie);

            if (enemy == null)
            {
                return(false);
            }

            driver.destination = enemy.Position;

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

            AttackThing(zombie, enemy, JobDefOf.AttackMelee);
            return(true);
        }
예제 #13
0
        // use rage grid to get to colonists ========================================================
        //
        public static bool RageMove(this JobDriver_Stumble driver, Zombie zombie, PheromoneGrid grid, List <IntVec3> possibleMoves, bool checkSmashable)
        {
            var info   = ZombieWanderer.GetMapInfo(zombie.Map);
            var newPos = info.GetParent(zombie.Position, false);

            if (newPos.IsValid == false)
            {
                // tanky can get directly through walls
                if (zombie.IsTanky)
                {
                    newPos = info.GetParent(zombie.Position, true);
                }

                if (newPos.IsValid == false)
                {
                    // no next move available
                    zombie.raging = 0;
                    return(Smash(driver, zombie, checkSmashable, false));
                }
            }

            // next tanky move is on a building
            if (newPos.GetEdifice(zombie.Map) is Building building && (building as Mineable) == null)
            {
                return(Smash(driver, zombie, checkSmashable, false));
            }

            // next move is on a door
            if (newPos.GetEdifice(zombie.Map) is Building_Door door)
            {
                if (door.Open)
                {
                    driver.destination = newPos;
                    return(false);
                }
                return(Smash(driver, zombie, checkSmashable, false));
            }

            // move into places where there is max 0/1 zombie already
            var destZombieCount = grid.GetZombieCount(newPos);

            if (destZombieCount < (zombie.IsTanky ? 1 : 2))
            {
                driver.destination = newPos;
                return(false);
            }

            // cannot move? lets smash things
            if (Smash(driver, zombie, checkSmashable, false))
            {
                return(true);
            }

            // cannot smash? look for alternative ways to move orthogonal
            if (TryToDivert(ref newPos, grid, zombie.Position, possibleMoves))
            {
                driver.destination = newPos;
                return(false);
            }

            // move to least populated place
            var zCount = possibleMoves.Select(p => grid.GetZombieCount(p)).Min();

            driver.destination = possibleMoves.Where(p => grid.GetZombieCount(p) == zCount).RandomElement();
            return(false);
        }
예제 #14
0
        public static bool Track(this JobDriver_Stumble driver, Zombie zombie, PheromoneGrid grid)
        {
            if (zombie.EveryNTick(NthTick.Every60) || fadeOff == -1)
            {
                fadeOff                = Tools.PheromoneFadeoff();
                wasColonistFadeoff     = fadeOff / 6;
                agitatedFadeoff        = fadeOff / 4;
                checkSmashableFadeoff1 = agitatedFadeoff / 4;
                checkSmashableFadeoff2 = agitatedFadeoff * 3 / 4;
            }

            var trackingMoves = new List <IntVec3>(8);
            var currentTicks  = Tools.Ticks();
            var timeDelta     = long.MaxValue;

            var fmin = long.MaxValue;

            if (zombie.raging == 0)
            {
                for (var i = 0; i < 8; i++)
                {
                    var pos = zombie.Position + GenAdj.AdjacentCells[i];
                    if (zombie.HasValidDestination(pos))
                    {
                        var f     = zombie.wasMapPawnBefore ? wasColonistFadeoff : fadeOff;
                        var tdiff = currentTicks - grid.GetTimestamp(pos);
                        fmin = Math.Min(fmin, tdiff);
                        if (tdiff < f)
                        {
                            trackingMoves.Add(pos);
                        }
                    }
                }
            }

            if (trackingMoves.Count > 0)
            {
                trackingMoves.Sort((p1, p2) => grid.GetTimestamp(p2).CompareTo(grid.GetTimestamp(p1)));
                trackingMoves = trackingMoves.Take(Constants.NUMBER_OF_TOP_MOVEMENT_PICKS).ToList();
                trackingMoves = trackingMoves.OrderBy(p => grid.GetZombieCount(p)).ToList();
                var nextMove = trackingMoves.First();
                timeDelta = currentTicks - (grid.GetTimestamp(nextMove));

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

            if (driver.destination.IsValid == false)
            {
                zombie.state = ZombieState.Wandering;
            }

            if (zombie.wasMapPawnBefore)
            {
                return(true);
            }

            var checkSmashable = timeDelta >= checkSmashableFadeoff1 && timeDelta < checkSmashableFadeoff2;

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

            return(checkSmashable);
        }