diff --git a/Assemblies/ZombieLand.dll b/Assemblies/ZombieLand.dll index 0788ea8c..1b875f88 100755 Binary files a/Assemblies/ZombieLand.dll and b/Assemblies/ZombieLand.dll differ diff --git a/Languages/ChineseSimplified/Keyed/Text.xml b/Languages/ChineseSimplified/Keyed/Text.xml index 59962bf9..e64dc564 100644 --- a/Languages/ChineseSimplified/Keyed/Text.xml +++ b/Languages/ChineseSimplified/Keyed/Text.xml @@ -69,6 +69,12 @@ 正常 较高 群体过于庞大时丧尸会变得愤怒。 + 愤怒程度 + 非常低的水平 + 低水平 + 正常水平 + 高水平 + 非常高的水平 丧尸的体质 丧尸会恢复伤势 diff --git a/Languages/ChineseTraditional/Keyed/Text.xml b/Languages/ChineseTraditional/Keyed/Text.xml index d28ab6bf..159f11f4 100644 --- a/Languages/ChineseTraditional/Keyed/Text.xml +++ b/Languages/ChineseTraditional/Keyed/Text.xml @@ -69,6 +69,12 @@ 正常 較高 群體過於龐大時殭屍會變得憤怒。 + 憤怒程度 + 非常低的水平 + 低水平 + 正常水平 + 高水平 + 非常高的水平 殭屍的體質 殭屍會恢復傷勢 diff --git a/Languages/English/Keyed/Help.xml b/Languages/English/Keyed/Help.xml index 2637321f..aa82adad 100644 --- a/Languages/English/Keyed/Help.xml +++ b/Languages/English/Keyed/Help.xml @@ -86,6 +86,7 @@ Zombie senses reach a bit longer Zombies are very sensitive and sense you and your trails over many cells To simulate group behaviour, zombies can become enraged if a tight group of zombies gets too large. Raging zombies have red eyes and start walking towards your colonists. They will be stronger and faster than normal. Try preventing zombie hordes from becoming to large or stuck at places on the map to avoid raging zombies. + This level controls the group size that triggers raging zombies. High levels are more dangerous and make zombies trigger more easily. diff --git a/Languages/English/Keyed/Text.xml b/Languages/English/Keyed/Text.xml index 7ca15a06..a5916d21 100644 --- a/Languages/English/Keyed/Text.xml +++ b/Languages/English/Keyed/Text.xml @@ -73,6 +73,12 @@ Normal Elevated Zombies rage if a group gets too large + Rage level + Very low + Low + Normal + High + Very high Zombie health Zombies recover from injuries diff --git a/Languages/French/Keyed/Text.xml b/Languages/French/Keyed/Text.xml index 82e8b0a7..1bc1e7d4 100644 --- a/Languages/French/Keyed/Text.xml +++ b/Languages/French/Keyed/Text.xml @@ -69,6 +69,12 @@ Normal Elevé Les zombies s’enragent si un groupe devient trop grand + Niveau de rage + Très bas + Faible + Normal + Haut + Très haut Vitalités des zombies Les zombies se remettent de leurs blessures diff --git a/Languages/German/Keyed/Text.xml b/Languages/German/Keyed/Text.xml index 8d5d7dd4..d5ca801d 100644 --- a/Languages/German/Keyed/Text.xml +++ b/Languages/German/Keyed/Text.xml @@ -69,6 +69,12 @@ Normal Empfindlich Zombies in zu grossen Gruppen werden rasend + Raserei + Sehr niedrig + Niedrig + Normal + Hoch + Sehr hoch Zombiegesundheit Zombies erholen sich von Verletzungen diff --git a/Languages/Russian/Keyed/Text.xml b/Languages/Russian/Keyed/Text.xml index 5d3dfabc..f6a406a2 100644 --- a/Languages/Russian/Keyed/Text.xml +++ b/Languages/Russian/Keyed/Text.xml @@ -69,6 +69,12 @@ Нормальные Агресивные Zombies ярости, если толпа становится слишком большой + Уровень ярости + Очень низкий + Низкий уровень + Нормальный + Высокий + Очень высокий Стойкость зомби Зомби восстанавливаются после травм diff --git a/Languages/Turkish/Keyed/Text.xml b/Languages/Turkish/Keyed/Text.xml index 25ac12c5..13827e20 100644 --- a/Languages/Turkish/Keyed/Text.xml +++ b/Languages/Turkish/Keyed/Text.xml @@ -69,6 +69,12 @@ Normal Yükseltilmiş Bir grup çok büyük alırsa Zombiler öfke + Öfke seviyesi + Çok düşük + Düşük + Normal + Yüksek + Çok yüksek Zombi sağlığı Zombiler yaralanmaları kurtarmak diff --git a/Source/Dialogs.cs b/Source/Dialogs.cs index 84a6843f..74cf24d3 100644 --- a/Source/Dialogs.cs +++ b/Source/Dialogs.cs @@ -297,19 +297,17 @@ public static void Dialog_FloatSlider(this Listing_Standard list, string labelId value = Widgets.HorizontalSlider(srect, value, min, max, false, null, labelId.SafeTranslate(), valstr, -1f); } - public static void Dialog_IntSlider(this Listing_Standard list, string labelId, string format, ref int value, int min, int max) + public static void Dialog_IntSlider(this Listing_Standard list, string labelId, Func format, ref int value, int min, int max) { list.Help(labelId, 32f); list.Gap(12f); - var valstr = string.Format("{0:" + format + "}", value); - var srect = list.GetRect(24f); srect.xMin += inset; srect.xMax -= inset; - value = (int)(0.5f + Widgets.HorizontalSlider(srect, value, min, max, false, null, labelId.SafeTranslate(), valstr, -1f)); + value = (int)(0.5f + Widgets.HorizontalSlider(srect, value, min, max, false, null, labelId.SafeTranslate(), format(value), -1f)); } public static void Dialog_TimeSlider(this Listing_Standard list, string labelId, ref int value, int min, int max, bool fullDaysOnly = false) @@ -344,7 +342,7 @@ public static void DoWindowContentsInternal(ref SettingsGroup settings, Rect inR var secondColumnWidth = inRect.width - Listing.ColumnSpacing - firstColumnWidth; var outerRect = new Rect(inRect.x, inRect.y, firstColumnWidth, inRect.height); - var innerRect = new Rect(0f, 0f, firstColumnWidth - 24f, inRect.height * 4.6f); + var innerRect = new Rect(0f, 0f, firstColumnWidth - 24f, inRect.height * 4.7f); Widgets.BeginScrollView(outerRect, ref scrollPosition, innerRect, true); currentHelpItem = null; @@ -384,7 +382,11 @@ public static void DoWindowContentsInternal(ref SettingsGroup settings, Rect inR // Senses list.Dialog_Enum("ZombieInstinctTitle", ref settings.zombieInstinct); list.Dialog_Checkbox("RagingZombies", ref settings.ragingZombies); - list.Gap(20f); + var rageLevelNames = new string[] { "RageLevelVeryLow", "RageLevelLow", "RageLevelNormal", "RageLevelHigh", "RageLevelVeryHigh" }; + list.Gap(4f); + if (settings.ragingZombies) + list.Dialog_IntSlider("RageLevel", level => rageLevelNames[level - 1].Translate(), ref settings.zombieRageLevel, 1, 5); + list.Gap(16f); // Health list.Dialog_Label("ZombieHealthTitle"); @@ -462,7 +464,7 @@ public static void DoWindowContentsInternal(ref SettingsGroup settings, Rect inR // Infections list.Dialog_Label("ZombieSerum"); list.Gap(8f); - list.Dialog_IntSlider("CorpsesExtractAmount", "0", ref settings.corpsesExtractAmount, 1, 10); + list.Dialog_IntSlider("CorpsesExtractAmount", amount => "" + amount, ref settings.corpsesExtractAmount, 1, 10); list.Dialog_TimeSlider("CorpsesDaysToDessicated", ref settings.corpsesHoursToDessicated, 1, 120); list.Gap(18f); diff --git a/Source/JobDriver_Stumble.cs b/Source/JobDriver_Stumble.cs index 4df7fb35..d68a5579 100644 --- a/Source/JobDriver_Stumble.cs +++ b/Source/JobDriver_Stumble.cs @@ -63,6 +63,8 @@ void TickAction() // -------------------------------------------------- */ + ZombieStateHandler.CheckEndRage(zombie); + if (this.ShouldDie(zombie)) return; diff --git a/Source/Patches.cs b/Source/Patches.cs index e2858f84..5396d4bb 100755 --- a/Source/Patches.cs +++ b/Source/Patches.cs @@ -54,7 +54,7 @@ static Patches() static readonly Dictionary> playerHostilesWithoutZombies = new Dictionary>(); // patch for debugging: show pheromone grid as overlay - // + /* [HarmonyPatch(typeof(SelectionDrawer))] [HarmonyPatch("DrawSelectionOverlays")] static class SelectionDrawer_DrawSelectionOverlays_Patch @@ -96,6 +96,7 @@ static void Postfix() }); } } + */ // patch for debugging: show zombie avoidance grid /* @@ -208,12 +209,12 @@ static void Postfix(float leftX, float width, ref float curBaseY) } [HarmonyPatch(typeof(Verse.TickManager))] - [HarmonyPatch("TickManagerUpdate")] + [HarmonyPatch(nameof(Verse.TickManager.TickManagerUpdate))] static class Verse_TickManager_TickManagerUpdate_Patch { static readonly Stopwatch watch = new Stopwatch(); - static void SingleTick(Verse.TickManager manager, int num) + static void TickZombies(Verse.TickManager manager, int num) { _ = num; watch.Reset(); @@ -231,7 +232,7 @@ static void SingleTick(Verse.TickManager manager, int num) } } - static void Prefix() + static void TickZombieWanderer() { _ = ZombieWanderer.processor.MoveNext(); } @@ -239,17 +240,32 @@ static void Prefix() static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) { var jump = generator.DefineLabel(); - var m_SingleTick = SymbolExtensions.GetMethodInfo(() => SingleTick(null, 0)); + var m_TickZombieWanderer = SymbolExtensions.GetMethodInfo(() => TickZombieWanderer()); + var m_TickZombies = SymbolExtensions.GetMethodInfo(() => TickZombies(null, 0)); var m_DoSingleTick = AccessTools.Method(typeof(Verse.TickManager), "DoSingleTick"); var list = instructions.ToList(); + list.Insert(0, new CodeInstruction(OpCodes.Call, m_TickZombieWanderer)); var idx = list.FindIndex(code => code.operand == m_DoSingleTick); - list[idx].operand = m_SingleTick; - list.Insert(idx, new CodeInstruction(OpCodes.Ldloc_1)); + if (idx >= 0) + { + list[idx].operand = m_TickZombies; + list.Insert(idx, new CodeInstruction(OpCodes.Ldloc_1)); + } + else + Log.Error("Unexpected code in patch " + MethodBase.GetCurrentMethod().DeclaringType); + /*var found = false; + var valueFrom = 1000f; + var valueTo = 3000f; foreach (var code in list) - if (code.operand == (object)1000) - code.operand = 200; + if (code.operand is float floatValue && floatValue == valueFrom) + { + code.operand = valueTo; + found = true; + } + if (found == false) + Log.Error("Float constant " + valueFrom + " not found in patch " + MethodBase.GetCurrentMethod().DeclaringType);*/ return list.AsEnumerable(); } @@ -1168,7 +1184,7 @@ static void DebugGrid(StringBuilder builder) _ = sb.Append("Zombie " + zombie.Name.ToStringShort + " at " + currPos.x + "," + currPos.z); _ = sb.Append(", " + zombie.state.ToString().ToLower()); if (zombie.raging > 0) - _ = sb.Append(", raging "); + _ = sb.Append(", raging[" + (zombie.raging - GenTicks.TicksAbs) + "] "); _ = sb.Append(", going to " + gotoPos.x + "," + gotoPos.z); _ = sb.Append(" (wander dest " + wanderTo.x + "," + wanderTo.z + ")"); _ = builder.AppendLine(sb.ToString()); diff --git a/Source/Properties/AssemblyInfo.cs b/Source/Properties/AssemblyInfo.cs index 86e8a005..991af8ab 100755 --- a/Source/Properties/AssemblyInfo.cs +++ b/Source/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.16.3.0")] -[assembly: AssemblyFileVersion("1.16.3.0")] +[assembly: AssemblyVersion("1.16.4.0")] +[assembly: AssemblyFileVersion("1.16.4.0")] diff --git a/Source/ZombieSettings.cs b/Source/ZombieSettings.cs index b6a25531..bd8ffa11 100644 --- a/Source/ZombieSettings.cs +++ b/Source/ZombieSettings.cs @@ -66,15 +66,10 @@ class SettingsGroup : IExposable, ICloneable public int baseNumberOfZombiesinEvent = 20; internal int extraDaysBetweenEvents = 0; public float suicideBomberChance = 0.01f; - public int suicideBomberIntChance = 1;//unused public float toxicSplasherChance = 0.01f; - public int toxicSplasherIntChance = 1;//unused public float tankyOperatorChance = 0.01f; - public int tankyOperatorIntChance = 1;//unused public float minerChance = 0.01f; - public int minerIntChance = 1;//unused public float electrifierChance = 0.01f; - public int electrifierIntChance = 1;//unused public float moveSpeedIdle = 0.2f; public float moveSpeedTracking = 1.3f; public float damageFactor = 1.0f; @@ -92,11 +87,19 @@ class SettingsGroup : IExposable, ICloneable public int corpsesHoursToDessicated = 1; public bool betterZombieAvoidance = true; public bool ragingZombies = true; + public int zombieRageLevel = 3; public bool replaceTwinkie = true; public bool zombiesDropBlood = true; public bool zombiesBurnLonger = true; public float reducedTurretConsumption = 0f; + // unused + public int suicideBomberIntChance = 1; + public int toxicSplasherIntChance = 1; + public int tankyOperatorIntChance = 1; + public int minerIntChance = 1; + public int electrifierIntChance = 1; + public object Clone() { return MemberwiseClone(); diff --git a/Source/ZombieStateHandler.cs b/Source/ZombieStateHandler.cs index d12f1768..ff8747aa 100644 --- a/Source/ZombieStateHandler.cs +++ b/Source/ZombieStateHandler.cs @@ -587,6 +587,7 @@ public static void ExecuteMove(this JobDriver_Stumble driver, Zombie zombie, Phe // check for tight groups of zombies ======================================================== // + static readonly int[] rageLevels = new int[] { 40, 32, 21, 18, 12 }; public static void BeginRage(Zombie zombie, PheromoneGrid grid) { if (zombie.IsTanky) return; @@ -594,7 +595,7 @@ public static void BeginRage(Zombie zombie, PheromoneGrid grid) if (zombie.raging == 0 && ZombieSettings.Values.ragingZombies) { var count = CountSurroundingZombies(zombie.Position, grid); - if (count > Constants.SURROUNDING_ZOMBIES_TO_TRIGGER_RAGE) + if (count >= rageLevels[ZombieSettings.Values.zombieRageLevel - 1]) StartRage(zombie); return; } @@ -603,6 +604,15 @@ public static void BeginRage(Zombie zombie, PheromoneGrid grid) zombie.raging = 0; } + public static void CheckEndRage(Zombie zombie) + { + if (zombie.raging == 0) + return; + + if (GenTicks.TicksAbs > zombie.raging || ZombieSettings.Values.ragingZombies == false) + zombie.raging = 0; + } + // subroutines ============================================================================== static Thing CanIngest(Zombie zombie) @@ -849,9 +859,13 @@ static int CountSurroundingZombies(IntVec3 pos, PheromoneGrid grid) .Select(c => grid.GetZombieCount(c)).Sum(); } + static readonly float[] minRageLength = new float[] { 0.1f, 0.2f, 0.5f, 1f, 2f }; + static readonly float[] maxRageLength = new float[] { 1f, 2f, 4f, 6f, 8f }; public static void StartRage(Zombie zombie) { - zombie.raging = GenTicks.TicksAbs + (int)(GenDate.TicksPerHour * Rand.Range(1f, 8f)); + var min = minRageLength[ZombieSettings.Values.zombieRageLevel - 1]; + var max = maxRageLength[ZombieSettings.Values.zombieRageLevel - 1]; + zombie.raging = GenTicks.TicksAbs + (int)(GenDate.TicksPerHour * Rand.Range(min, max)); Tools.CastThoughtBubble(zombie, Constants.RAGING); if (Constants.USE_SOUND && Prefs.VolumeAmbient > 0f) diff --git a/Source/ZombieWanderer.cs b/Source/ZombieWanderer.cs index ced7c768..49c30818 100644 --- a/Source/ZombieWanderer.cs +++ b/Source/ZombieWanderer.cs @@ -252,25 +252,32 @@ IEnumerator Recalculate(IntVec3[] positions, bool ignoreBuildings) } } - public IEnumerator RecalculateAll(IntVec3[] positions, bool hasTankyZombies) + public IEnumerator RecalculateAll(IntVec3[] positions, IEnumerable zombies) { if (dirtyCells) { ClearCells(); dirtyCells = false; } - if (ZombieSettings.Values.ragingZombies == false && hasTankyZombies == false) yield break; - dirtyCells = true; - // Log.Warning("recalc 1"); - var it1 = Recalculate(positions, hasTankyZombies); - while (it1.MoveNext()) - yield return null; + var hasRagingZombies = zombies.Any(zombie => zombie.raging > 0); + if (hasRagingZombies) + { + dirtyCells = true; + //Log.Warning("recalc 1"); + var it1 = Recalculate(positions, false); + while (it1.MoveNext()) + yield return null; + } - // Log.Warning("recalc 2"); - var it2 = Recalculate(positions, hasTankyZombies); - while (it2.MoveNext()) - yield return null; + var hasTankyZombies = zombies.Any(zombie => zombie.IsTanky); + if (hasTankyZombies) + { + //Log.Warning("recalc 2"); + var it2 = Recalculate(positions, true); + while (it2.MoveNext()) + yield return null; + } publicIndex = privateIndex; privateIndex = 1 - privateIndex; @@ -332,8 +339,7 @@ public static IEnumerator Process() yield return null; if (colonistPositions.Any()) { - var hasTankyZombies = pawnArray.OfType().Any(zombie => zombie.IsTanky); - var it = info.RecalculateAll(colonistPositions, hasTankyZombies); + var it = info.RecalculateAll(colonistPositions, mapPawns.OfType()); while (it.MoveNext()) yield return null; }