diff --git a/1.3/Assemblies/ZombieLand.dll b/1.3/Assemblies/ZombieLand.dll index 788f6dcf..ece9c6e1 100644 Binary files a/1.3/Assemblies/ZombieLand.dll and b/1.3/Assemblies/ZombieLand.dll differ diff --git a/About/Manifest.xml b/About/Manifest.xml index e337a939..e38040f5 100644 --- a/About/Manifest.xml +++ b/About/Manifest.xml @@ -1,7 +1,7 @@  net.pardeike.rimworld.mod.zombieland - 2.6.0.0 + 2.7.0.0
  • 1.0.0
  • 1.1.0
  • diff --git a/Languages/ChineseSimplified/Keyed/Text.xml b/Languages/ChineseSimplified/Keyed/Text.xml index 93ab2259..7375099c 100644 --- a/Languages/ChineseSimplified/Keyed/Text.xml +++ b/Languages/ChineseSimplified/Keyed/Text.xml @@ -44,9 +44,20 @@ 殖民地倍乘数 安全的双杀 + 动态威胁水平 + 使用动态威胁级别 + 威胁曲线平滑度 + 威胁曲线拉伸 + 僵尸在 0% 威胁时死亡 + 威胁级别 + 威胁预测 + 未来 14 天 + 未来 4 个季度 + 丧尸事件 丧尸/每个殖民者 事件的间隔天数 + 到达群体被感染 丧尸的速度 平静时 @@ -66,9 +77,12 @@ 躺在床上治疗可阻止感染 死后成为僵尸的时间 立即地 + 当有人变成僵尸时,展示一条消息 - 僵尸血清 + 从僵尸收获 每个僵尸的僵尸提取物量 + 机会 + 僵尸死后的战利品数量 直到僵尸尸体消失的小时数 限制提取区域 到处 @@ -158,13 +172,4 @@ 自动避免僵尸,除非殖民者被起草和站立 忽略僵尸。 您需要手动控制所有操作 - 威胁级别 - 威胁预测 - 未来 14 天 - 未来 4 个季度 - 使用动态威胁级别 - 威胁曲线平滑度 - 威胁曲线拉伸 - 僵尸在 0% 威胁时死亡 - diff --git a/Languages/ChineseTraditional/Keyed/Text.xml b/Languages/ChineseTraditional/Keyed/Text.xml index 085fcdd7..f165b040 100644 --- a/Languages/ChineseTraditional/Keyed/Text.xml +++ b/Languages/ChineseTraditional/Keyed/Text.xml @@ -44,9 +44,20 @@ 殖民地倍乘數 安全的雙殺 + 動態威脅水平 + 使用動態威脅級別 + 威脅曲線平滑度 + 威脅曲線拉伸 + 殭屍在 0% 威脅時死亡 + 威脅級別 + 威脅預測 + 未來 14 天 + 未來 4 個季度 + 殭屍事件 殭屍/每個殖民者 事件的間隔天數 + 到達群體被感染 殭屍的速度 平靜時 @@ -66,9 +77,12 @@ 躺在床上治療可阻止感染 死後成為殭屍的時間 立即地 + 當有人變成殭屍時,展示一條消息 - 殭屍血清 + 從殭屍收穫 每個殭屍的殭屍提取物量 + 機會 + 殭屍死後的戰利品數量 直到殭屍屍體消失的小時數 限制提取區域 到處 @@ -158,13 +172,4 @@ 自動避免殭屍,除非殖民者被起草和站立 忽略殭屍。 您需要手動控制所有操作 - 威脅級別 - 威脅預測 - 未來 14 天 - 未來 4 個季度 - 使用動態威脅級別 - 威脅曲線平滑度 - 威脅曲線拉伸 - 殭屍在 0% 威脅時死亡 - diff --git a/Languages/English/Keyed/Help.xml b/Languages/English/Keyed/Help.xml index 00e11877..9c6056c5 100644 --- a/Languages/English/Keyed/Help.xml +++ b/Languages/English/Keyed/Help.xml @@ -17,7 +17,7 @@ Zombies do not behave like raiders (with the exception of raging zombies). They are mindless wanderers that only get excited when their senses get activated. This setting allows you to define the creature type that will get them going. - Make zombies track and attack anything that moves - even animals. Only stationary weapons like turrents and such will never catch their attention. + Make zombies track and attack anything that moves - even animals. Only stationary weapons like turrets and such will never catch their attention. This is the "normal" way zombies should work. Their lust for brains makes them hunt human pawns regardless of faction. If you think that zombies should never attack anyone else but your colonists, then this is the right mode for you. They might hit back when attacked but will never initiate any aggression. Turn this off and your enemies will ignore and avoid zombies. When turned on, raiders will see them as dangerous and will try their best to fight them. Good luck! @@ -42,6 +42,7 @@ The upper limit of zombies on a given map tile. Actual numbers can be lower depending on the colony. Sometimes, Zombieland estimates too few zombies. For example, if you want to play with very few colonists but want the number of zombies to be high nevertheless. With this multiplier, you can tell Zombieland that your colony is x times more capable of defending itself. This will increase the number of zombies for when you really want that challenge (but the upper limit is always respected). + This section lets you switch between a constant and dynamic amount of zombies. Here you to can make the number of zombies on the map dynamically. This means that you get a forecast on the right side of the screen to plan your actions ahead. Determines the smoothness of the threat curve. With this setting you can stretch the curve and make it less dynamic. @@ -52,6 +53,7 @@ If you play with "in events only" and sometimes even in the other modes, Zombieland will trigger a zombie attack. This is not like a raider event but instead, a larger number of zombies appear together and depending on the group size, might even rage and start tracking towards you. To control the size of the event, you can specify how many zombies per colonist will appear. Please note, that this does not change the overall limit of zombies on the map and if the current number of zombies is already close to the total limit or to the number that Zombieland has determined for your current colony size, less zombies will be in the event. Putting a really large number here will only remove the upper event size limit. Setting a small number will reduce the overall event size - especially with fewer fight-capable colonists. Normally, zombie events are spaced out automatically. The actual interval depends on a few internal factors. In case you think that this is too frequent, you can use this setting to add extra days between events. + This is the chance that a hostile/friendly group arriving on the map is already infected and no interaction between them and the zombies happens. @@ -79,11 +81,13 @@ The third and last phase (red). Now the infection cannot be treated anymore and only amputation can stop the process. Many players make the mistake and end the life of their infected colonist too early. Zombieland, while cruel, has a few surprises build in to make this phase useful for you. Find out how the colonist has changed/morphed into while almost becoming a zombie! To allow simple treatment in a bed to stop an infected bite, select this option. It is a good idea to turn this on when you are not yet familiar with the game mechanics. The time for a dead humanlike pawn to become a zombie. Move all to the left to turns this feature off. + Turns the message on/off that indicates that someone turned into a zombie. - Compared to amputations, Zombie Serum is the best alternative to cure infections. Collect zombie extract whenever you can from dead zombies and refine it on a drugtable into zombie serum. The serum comes in qualities of 10% to 100% and one potion of serum heals one zombie bite. The quality determines the failure risk when doing an operation on an infected colonist. - Zombie Extract is the ingredients in Zombie Serum. You collect it from dead zombies before they disappear (either automatic or by right-click on the zombie corpse). Only colonists assigned to doctoring can collect extract. And only if there are no dangerous zombies nearby (roughly 10-20 cell radius) depending on the surroundings. To make creating serum easier, increase the amount of extract per zombie. + When zombies die they may drop some of their apparel and you can get zombie extract from their corpses before they disappear. In order to not get overloaded with lots of corpses, you can select how fast zombie corpses disappear.\n\nZombie serum is the best alternative to cure infections. The serum is made out of zombie extract and comes in qualities of 10% to 100% and one potion of serum heals one zombie bite. The quality determines the failure risk when doing an operation on an infected colonist. + You collect zombie extract from dead zombies before they disappear either automatic or by right-click on the zombie corpse and make serum out of it on a drug table or a crafting spot if you play with tribals. Only colonists assigned to doctoring can collect extract. And only if there are no dangerous zombies nearby (roughly 10-20 cell radius) depending on the surroundings. To make creating serum easier, increase the amount of extract per zombie. + Make zombies drop their apparel by chance. Zombies can wear any apparel that exists in your game with all its mods. This defines the chance and amount of dropped apparel (100% = 1 item) Zombie corpses will usually dessicate quickly so you don't end up with too many of them. To make collecting extract easier, you can increase the time before they disappear. Extraction from zombies can be dangerous. Use this feature to restrict the activity to one of your areas. If you rename the area, you need to update it here. Manually ordered extraction will always work. diff --git a/Languages/English/Keyed/Text.xml b/Languages/English/Keyed/Text.xml index 4b8ef094..d34bfb53 100644 --- a/Languages/English/Keyed/Text.xml +++ b/Languages/English/Keyed/Text.xml @@ -44,9 +44,20 @@ Colony multiplier Double tapping + Dynamic threat level + Use dynamic threat level + Threat curve smoothness + Threat curve stretch + Zombies die during 0% threat + threat level + Threat Forecast + Next 14 days + Next 4 quadrums + Zombie event Zombies per colonist Extra days between events + Arriving groups are infected Zombie speed If they are calm @@ -66,9 +77,12 @@ Treatment in a bed stops infections Hours to become a zombie after death Immediately + Show a message when someone turns into a zombie - Zombie Serum + Loot from zombies Amount of zombie extract per zombie + Chance + Amount of loot that zombies drop Hours until zombie corpses disappear Restrict extracting to area Everywhere @@ -158,13 +172,4 @@ Automatically avoid zombies, except when standing drafted Ignore zombies. You need to manually control all actions - threat level - Threat Forecast - Next 14 days - Next 4 quadrums - Use dynamic threat level - Threat curve smoothness - Threat curve stretch - Zombies die during 0% threat - diff --git a/Languages/French/Keyed/Text.xml b/Languages/French/Keyed/Text.xml index a11b4791..2e68bb05 100644 --- a/Languages/French/Keyed/Text.xml +++ b/Languages/French/Keyed/Text.xml @@ -44,9 +44,20 @@ Multiplicateur de colonies Mort à double sécurité + Niveau de menace dynamique + Utiliser le niveau de menace dynamique + Lissage de la courbe de menace + Étirement de la courbe de menace + Zombies meurent au cours de 0% menace + niveau de menace + Prévision des menaces + 14 prochains jours + 4 prochains trimestres + Apparition de Zombie Zombies par colon Jours supplémentaires entre les événements + Les groupes d'arrivée sont infectés Vitesse des Zombies Si ils sont calmes @@ -66,9 +77,12 @@ Le traitement au lit arête l’infection Des heures pour devenir un zombie après la mort Aussitôt + Montrer un message quand quelqu'un se transforme en zombie - Sérum Zombie + Butin de zombies Quantité d'extrait de zombie par zombie + Chance + Quantité de butin après que les zombies meurent Des heures avant que les cadavres de zombies disparaissent Limiter l'extraction à la zone Partout @@ -158,13 +172,4 @@ Évite automatiquement les zombies, sauf lorsque la personne est mobilisée et debout Ignorer les zombies. Vous devez contrôler manuellement toutes les actions - niveau de menace - Prévision des menaces - 14 prochains jours - 4 prochains trimestres - Niveau de menace dynamique - Lissage de la courbe de menace - Étirement de la courbe de menace - Zombies meurent au cours de 0% menace - diff --git a/Languages/German/Keyed/Text.xml b/Languages/German/Keyed/Text.xml index 5acc8713..0137db54 100644 --- a/Languages/German/Keyed/Text.xml +++ b/Languages/German/Keyed/Text.xml @@ -44,9 +44,20 @@ Koloniemultiplikator Sicheres 2x killen + Dynamisches Bedrohungsniveau + Verwende dynamisches Bedrohungsniveau + Glattheit der Kurve + Kurve ausweiten + Zombies sterben während 0% Bedrohung + Bedrohungsniveau + Bedrohungsvorhersage + Nächsten 14 Tage + Nächsten 4 Quartale + Zombieangriff Zombies pro Kolonist Extra Tage zwischen Angriffen + Ankommende Gruppen sind infiziert Zombiegeschwindigkeit Wenn wandernt @@ -66,9 +77,12 @@ Behandlung in einem Bett stoppt Infektionen Zeit nach dem Tod bis man zum Zombie wird Unmittelbar + Nachricht, wenn jemand ein Zombie wird - Zombieserum + Looten von Zombies Menge an Zombieextrakt pro Zombie + Chance + Menge an Loot wenn Zombies sterben Zeit bis Zombieleichen verschwinden Begrenze das Extrahieren auf Bereich Überall @@ -158,13 +172,4 @@ Automatisches Vermeiden von Zombies, außer wenn im Kampfmodus (stehend) Ignoriere Zombies. Alle Aktionen müssen manuell kontrolliert werden - Bedrohungsniveau - Bedrohungsvorhersage - Nächsten 14 Tage - Nächsten 4 Quartale - Verwende dynamisches Bedrohungsniveau - Glattheit der Kurve - Kurve ausweiten - Zombies sterben während 0% Bedrohung - diff --git a/Languages/Russian/Keyed/Text.xml b/Languages/Russian/Keyed/Text.xml index 67d3443a..a069d108 100644 --- a/Languages/Russian/Keyed/Text.xml +++ b/Languages/Russian/Keyed/Text.xml @@ -44,9 +44,20 @@ Множитель колоний Безопасное двойное убийство + Уровень динамической угрозы + Используйте динамический уровень угрозы + Плавность кривой угрозы + Растяжение кривой угрозы + Зомби умирают при 0% угрозе + уровень угрозы + Прогноз угрозы + Следующие 14 дней + Следующие 4 квартала + Зомби-происшествие Кол-во зомби на одного колониста Дополнительные дни между событиями + Прибывающие группы заражены Скорость зомби В спокойном состоянии @@ -66,9 +77,12 @@ Лечение в постели останавливает инфекцию Время стать зомби после смерти Немедленно + Показать сообщение, когда кто-то превращается в зомби - Зомби Сыворотка + Выигрыш от зомби Количество зомби экстракта на зомби + Шанс + Количество вещей упало после смерти зомби Часы, пока трупы зомби не исчезнут Ограничить извлечение в области Везде @@ -158,13 +172,4 @@ Автоматически избегайте зомби, кроме случаев, когда колонист мобилизован и стоит Игнорировать зомби. Вам нужно вручную контролировать все действия - уровень угрозы - Прогноз угрозы - Следующие 14 дней - Следующие 4 квартала - Уровень динамической угрозы - Плавность кривой угрозы - Растяжение кривой угрозы - Зомби умирают при 0% угрозе - diff --git a/Languages/Turkish/Keyed/Text.xml b/Languages/Turkish/Keyed/Text.xml index b85f26ad..90e6604a 100644 --- a/Languages/Turkish/Keyed/Text.xml +++ b/Languages/Turkish/Keyed/Text.xml @@ -44,9 +44,20 @@ Koloni çarpanı Güvenli çift öldürme + Dinamik tehdit düzeyini + Dinamik tehdit düzeyini kullan + Tehdit eğrisi düzgünlüğü + Tehdit eğrisi esnemesi + Zombiler %0 tehdit sırasında ölür + tehdit düzeyi + Tehdit Tahmini + Önümüzdeki 14 gün + Sonraki 4 çeyrek + Zombi olayı Kolonist başına zombiler Etkinlikler arasında ekstra günler + Gelen gruplar enfekte olmuş Zombi hızı Onlar sakin iseniz @@ -66,9 +77,12 @@ Yatakta tedavi enfeksiyonları durdurur Ölümden sonra zombi olmak için saatler Hemen + Birisi bir zombiye döndüğünde bir mesaj göster - Zombi Serumu + Zombilerden Hasat Zombi başına zombi ekstresi miktarı + Şans + Amount of loot after zombies die Zombi cesetleri yok olana kadar saatler Ayıklamayı belirli bir alanla sınırla Her yerde @@ -158,13 +172,4 @@ Koloninin hazırlandığı ve ayakta durduğu durumlar dışında zombileri otomatik olarak önlemek Zombileri yoksay. Tüm eylemleri manuel olarak kontrol etmeniz gerekir - tehdit düzeyi - Tehdit Tahmini - Önümüzdeki 14 gün - Sonraki 4 çeyrek - Dinamik tehdit düzeyini kullan - Tehdit eğrisi düzgünlüğü - Tehdit eğrisi esnemesi - Zombiler %0 tehdit sırasında ölür - diff --git a/Source/Dialogs.cs b/Source/Dialogs.cs index a7269b2d..e89bc324 100644 --- a/Source/Dialogs.cs +++ b/Source/Dialogs.cs @@ -361,13 +361,14 @@ public static void Dialog_Integer(this Listing_Standard list, string labelId, st GUI.color = color; } - public static void Dialog_FloatSlider(this Listing_Standard list, string labelId, string format, bool logarithmic, ref float value, float min, float max, Func formatFunc = null) + public static void Dialog_FloatSlider(this Listing_Standard list, string labelId, Func labelFormatFunc, bool logarithmic, ref float value, float min, float max, Func formatFunc = null) { list.Help(labelId, 32f); list.Gap(12f); - var valstr = string.Format("{0:" + format + "}", formatFunc != null ? formatFunc(value) : value); + var format = labelFormatFunc(value); + var valstr = string.Format(format, formatFunc != null ? formatFunc(value) : value); var srect = list.GetRect(24f); srect.xMin += inset; @@ -450,6 +451,12 @@ public static void ChooseExtractArea(Listing_Standard list, SettingsGroup settin list.Dialog_List("ExtractZombieArea", settings.extractZombieArea, area => settings.extractZombieArea = area ?? "", areas, "Everywhere".Translate()); } + public static string ExtractAmount(float f) + { + if (f == 0) return "Off".TranslateSimple(); + return "{0:0%} " + "CorpsesExtractChance".Translate(f); + } + public static Vector2 scrollPosition = Vector2.zero; public static void DoWindowContentsInternal(ref SettingsGroup settings, Rect inRect) { @@ -462,7 +469,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, 3000); + var innerRect = new Rect(0f, 0f, firstColumnWidth - 24f, 3200); Widgets.BeginScrollView(outerRect, ref scrollPosition, innerRect, true); currentHelpItem = null; @@ -527,14 +534,14 @@ public static void DoWindowContentsInternal(ref SettingsGroup settings, Rect inR list.Gap(8f); var allMax = Mathf.Max(0.04f, settings.suicideBomberChance, settings.toxicSplasherChance, settings.tankyOperatorChance, settings.minerChance, settings.electrifierChance, settings.albinoChance, settings.darkSlimerChance, settings.healerChance); var max = Mathf.Min(1f, 2f * allMax); - list.Dialog_FloatSlider("SuicideBomberChance", "0.00%", false, ref settings.suicideBomberChance, 0f, Mathf.Min(max, 1f - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance)); - list.Dialog_FloatSlider("ToxicSplasherChance", "0.00%", false, ref settings.toxicSplasherChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance)); - list.Dialog_FloatSlider("TankyOperatorChance", "0.00%", false, ref settings.tankyOperatorChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance)); - list.Dialog_FloatSlider("MinerChance", "0.00%", false, ref settings.minerChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance)); - list.Dialog_FloatSlider("ElectrifierChance", "0.00%", false, ref settings.electrifierChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance)); - list.Dialog_FloatSlider("AlbinoChance", "0.00%", false, ref settings.albinoChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.darkSlimerChance - settings.healerChance)); - list.Dialog_FloatSlider("DarkSlimerChance", "0.00%", false, ref settings.darkSlimerChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.healerChance)); - list.Dialog_FloatSlider("HealerChance", "0.00%", false, ref settings.healerChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance)); + list.Dialog_FloatSlider("SuicideBomberChance", _ => "{0:0.00%}", false, ref settings.suicideBomberChance, 0f, Mathf.Min(max, 1f - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance)); + list.Dialog_FloatSlider("ToxicSplasherChance", _ => "{0:0.00%}", false, ref settings.toxicSplasherChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance)); + list.Dialog_FloatSlider("TankyOperatorChance", _ => "{0:0.00%}", false, ref settings.tankyOperatorChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance)); + list.Dialog_FloatSlider("MinerChance", _ => "{0:0.00%}", false, ref settings.minerChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance)); + list.Dialog_FloatSlider("ElectrifierChance", _ => "{0:0.00%}", false, ref settings.electrifierChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance)); + list.Dialog_FloatSlider("AlbinoChance", _ => "{0:0.00%}", false, ref settings.albinoChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.darkSlimerChance - settings.healerChance)); + list.Dialog_FloatSlider("DarkSlimerChance", _ => "{0:0.00%}", false, ref settings.darkSlimerChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.healerChance)); + list.Dialog_FloatSlider("HealerChance", _ => "{0:0.00%}", false, ref settings.healerChance, 0f, Mathf.Min(max, 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance)); var normalChance = 1f - settings.suicideBomberChance - settings.toxicSplasherChance - settings.tankyOperatorChance - settings.minerChance - settings.electrifierChance - settings.albinoChance - settings.darkSlimerChance - settings.healerChance; list.Gap(-6f); list.Dialog_Text(GameFont.Tiny, "NormalZombieChance", string.Format("{0:0.00%}", normalChance)); @@ -549,14 +556,18 @@ public static void DoWindowContentsInternal(ref SettingsGroup settings, Rect inR list.Dialog_Label("ZombiesOnTheMap", headerColor); list.Dialog_Integer("MaximumNumberOfZombies", "Zombies", 0, 5000, ref settings.maximumNumberOfZombies); list.Gap(8f); - list.Dialog_FloatSlider("ColonyMultiplier", "0.0x", false, ref settings.colonyMultiplier, 1f, 10f); + list.Dialog_FloatSlider("ColonyMultiplier", _ => "{0:0.0}x", false, ref settings.colonyMultiplier, 1f, 10f); + list.Gap(28f); + + list.Dialog_Label("DynamicThreatLevelTitle", headerColor); + list.Gap(8f); list.Dialog_Checkbox("UseDynamicThreatLevel", ref settings.useDynamicThreatLevel); if (settings.useDynamicThreatLevel) { list.Gap(8f); - list.Dialog_FloatSlider("DynamicThreatSmoothness", "0%", false, ref settings.dynamicThreatSmoothness, 1f, 5f, f => (f - 1f) / 4f); + list.Dialog_FloatSlider("DynamicThreatSmoothness", _ => "{0:0%}", false, ref settings.dynamicThreatSmoothness, 1f, 5f, f => (f - 1f) / 4f); list.Gap(-4f); - list.Dialog_FloatSlider("DynamicThreatStretch", "0%", false, ref settings.dynamicThreatStretch, 10f, 30f, f => (f - 10f) / 20f); + list.Dialog_FloatSlider("DynamicThreatStretch", _ => "{0:0%}", false, ref settings.dynamicThreatStretch, 10f, 30f, f => (f - 10f) / 20f); list.Gap(-6f); list.Dialog_Checkbox("ZombiesDieOnZeroThreat", ref settings.zombiesDieOnZeroThreat); } @@ -566,19 +577,21 @@ public static void DoWindowContentsInternal(ref SettingsGroup settings, Rect inR list.Dialog_Label("ZombieEventTitle", headerColor); list.Dialog_Integer("ZombiesPerColonistInEvent", null, 0, 200, ref settings.baseNumberOfZombiesinEvent); list.Dialog_Integer("ExtraDaysBetweenEvents", null, 0, 10000, ref settings.extraDaysBetweenEvents); - list.Gap(34f); + list.Gap(12f); + list.Dialog_FloatSlider("InfectedRaidsChance", f => f == 0 ? "Off".TranslateSimple() : "{0:0.0%}", true, ref settings.infectedRaidsChance, 0f, 1f); + list.Gap(28f); // Speed list.Dialog_Label("ZombieSpeedTitle", headerColor); list.Gap(8f); - list.Dialog_FloatSlider("MoveSpeedIdle", "0.00x", false, ref settings.moveSpeedIdle, 0.01f, 2f); - list.Dialog_FloatSlider("MoveSpeedTracking", "0.00x", false, ref settings.moveSpeedTracking, 0.05f, 3f); + list.Dialog_FloatSlider("MoveSpeedIdle", _ => "{0:0.00}x", false, ref settings.moveSpeedIdle, 0.01f, 2f); + list.Dialog_FloatSlider("MoveSpeedTracking", _ => "{0:0.00}x", false, ref settings.moveSpeedTracking, 0.05f, 3f); list.Gap(24f); // Damage list.Dialog_Label("ZombieDamageTitle", headerColor); list.Gap(8f); - list.Dialog_FloatSlider("ZombieDamageFactor", "0.0x", false, ref settings.damageFactor, 0.1f, 4f); + list.Dialog_FloatSlider("ZombieDamageFactor", _ => "{0:0.0}x", false, ref settings.damageFactor, 0.1f, 4f); list.Gap(-4f); list.Dialog_Checkbox("ZombiesCauseManhunting", ref settings.zombiesCauseManhuntingResponse); list.Gap(36f); @@ -586,13 +599,13 @@ public static void DoWindowContentsInternal(ref SettingsGroup settings, Rect inR // Tweaks list.Dialog_Label("ZombieGameTweaks", headerColor); list.Gap(8f); - list.Dialog_FloatSlider("ReduceTurretConsumption", "0%", false, ref settings.reducedTurretConsumption, 0f, 1f); + list.Dialog_FloatSlider("ReduceTurretConsumption", _ => "{0:0%}", false, ref settings.reducedTurretConsumption, 0f, 1f); list.Gap(28f); // Infections list.Dialog_Label("ZombieInfection", headerColor); list.Gap(8f); - list.Dialog_FloatSlider("ZombieBiteInfectionChance", "0%", false, ref settings.zombieBiteInfectionChance, 0f, 1f); + list.Dialog_FloatSlider("ZombieBiteInfectionChance", _ => "{0:0%}", false, ref settings.zombieBiteInfectionChance, 0f, 1f); list.Dialog_TimeSlider("ZombieBiteInfectionUnknown", ref settings.hoursInfectionIsUnknown, 0, 48); list.Dialog_TimeSlider("ZombieBiteInfectionTreatable", ref settings.hoursInfectionIsTreatable, 0, 6 * 24); list.Dialog_TimeSlider("ZombieBiteInfectionPersists", ref settings.hoursInfectionPersists, 0, 30 * 24, null, true); @@ -601,12 +614,22 @@ public static void DoWindowContentsInternal(ref SettingsGroup settings, Rect inR list.Gap(22f); static string hoursTranslator(int n) => n == -1 ? "Off".Translate() : (n == 0 ? "Immediately".Translate() : null); list.Dialog_TimeSlider("HoursAfterDeathToBecomeZombie", ref settings.hoursAfterDeathToBecomeZombie, -1, 6 * 24, hoursTranslator, false); + if (settings.hoursAfterDeathToBecomeZombie > -1) + { + list.Gap(-4f); + list.Dialog_Checkbox("DeadBecomesZombieMessage", ref settings.deadBecomesZombieMessage); + } list.Gap(30f); - // Serum - list.Dialog_Label("ZombieSerum", headerColor); + // Zombie loot + list.Dialog_Label("ZombieHarvestingTitle", headerColor); list.Gap(8f); - list.Dialog_IntSlider("CorpsesExtractAmount", amount => (amount == 0 ? "Off".TranslateSimple() : "" + amount), ref settings.corpsesExtractAmount, 0, 10); + var f1 = Mathf.Round(settings.corpsesExtractAmount * 100f) / 100f; + list.Dialog_FloatSlider("CorpsesExtractAmount", f => ExtractAmount(f), false, ref f1, 0, 4); + settings.corpsesExtractAmount = Mathf.Round(f1 * 100f) / 100f; + var f2 = Mathf.Round(settings.lootExtractAmount * 100f) / 100f; + list.Dialog_FloatSlider("LootExtractAmount", f => ExtractAmount(f), false, ref f2, 0, 4); + settings.lootExtractAmount = Mathf.Round(f2 * 100f) / 100f; list.Dialog_TimeSlider("CorpsesDaysToDessicated", ref settings.corpsesHoursToDessicated, 1, 120); ChooseExtractArea(list, settings); list.Gap(28f); diff --git a/Source/HediffComp_Zombie_Infecter.cs b/Source/HediffComp_Zombie_Infecter.cs index ee287ab7..4da2755e 100644 --- a/Source/HediffComp_Zombie_Infecter.cs +++ b/Source/HediffComp_Zombie_Infecter.cs @@ -48,6 +48,16 @@ public void MakeHarmfull() infectionEndTime = infectionStartTime + 2 * hour; } + public void ForceFinalStage() + { + var h = GenDate.TicksPerHour; + var start = ZombieSettings.Values.hoursInfectionIsTreatable * h; + infectionKnownDelay = GenTicks.TicksAbs - start; + infectionStartTime = GenTicks.TicksAbs + 0; + var end = ZombieSettings.Values.hoursInfectionPersists * h / 4; + infectionEndTime = infectionStartTime + end; + } + public override void CompPostPostAdd(DamageInfo? dinfo) { var pawn = Pawn; diff --git a/Source/JobDriver_ExtractZombieSerum.cs b/Source/JobDriver_ExtractZombieSerum.cs index 8869ed63..bf1e2293 100644 --- a/Source/JobDriver_ExtractZombieSerum.cs +++ b/Source/JobDriver_ExtractZombieSerum.cs @@ -59,7 +59,7 @@ public override IEnumerable MakeNewToils() if (extractProcess >= extractWork) { var extractResult = ThingMaker.MakeThing(extractDef, null); - extractResult.stackCount = ZombieSettings.Values.corpsesExtractAmount; + extractResult.stackCount = Tools.ExtractPerZombie(); _ = GenPlace.TryPlaceThing(extractResult, pawn.Position, pawn.Map, ThingPlaceMode.Near, null, null); if (zombieCorpse != null) diff --git a/Source/Patches.cs b/Source/Patches.cs index 9c3be124..05cc30b2 100644 --- a/Source/Patches.cs +++ b/Source/Patches.cs @@ -397,7 +397,11 @@ static void Postfix(Thing a, Thing b, ref bool __result) { if (!(a is Pawn pawn) || pawn.IsColonist || (pawn is Zombie) || !(b is Zombie zombie)) return; - __result = Tools.IsHostileToZombies(pawn); + + if (Tools.HasInfectionState(pawn, InfectionState.BittenInfectable, InfectionState.Infected)) + __result = false; + else + __result = Tools.IsHostileToZombies(pawn); } } [HarmonyPatch(typeof(GenHostility))] @@ -2128,6 +2132,19 @@ static bool Prefix(ref bool __result, IncidentParms parms) } } + // patch to let incidents spawn infected + [HarmonyPatch(typeof(PawnGroupKindWorker))] + [HarmonyPatch(nameof(PawnGroupKindWorker.GeneratePawns))] + [HarmonyPatch(new[] { typeof(PawnGroupMakerParms), typeof(PawnGroupMaker), typeof(bool) })] + static class IncidentWorker_Patches + { + static void Postfix(List __result) + { + if (__result == null || Rand.Chance(ZombieSettings.Values.infectedRaidsChance) == false) return; + __result.DoIf(pawn => pawn.RaceProps.Humanlike, Tools.AddZombieInfection); + } + } + // patch to allow spawning zombies with debug tools // [HarmonyPatch(typeof(PawnGenerator))] @@ -2600,7 +2617,7 @@ static void RenderExtras(PawnRenderer renderer, Vector3 drawLoc) } } - if (zombie.isHealer && zombie.healInfo.Count > 0) + if (zombie.isHealer && zombie.state != ZombieState.Emerging && zombie.healInfo.Count > 0) { var i = 0; var isNotPaused = Find.TickManager.Paused == false; @@ -3804,6 +3821,7 @@ static void Prefix(Pawn __instance) { if (zombie.jobs != null && zombie.CurJob != null) zombie.jobs.EndCurrentJob(JobCondition.InterruptForced, false); + Tools.DropLoot(zombie); return; } diff --git a/Source/Tools.cs b/Source/Tools.cs index 1c0d0d7d..13e30dd7 100644 --- a/Source/Tools.cs +++ b/Source/Tools.cs @@ -268,6 +268,9 @@ public static bool ShouldAvoidZombies(Pawn pawn = null) if (pawn.IsColonist == false) { + if (HasInfectionState(pawn, InfectionState.BittenInfectable, InfectionState.Infected)) + return false; + if (pawn.HostileTo(Faction.OfPlayer) == false) { var map = pawn.Map; @@ -538,9 +541,12 @@ public static void ConvertToZombie(ThingWithComps thing, Map map, bool force = f _ = tickManager.allZombiesCached.Add(zombie); - var label = wasPlayer ? "ColonistBecameAZombieLabel".Translate() : "OtherBecameAZombieLabel".Translate(); - var text = "BecameAZombieDesc".SafeTranslate(new object[] { pawnName.ToStringShort }); - Find.LetterStack.ReceiveLetter(label, text, wasPlayer ? CustomDefs.ColonistTurnedZombie : CustomDefs.OtherTurnedZombie, zombie); + if (ZombieSettings.Values.deadBecomesZombieMessage || wasPlayer) + { + var label = wasPlayer ? "ColonistBecameAZombieLabel".Translate() : "OtherBecameAZombieLabel".Translate(); + var text = "BecameAZombieDesc".SafeTranslate(new object[] { pawnName.ToStringShort }); + Find.LetterStack.ReceiveLetter(label, text, wasPlayer ? CustomDefs.ColonistTurnedZombie : CustomDefs.OtherTurnedZombie, zombie); + } }); while (it.MoveNext()) ; } @@ -623,6 +629,21 @@ public static bool IsZombieHediff(this HediffDef hediff) return false; } + public static void AddZombieInfection(Pawn pawn) + { + if (pawn is Zombie || HasInfectionState(pawn, InfectionState.Infected, InfectionState.All)) + return; + + var torso = pawn.health.hediffSet.GetNotMissingParts(BodyPartHeight.Undefined, BodyPartDepth.Undefined, null, null).FirstOrDefault((BodyPartRecord x) => x.def == BodyPartDefOf.Torso); + var bite = (Hediff_Injury_ZombieBite)HediffMaker.MakeHediff(HediffDef.Named("ZombieBite"), pawn, torso); + + bite.mayBecomeZombieWhenDead = true; + var damageInfo = new DamageInfo(ZombieBiteDamageDef, 0); + pawn.health.AddHediff(bite, torso, damageInfo); + bite.Tended(1, 1); + bite.TendDuration.ZombieInfector.ForceFinalStage(); + } + public static bool HasInfectionState(Pawn pawn, InfectionState state) { if (pawn.RaceProps.Humanlike == false) return false; @@ -1226,6 +1247,31 @@ public static IEnumerable DownedReplacer(IEnumerable apparel.MarketValue); + _ = zombie.apparel.TryDrop(apparel); + } + } } /*public class Random diff --git a/Source/Zombie.cs b/Source/Zombie.cs index 9ef14dca..bfcd74ee 100644 --- a/Source/Zombie.cs +++ b/Source/Zombie.cs @@ -459,7 +459,7 @@ public void CustomTick(float threatLevel) if (threatLevel <= 0.002f && ZombieSettings.Values.zombiesDieOnZeroThreat && Rand.Chance(0.002f)) _ = TakeDamage(damageInfo); - if (isHealer && EveryNTick(NthTick.Every15)) + if (isHealer && state != ZombieState.Emerging && EveryNTick(NthTick.Every15)) { var radius = 4 + ZombieLand.Tools.Difficulty() * 2; GenRadial.RadialDistinctThingsAround(Position, map, radius, false) diff --git a/Source/ZombieLand.csproj b/Source/ZombieLand.csproj index 942b694a..33ed4bf6 100644 --- a/Source/ZombieLand.csproj +++ b/Source/ZombieLand.csproj @@ -10,7 +10,7 @@ ..\1.3\Assemblies\ true false - 2.6.0.0 + 2.7.0.0 Copyright © 2017 @@ -30,9 +30,9 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -90,6 +90,8 @@ https://github.com/pardeike/Zombieland https://github.com/pardeike/Zombieland ZombieLand + 2.7.0.0 + 2.7.0.0 diff --git a/Source/ZombieSettings.cs b/Source/ZombieSettings.cs index 982e890d..e70489ee 100644 --- a/Source/ZombieSettings.cs +++ b/Source/ZombieSettings.cs @@ -66,6 +66,7 @@ class SettingsGroup : IExposable, ICloneable public bool zombiesDieOnZeroThreat = true; public float dynamicThreatSmoothness = 2.5f; public float dynamicThreatStretch = 20f; + public float infectedRaidsChance = 0.1f; public float colonyMultiplier = 1f; public int baseNumberOfZombiesinEvent = 20; internal int extraDaysBetweenEvents = 0; @@ -77,8 +78,8 @@ class SettingsGroup : IExposable, ICloneable public float albinoChance = 0.01f; public float darkSlimerChance = 0.01f; public float healerChance = 0.01f; - public float moveSpeedIdle = 0.2f; - public float moveSpeedTracking = 0.7f; + public float moveSpeedIdle = 0.15f; + public float moveSpeedTracking = 0.6f; public bool moveSpeedUpgraded = false; public float damageFactor = 1.0f; public ZombieInstinct zombieInstinct = ZombieInstinct.Normal; @@ -92,7 +93,9 @@ class SettingsGroup : IExposable, ICloneable public int hoursInfectionPersists = 6 * 24; public bool anyTreatmentStopsInfection; public int hoursAfterDeathToBecomeZombie = 8; - public int corpsesExtractAmount = 1; + public bool deadBecomesZombieMessage = true; + public float corpsesExtractAmount = 1f; + public float lootExtractAmount = 0.1f; public string extractZombieArea = ""; public int corpsesHoursToDessicated = 1; public bool betterZombieAvoidance = true;