diff --git a/1.4/Assemblies/ZombieLand.dll b/1.4/Assemblies/ZombieLand.dll index 126813f3..393d5fec 100644 Binary files a/1.4/Assemblies/ZombieLand.dll and b/1.4/Assemblies/ZombieLand.dll differ diff --git a/About/Manifest.xml b/About/Manifest.xml index 0798114b..d979f64a 100644 --- a/About/Manifest.xml +++ b/About/Manifest.xml @@ -1,7 +1,7 @@  net.pardeike.rimworld.mod.zombieland - 3.3.1.0 + 3.4.0.0
  • 1.0.0
  • 1.1.0
  • diff --git a/Source/TickManager.cs b/Source/TickManager.cs index 02376dcc..173fcd01 100644 --- a/Source/TickManager.cs +++ b/Source/TickManager.cs @@ -28,7 +28,24 @@ public static class ZombieTicker public static void DoSingleTick() { if (RimThreaded == null) - managers.Do(tickManager => tickManager.ZombieTicking()); + managers.Do(tickManager => + { + switch (tickManager.isInitialized) + { + case 0: + Log.Error("Fatal error! Zombieland's TickManager is not initialized. This should never happen unless you're using another mod that caused an error in MapComponent.FinalizeInit"); + break; + case 1: + Log.Error("Fatal error! Zombieland's TickManager is not initialized. The base implementation never returned which means another mod is causing an error in MapComponent.FinalizeInit"); + break; + case 2: + Log.Error("Fatal error! Zombieland's TickManager is not initialized because its FinalizeInit method caused an error. Maybe another mod caused this error indirectly, you should report this."); + break; + case 3: + tickManager.ZombieTicking(); + break; + } + }); } public static float PercentTicking @@ -47,6 +64,7 @@ public static float PercentTicking public class TickManager : MapComponent { + public int isInitialized = 0; int populationSpawnCounter; int nextVisibleGridUpdate; @@ -71,18 +89,18 @@ public class TickManager : MapComponent Sustainer zombiesAmbientSound; float zombiesAmbientSoundVolume; - public readonly HashSet hummingZombies = new HashSet(); + public readonly HashSet hummingZombies = new(); Sustainer electricSustainer; - public Queue colonistsConverter = new Queue(); - public Queue> rimConnectActions = new Queue>(); + public Queue colonistsConverter = new(); + public Queue> rimConnectActions = new(); - public List explosions = new List(); - public IncidentInfo incidentInfo = new IncidentInfo(); + public List explosions = new(); + public IncidentInfo incidentInfo = new(); public ZombiePathing zombiePathing; - public List floatingSpaceZombiesBack = new List(); - public List floatingSpaceZombiesFore = new List(); + public List floatingSpaceZombiesBack = new(); + public List floatingSpaceZombiesFore = new(); public TickManager(Map map) : base(map) { @@ -109,7 +127,9 @@ public TickManager(Map map) : base(map) public override void FinalizeInit() { + isInitialized = 1; base.FinalizeInit(); + isInitialized = 2; Tools.nextPlayerReachableRegionsUpdate = 0; @@ -159,6 +179,8 @@ public override void FinalizeInit() taskTicker = TickTasks(); while (taskTicker.Current as string != "end") _ = taskTicker.MoveNext(); + + isInitialized = 3; } public override void MapRemoved() @@ -185,17 +207,14 @@ public override void ExposeData() if (Scribe.mode == LoadSaveMode.PostLoadInit) { - if (allZombiesCached == null) - allZombiesCached = new HashSet(); + allZombiesCached ??= new HashSet(); allZombiesCached = allZombiesCached.Where(zombie => zombie != null && zombie.Spawned && zombie.Dead == false).ToHashSet(); - if (allZombieCorpses == null) - allZombieCorpses = new List(); + allZombieCorpses ??= new List(); allZombieCorpses = allZombieCorpses.Where(corpse => corpse.DestroyedOrNull() == false && corpse.Spawned).ToList(); runZombiesForNewIncident = true; - if (explosions == null) - explosions = new List(); + explosions ??= new List(); } } @@ -410,8 +429,7 @@ void FetchAvoidGrid() { if (Tools.ShouldAvoidZombies() == false) { - if (emptyAvoidGrid == null) - emptyAvoidGrid = new AvoidGrid(map); + emptyAvoidGrid ??= new AvoidGrid(map); avoidGrid = emptyAvoidGrid; return; } @@ -521,8 +539,7 @@ public void UpdateElectricalHumming() return; } - if (electricSustainer == null) - electricSustainer = CustomDefs.ZombieElectricHum.TrySpawnSustainer(SoundInfo.OnCamera(MaintenanceType.None)); + electricSustainer ??= CustomDefs.ZombieElectricHum.TrySpawnSustainer(SoundInfo.OnCamera(MaintenanceType.None)); if (hummingZombies.Count == 0) { @@ -590,8 +607,7 @@ IEnumerator TickTasks() yield return null; if (Constants.USE_SOUND && ZombieSettings.Values.playCreepyAmbientSound) { - if (zombiesAmbientSound == null) - zombiesAmbientSound = CustomDefs.ZombiesClosingIn.TrySpawnSustainer(SoundInfo.OnCamera(MaintenanceType.None)); + zombiesAmbientSound ??= CustomDefs.ZombiesClosingIn.TrySpawnSustainer(SoundInfo.OnCamera(MaintenanceType.None)); if (volume < zombiesAmbientSoundVolume) zombiesAmbientSoundVolume -= 0.0001f; @@ -600,21 +616,25 @@ IEnumerator TickTasks() zombiesAmbientSound.info.volumeFactor = zombiesAmbientSoundVolume; } else + { StopAmbientSound(); + yield return null; + } - yield return null; if (colonistsConverter.Count > 0 && map != null) { var pawn = colonistsConverter.Dequeue(); Tools.ConvertToZombie(pawn, map); + yield return null; } - yield return null; if (rimConnectActions.Count > 0 && map != null) { var action = rimConnectActions.Dequeue(); action(map); + yield return null; } - yield return "end"; + + yield return "end"; // must be called "end"! } } diff --git a/Source/VariableGraphic.cs b/Source/VariableGraphic.cs index 54da9758..a69a1e5b 100644 --- a/Source/VariableGraphic.cs +++ b/Source/VariableGraphic.cs @@ -30,7 +30,7 @@ public IEnumerator InitIterativ(GraphicRequest req, int n, int points) var stain = ZombieStains.GetRandom(points, req.path.Contains("Naked")); var it = data.ApplyStainsIterativ(stain.Key, Rand.Bool, Rand.Bool); while (it.MoveNext()) - yield return null; + yield return it.Current; points -= stain.Value; hash = Gen.HashCombine(hash, stain); @@ -48,7 +48,7 @@ public IEnumerator InitIterativ(GraphicRequest req, int n, int points) mats[n] = new VariableMaterial(request, data); } - public static GraphicRequest minimal = new GraphicRequest(); + public static GraphicRequest minimal = new(); public override void Init(GraphicRequest req) { if (req == minimal) @@ -67,7 +67,8 @@ public override void Init(GraphicRequest req) for (var i = 0; i < 4; i++) { var iterator = InitIterativ(req, i, ZombieStains.maxStainPoints); - while (iterator.MoveNext()) ; + while (iterator.MoveNext()) + ; } } diff --git a/Source/ZombieAvoider.cs b/Source/ZombieAvoider.cs index 59fda33f..da9632d3 100644 --- a/Source/ZombieAvoider.cs +++ b/Source/ZombieAvoider.cs @@ -77,12 +77,15 @@ public class ZombieAvoider ConcurrentQueue QueueForMap(Map map) { - if (resultQueues.TryGetValue(map, out var queue) == false) + lock (requestQueue) { - queue = new ConcurrentQueue(true); - resultQueues.Add(map, queue); + if (resultQueues.TryGetValue(map, out var queue) == false) + { + queue = new ConcurrentQueue(true); + resultQueues.Add(map, queue); + } + return queue; } - return queue; } public ZombieAvoider() diff --git a/Source/ZombieGenerator.cs b/Source/ZombieGenerator.cs index 5d5c46bd..4de9fae5 100644 --- a/Source/ZombieGenerator.cs +++ b/Source/ZombieGenerator.cs @@ -43,7 +43,7 @@ public static Color HairColor() return new Color(0.3f, 0.2f, 0.1f); } - private static readonly Dictionary eyeOffsets = new Dictionary() { + private static readonly Dictionary eyeOffsets = new() { { "Female_Average_Normal", new IntVec2(11, -5) }, { "Female_Average_Pointy", new IntVec2(11, -5) }, { "Female_Average_Wide", new IntVec2(11, -6) }, @@ -220,8 +220,44 @@ static BodyTypeDef SetRandomBody(Zombie zombie) public static class ZombieGenerator { public static int ZombiesSpawning = 0; - public static readonly List childBackstories = DefDatabase.AllDefs.Where(b => b.slot == BackstorySlot.Childhood).ToList(); - public static readonly List adultBackstories = DefDatabase.AllDefs.Where(b => b.slot == BackstorySlot.Adulthood).ToList(); + public static readonly List childBackstories; + public static readonly List adultBackstories; + public static readonly Dictionary>> AllApparel; + + static ZombieGenerator() + { + childBackstories = DefDatabase.AllDefs.Where(b => b.slot == BackstorySlot.Childhood).ToList(); + adultBackstories = DefDatabase.AllDefs.Where(b => b.slot == BackstorySlot.Adulthood).ToList(); + + var pairs = ThingStuffPair.AllWith((ThingDef td) => td.IsApparel) + .Where(pair => + { + var def = pair.thing; + if (def.IsApparel == false) + return false; + if (def.IsZombieDef()) + return false; + if (def == ThingDefOf.Apparel_ShieldBelt) + return false; + if (def == ThingDefOf.Apparel_SmokepopBelt) + return false; + if (def.thingClass?.Name.Contains("ApparelHolographic") ?? false) + return false; // SoS + var path = def.apparel.wornGraphicPath; + return path != null && path.Length > 0; + }) + .ToList(); + + var dict = new List() { BodyTypeDefOf.Fat.defName, BodyTypeDefOf.Thin.defName, BodyTypeDefOf.Male.defName, BodyTypeDefOf.Female.defName, BodyTypeDefOf.Hulk.defName } + .Select(bodyType => (bodyType, pairs: pairs.Where(pair => GraphicFileExist(pair.thing.apparel, bodyType)).ToList())) + .ToDictionary(item => item.bodyType, item => item.pairs); + + AllApparel = new Dictionary>>() + { + { false, dict }, + { true, dict.Select(pair => (key: pair.Key, pairs: pair.Value.Where(p => PawnApparelGenerator.IsHeadgear(p.thing)).ToList())).ToDictionary(pair => pair.key, pair => pair.pairs) } + }; + } private static BodyTypeDef PrepareZombieType(Zombie zombie, ZombieType overwriteType) { @@ -305,9 +341,7 @@ static IEnumerator AssignNewGraphicsIterator(Zombie zombie) color = "healer"; specialColor = Color.cyan; } - yield return null; var bodyRequest = new GraphicRequest(typeof(VariableGraphic), bodyPath, ShaderDatabase.Cutout, Vector2.one, Color.white, Color.white, null, renderPrecedence, new List(), null); - yield return null; var maxStainPoints = ZombieStains.maxStainPoints; if (zombie.isMiner) @@ -320,99 +354,53 @@ static IEnumerator AssignNewGraphicsIterator(Zombie zombie) maxStainPoints = 0; var customBodyGraphic = new VariableGraphic { bodyColor = color }; - yield return null; customBodyGraphic.Init(VariableGraphic.minimal); - yield return null; for (var i = 0; i < 4; i++) { var j = 0; var it = customBodyGraphic.InitIterativ(bodyRequest, i, maxStainPoints); while (it.MoveNext()) { - yield return null; + yield return it.Current; j++; } } zombie.Drawer.renderer.graphics.nakedGraphic = customBodyGraphic; var headRequest = new GraphicRequest(typeof(VariableGraphic), headPath, ShaderDatabase.Cutout, Vector2.one, Color.white, Color.white, null, renderPrecedence, new List(), null); - yield return null; var customHeadGraphic = new VariableGraphic { bodyColor = color }; - yield return null; customHeadGraphic.Init(VariableGraphic.minimal); - yield return null; for (var i = 0; i < 4; i++) { var j = 0; var it = customHeadGraphic.InitIterativ(headRequest, i, maxStainPoints); while (it.MoveNext()) { - yield return null; + yield return it.Current; j++; } } //zombie.Drawer.renderer.graphics.headGraphic = customHeadGraphic; zombie.Drawer.renderer.graphics.headGraphic = zombie.story.headType.GetGraphic(specialColor ?? color.HexColor(), false, true); } - - yield return null; } - static List _allApparelPairs; - static List AllApparelPairs() - { - _allApparelPairs ??= ThingStuffPair.AllWith((ThingDef td) => td.IsApparel) - .Where(pair => - { - var def = pair.thing; - if (def.IsApparel == false) - return false; - if (def.IsZombieDef()) - return false; - if (def == ThingDefOf.Apparel_ShieldBelt) - return false; - if (def == ThingDefOf.Apparel_SmokepopBelt) - return false; - if (def.thingClass?.Name.Contains("ApparelHolographic") ?? false) - return false; // SoS - var path = def.apparel.wornGraphicPath; - return path != null && path.Length > 0; - }) - .ToList(); - return _allApparelPairs; - } - - static bool CanWear(Zombie zombie, ThingDef thing) - { - if (thing == null) - return false; - - if (zombie.isMiner && PawnApparelGenerator.IsHeadgear(thing)) - return false; - - return ApparelUtility.HasPartsToWear(zombie, thing); - } - - static bool GraphicFileExist(Zombie zombie, ApparelProperties apparel) + static bool GraphicFileExist(ApparelProperties apparel, string bodyTypeDefName) { var path = apparel.wornGraphicPath; if (apparel.LastLayer != ApparelLayerDefOf.Overhead) - path += "_" + zombie.story.bodyType.defName; + path += "_" + bodyTypeDefName; return ContentFinder.Get(path + "_north", false) != null; } public static IEnumerator GenerateStartingApparelFor(Zombie zombie) { - var wearableApparel = AllApparelPairs().Where(pair => CanWear(zombie, pair.thing)).ToList(); - yield return null; - var possibleApparel = wearableApparel.Where(pair => GraphicFileExist(zombie, pair.thing.apparel)).ToList(); - yield return null; + var possibleApparel = AllApparel[zombie.isMiner][zombie.story.bodyType.defName]; if (possibleApparel.Count > 0) { for (var i = 0; i < Rand.Range(0, 4); i++) { var pair = possibleApparel.SafeRandomElement(); - yield return null; var apparel = (Apparel)ThingMaker.MakeThing(pair.thing, pair.stuff); yield return null; PawnGenerator.PostProcessGeneratedGear(apparel, zombie); @@ -422,16 +410,16 @@ public static IEnumerator GenerateStartingApparelFor(Zombie zombie) if (zombie.apparel.WornApparel.All(pa => ApparelUtility.CanWearTogether(pair.thing, pa.def, zombie.RaceProps.body))) { var colorComp = apparel.GetComp(); - if (colorComp != null) - colorComp.SetColor(Zombie.zombieColors[Rand.Range(0, Zombie.zombieColors.Length)].SaturationChanged(0.25f)); + colorComp?.SetColor(Zombie.zombieColors[Rand.Range(0, Zombie.zombieColors.Length)].SaturationChanged(0.25f)); Graphic_Multi_Init_Patch.suppressError = true; Graphic_Multi_Init_Patch.textureError = false; try { zombie.apparel.Wear(apparel, false); } - catch (Exception) + catch (Exception ex) { + Log.Warning($"Wear error: {ex.Message} for {apparel}"); } if (Graphic_Multi_Init_Patch.textureError) zombie.apparel.Remove(apparel); @@ -493,7 +481,6 @@ static void Abort(Exception ex) ZombiesSpawning++; var zombie = (Zombie)ThingMaker.MakeThing(ZombieDefOf.Zombie.race, null); - yield return null; if (RunWithFailureCheck(out var bodyType, out var ex1, () => { @@ -503,14 +490,12 @@ static void Abort(Exception ex) return bodyType; })) { Abort(ex1); yield break; } - yield return null; if (RunWithFailureCheck(out var ex2, () => { PawnComponentsUtility.CreateInitialComponents(zombie); })) { Abort(ex2); yield break; } - yield return null; if (RunWithFailureCheck(out var ex3, () => { @@ -522,7 +507,6 @@ static void Abort(Exception ex) var idx = zombie.ageTracker.CurLifeStageIndex; // trigger calculations })) { Abort(ex3); yield break; } - yield return null; if (RunWithFailureCheck(out var ex4, () => { @@ -530,21 +514,18 @@ static void Abort(Exception ex) zombie.records.records.values.Clear(); })) { Abort(ex4); yield break; } - yield return null; if (RunWithFailureCheck(out var ex5, () => { zombie.needs.mood = new Need_Mood(zombie); })) { Abort(ex5); yield break; } - yield return null; if (RunWithFailureCheck(out var name, out var ex6, () => { return PawnNameDatabaseSolid.GetListForGender((zombie.gender == Gender.Female) ? GenderPossibility.Female : GenderPossibility.Male).SafeRandomElement(); })) { Abort(ex6); yield break; } - yield return null; if (RunWithFailureCheck(out var ex7, () => { @@ -554,14 +535,12 @@ static void Abort(Exception ex) zombie.Name = new NameTriple(n1, n3, n2); })) { Abort(ex7); yield break; } - yield return null; if (RunWithFailureCheck(out var ex8, () => { zombie.story.childhood = childBackstories.SafeRandomElement(); })) { Abort(ex8); yield break; } - yield return null; if (RunWithFailureCheck(out var ex9, () => { @@ -569,7 +548,6 @@ static void Abort(Exception ex) zombie.story.adulthood = adultBackstories.SafeRandomElement(); })) { Abort(ex9); yield break; } - yield return null; if (RunWithFailureCheck(out var ex10, () => { @@ -585,14 +563,12 @@ static void Abort(Exception ex) zombie.story.hairDef = PawnStyleItemChooser.RandomHairFor(zombie); })) { Abort(ex10); yield break; } - yield return null; if (RunWithFailureCheck(out var ex11, () => { FixVanillaHairExpanded(zombie, ZombieDefOf.Zombies); })) { Abort(ex11); yield break; } - yield return null; IEnumerator it = default; var looping = false; @@ -603,7 +579,7 @@ static void Abort(Exception ex) { if (RunWithFailureCheck(out var ex12, () => looping = it.MoveNext())) { looping = false; Abort(ex12); yield break; } - yield return null; + yield return it.Current; } if (RunWithFailureCheck(out var ex13, () => @@ -613,7 +589,6 @@ static void Abort(Exception ex) zombie.pather.destination = IntVec3.Invalid; })) { Abort(ex13); yield break; } - yield return null; if (zombie.IsTanky == false && ZombieSettings.Values.disableRandomApparel == false) { @@ -623,7 +598,7 @@ static void Abort(Exception ex) { if (RunWithFailureCheck(out var ex14, () => looping = it.MoveNext())) { looping = false; Abort(ex14); yield break; } - yield return null; + yield return it.Current; } } @@ -635,7 +610,6 @@ static void Abort(Exception ex) _ = GenPlace.TryPlaceThing(zombie, cell, map, ThingPlaceMode.Direct); })) { Abort(ex15); yield break; } - yield return null; try { @@ -652,16 +626,16 @@ static void Abort(Exception ex) case TimeSpeed.Paused: break; case TimeSpeed.Normal: - yield return new WaitForSeconds(0.1f); + yield return new WaitForSeconds(0.01f); break; case TimeSpeed.Fast: - yield return new WaitForSeconds(0.25f); + yield return new WaitForSeconds(0.025f); break; case TimeSpeed.Superfast: - yield return new WaitForSeconds(0.5f); + yield return new WaitForSeconds(0.05f); break; case TimeSpeed.Ultrafast: - yield return new WaitForSeconds(1f); + yield return new WaitForSeconds(0.1f); break; } if (zombie.isElectrifier) diff --git a/Source/ZombieIncidents.cs b/Source/ZombieIncidents.cs index b1fca8ec..b6406f4e 100644 --- a/Source/ZombieIncidents.cs +++ b/Source/ZombieIncidents.cs @@ -39,7 +39,7 @@ public class IncidentParameters public class IncidentInfo : IExposable { int lastIncident; - public IncidentParameters parameters = new IncidentParameters(); + public IncidentParameters parameters = new(); public int NextIncident() { @@ -227,7 +227,7 @@ static IEnumerator SpawnEventProcess(Map map, int incidentSize, IntVec3 spot, Pr var it = ZombieGenerator.SpawnZombieIterativ(cell, map, zombieType, zombie => tickManager.allZombiesCached.Add(zombie)); while (it.MoveNext()) { - if (ZombielandMod.frameWatch.ElapsedMilliseconds >= 4) + if (ZombielandMod.frameWatch.ElapsedMilliseconds >= 10) yield return null; } incidentSize--; @@ -272,6 +272,39 @@ public static IntVec3 GetValidSpot(Map map, IntVec3 spot, Predicate cel return spot; } + /* + static readonly Dictionary elapsed = new(); + static IEnumerator DeltaTimeProxy(Map map, int incidentSize, IntVec3 spot, Predicate cellValidator, bool useAlert, bool ignoreLimit, ZombieType zombieType = ZombieType.Random) + { + elapsed.Clear(); + var it = SpawnEventProcess(map, incidentSize, spot, cellValidator, useAlert, ignoreLimit, zombieType); + while (true) + { + var sw = Stopwatch.StartNew(); + if (it.MoveNext() == false) + break; + var res = it.Current; + if (res is string key) + { + var ms = sw.ElapsedMilliseconds; + if (elapsed.TryGetValue(key, out var value) == false) + elapsed.Add(key, (ms, ms)); + else + { + value.Item1 = Math.Min(value.Item1, ms); + value.Item2 = Math.Max(value.Item2, ms); + elapsed[key] = value; + } + yield return null; + } + else + yield return res; + } + foreach (var item in elapsed) + Log.Warning($"{item.Key}: {item.Value.Item1} - {item.Value.Item2}"); + } + */ + public static bool TryExecute(Map map, int incidentSize, IntVec3 spot, bool useAlert, bool ignoreLimit = false, ZombieType zombieType = ZombieType.Random) { if (map.IsBlacklisted()) @@ -284,4 +317,4 @@ public static bool TryExecute(Map map, int incidentSize, IntVec3 spot, bool useA return true; } } -} +} \ No newline at end of file diff --git a/Source/ZombieLand.csproj b/Source/ZombieLand.csproj index 94aa22d5..3691dc0f 100644 --- a/Source/ZombieLand.csproj +++ b/Source/ZombieLand.csproj @@ -10,7 +10,7 @@ ..\1.4\Assemblies\ true false - 3.3.1.0 + 3.4.0.0 Copyright © 2017