Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of TML Infection v1.2.0
Mods/TMLInfection.dll
Decompiled 3 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.Json; using Il2CppFishNet.Object; using Il2CppInterop.Runtime.InteropTypes; using Il2CppScheduleOne.Core.Weather; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Effects; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.Messaging; using Il2CppScheduleOne.NPCs; using Il2CppScheduleOne.Networking; using Il2CppScheduleOne.PlayerScripts; using Il2CppScheduleOne.Product; using Il2CppScheduleOne.UI.MainMenu; using Il2CppScheduleOne.Weather; using Il2CppSystem.Collections.Generic; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using S1API.Console; using S1API.Entities; using S1API.GameTime; using S1API.Lifecycle; using S1API.Money; using S1API.UI; using TMLInfection; using UnityEngine; using UnityEngine.AI; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Core), "TML Infection", "1.2.0", "ThatMaddLadd", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("TMLInfection")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0+d2c92b3760785cb969d7407d481067dd605cc07e")] [assembly: AssemblyProduct("TMLInfection")] [assembly: AssemblyTitle("TMLInfection")] [assembly: AssemblyVersion("1.2.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TMLInfection { internal static class BloodSky { private static readonly Color MoonGlow = new Color(1f, 0.22f, 0.16f); private static readonly int SkyUpper = Shader.PropertyToID("_SkyUpperColor"); private static readonly int SkyMiddle = Shader.PropertyToID("_SkyMiddleColor"); private static readonly int SkyLower = Shader.PropertyToID("_SkyLowerColor"); private static readonly int MoonColor = Shader.PropertyToID("_MoonColor"); private static readonly int MoonSize = Shader.PropertyToID("_MoonSize"); private static readonly int FogColor = Shader.PropertyToID("_FogColor"); private static readonly int FogDensity = Shader.PropertyToID("_FogDensity"); private static float _blend; private static float _moonBlend; private static bool _tinted; public static void LateUpdate() { bool flag = Settings.BloodSky && Horde.Active; float num = ((!flag) ? 0f : (Horde.BloodMoon ? Settings.BloodMoonSkyStrength : Settings.BloodSkyStrength)); float num2 = ((Settings.BloodSkyFadeSeconds > 0f) ? (Time.deltaTime / Settings.BloodSkyFadeSeconds) : 1f); _blend = Mathf.MoveTowards(_blend, num, num2); _moonBlend = Mathf.MoveTowards(_moonBlend, (flag && Horde.BloodMoon) ? 1f : 0f, num2); if ((!(_blend <= 0f) || _tinted) && NetworkSingleton<EnvironmentManager>.InstanceExists) { EnvironmentManager instance = NetworkSingleton<EnvironmentManager>.Instance; SkyState currentSkyState = instance._currentSkyState; if (currentSkyState != null) { Paint(currentSkyState, instance._dayNightController); _tinted = _blend > 0f; } } } public static void Reset() { _blend = 0f; _moonBlend = 0f; _tinted = false; } private static void Paint(SkyState sky, DayNightController? dayNight) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) Color val = Toward(sky.SkyUpperColor, 0.3f, 1f); Color val2 = Toward(sky.SkyMiddleColor, 0.65f, 1f); Color val3 = Toward(sky.SkyLowerColor, 1f + 0.4f * _moonBlend, 1f); Color val4 = Color.Lerp(sky.MoonColor, Keep(MoonGlow, sky.MoonColor.a), _blend); float num = sky.MoonSize * Mathf.Lerp(1f, Settings.BloodMoonSize, _moonBlend); Color val5 = Toward(sky.FogColor, 0.35f, 1f); float num2 = sky.FogDensity * Mathf.Lerp(1f, Settings.BloodMoonFog, _moonBlend); Shader.SetGlobalColor(SkyUpper, val); Shader.SetGlobalColor(SkyMiddle, val2); Shader.SetGlobalColor(SkyLower, val3); Shader.SetGlobalColor(MoonColor, val4); Shader.SetGlobalFloat(MoonSize, num); Shader.SetGlobalColor(FogColor, val5); Shader.SetGlobalFloat(FogDensity, num2); object obj; if (dayNight == null) { obj = null; } else { MeshRenderer skyRenderer = dayNight._skyRenderer; obj = ((skyRenderer != null) ? ((Renderer)skyRenderer).sharedMaterial : null); } Material val6 = (Material)obj; if ((Object)(object)val6 != (Object)null) { SetColor(val6, SkyUpper, val); SetColor(val6, SkyMiddle, val2); SetColor(val6, SkyLower, val3); SetColor(val6, MoonColor, val4); if (val6.HasProperty(MoonSize)) { val6.SetFloat(MoonSize, num); } } Light val7 = ((dayNight != null) ? dayNight._moonLight : null); if ((Object)(object)val7 != (Object)null) { val7.color = Color.Lerp(sky.MoonLightColor, Keep(MoonGlow, sky.MoonLightColor.a), _blend); } RenderSettings.fogColor = val5; RenderSettings.fogDensity = num2; RenderSettings.ambientSkyColor = Toward(sky.AmbientSkyColor, 0.5f, 0.6f); RenderSettings.ambientEquatorColor = Toward(sky.AmbientEquatorColor, 0.4f, 0.6f); RenderSettings.ambientGroundColor = Toward(sky.AmbientGroundColor, 0.25f, 0.6f); } private static void SetColor(Material material, int id, Color color) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty(id)) { material.SetColor(id, color); } } private static Color Toward(Color current, float shade, float weight) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) Color bloodSkyColor = Settings.BloodSkyColor; Color val = default(Color); ((Color)(ref val))..ctor(bloodSkyColor.r * shade, bloodSkyColor.g * shade, bloodSkyColor.b * shade, current.a); return Color.Lerp(current, val, _blend * weight); } private static Color Keep(Color color, float alpha) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) return new Color(color.r, color.g, color.b, alpha); } } internal static class Json { public static T? Read<T>(string json) where T : class { try { return JsonSerializer.Deserialize<T>(json); } catch (Exception ex) when (((ex is JsonException || ex is NotSupportedException) ? 1 : 0) != 0) { return null; } } public static string Write<T>(T value) { return JsonSerializer.Serialize(value, new JsonSerializerOptions { WriteIndented = true }); } } public sealed class Core : MelonMod { internal static Instance Log { get; private set; } public override void OnInitializeMelon() { Log = ((MelonBase)this).LoggerInstance; Settings.Load(); Lang.Load(Settings.Language); Stats.Load(); GameLifecycle.OnPreLoad += Horde.Detach; GameLifecycle.OnPreSceneChange += Horde.Detach; GameLifecycle.OnPreSceneChange += BloodSky.Reset; GameLifecycle.OnLoadComplete += Horde.Attach; TimeManager.OnHourPass = (Action)Delegate.Combine(TimeManager.OnHourPass, new Action(Horde.OnHour)); TimeManager.OnHourPass = (Action)Delegate.Combine(TimeManager.OnHourPass, new Action(Infection.OnHour)); TimeManager.OnSleepEnd = (Action<int>)Delegate.Combine(TimeManager.OnSleepEnd, (Action<int>)delegate { Infection.Cure(); }); } public override void OnUpdate() { Horde.Update(); } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { MenuTheme.OnScene(sceneName); } public override void OnLateUpdate() { BloodSky.LateUpdate(); } internal static void Debug(string message) { if (Settings.DebugLogs) { Log.Msg(message); } } } internal static class Horde { public static readonly Vector3 Crypt = new Vector3(-46f, 1.06f, 74f); private const string SpeedControl = "tmlinfection_shamble"; private const float ThinkInterval = 0.5f; private const float BiteInterval = 0.2f; private const float ChaseRefresh = 0.75f; private const float WanderRadius = 10f; private const float PoolTimeout = 30f; private const int RiseAttempts = 16; private const float BannerGap = 4.5f; private const string GlowName = "TMLInfectionGlow"; private static readonly Random Rng = new Random(); private static readonly Dictionary<Zombie, float> Risen = new Dictionary<Zombie, float>(); private static readonly Dictionary<Zombie, float> Graves = new Dictionary<Zombie, float>(); private static readonly Dictionary<Zombie, float> NextBite = new Dictionary<Zombie, float>(); private static readonly HashSet<Zombie> Hooked = new HashSet<Zombie>(); private static readonly List<(float at, Vector3 position)> Bitten = new List<(float, Vector3)>(); private static readonly Queue<Vector3> Turning = new Queue<Vector3>(); private static bool _attached; private static Player? _hookedPlayer; private static float _nextThink; private static float _nextBite; private static int _turned; private static bool _disguised; public static bool Active { get; private set; } public static bool BloodMoon { get; private set; } public static int RisenCount => Risen.Count; public static int DeadCount => Graves.Count; private static bool IsHost { get { Lobby instance = Singleton<Lobby>.Instance; return instance == null || !instance.IsInLobby || instance.IsHost; } } public static void Attach() { _attached = true; MelonCoroutines.Start(WaitForPool()); } public static void Detach() { _attached = false; Active = false; Risen.Clear(); Graves.Clear(); NextBite.Clear(); Hooked.Clear(); ClearTurning(); Stats.Abandon(); Infection.Cure(); Hud.Hide(); } public static void OnHour() { if (!_attached) { return; } bool flag = Settings.Enabled && IsNightTime(); if (flag && !Active) { bool flag2 = IsBloodMoonTonight(); if (flag2 || RollsTonight()) { Rise(flag2); } } else if (!flag && Active) { Dawn(); } } public static void Rise(bool bloodMoon) { Active = true; BloodMoon = bloodMoon; Hud.Announce(bloodMoon ? Strings.BloodMoon : Strings.Rise, danger: true); Stats.NightStart(); if (bloodMoon && Settings.PatientZero) { Hud.AnnounceAfter(4.5f, Strings.PatientZero, danger: true); } Core.Debug("The dead rise" + (bloodMoon ? " under a Blood Moon" : "") + "."); } public static void Dawn() { Stats.Dawn(BloodMoon, 4.5f); Active = false; BloodMoon = false; ClearTurning(); if (IsHost) { foreach (Zombie item in Everyone()) { if (((NPC)item).IsDead) { Safely((Action)((NPC)item).Revive); } Bury(item); } } Graves.Clear(); NextBite.Clear(); Hud.Announce(Strings.Dawn, danger: false); Core.Debug("Dawn. The dead are buried."); } public static void Update() { if (_attached && Active) { float time = Time.time; if (IsHost && time >= _nextThink) { _nextThink = time + 0.5f; Think(time); } if (time >= _nextBite) { _nextBite = time + 0.2f; Bite(time); } } } private static IEnumerator WaitForPool() { float deadline = Time.realtimeSinceStartup + 30f; while (_attached && Time.realtimeSinceStartup < deadline && Pool().Count() < 24) { yield return (object)new WaitForSeconds(1f); } if (!_attached) { yield break; } foreach (Zombie item in Everyone()) { Hook(item); if (IsHost) { Bury(item); } } Player local = Player.Local; if (local != null && local != _hookedPlayer) { _hookedPlayer = local; local.OnDeath += Infection.Cure; local.OnDeath += Stats.Died; } Core.Debug($"{Pool().Count()} zombies waiting in the crypt."); OnHour(); } private static void Hook(Zombie zombie) { if (Hooked.Add(zombie)) { ((NPC)zombie).OnDeath += delegate { Reward(zombie); }; HideContact(zombie); if (zombie.Kind == ZombieKind.PatientZero) { Glow(zombie); } } } private static void Think(float now) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) List<Player> list = Player.All.Where((Player p) => p != null && !p.IsDead).ToList(); if (list.Count == 0) { return; } for (int num = Bitten.Count - 1; num >= 0; num--) { if (!(now < Bitten[num].at)) { Turning.Enqueue(Bitten[num].position); Bitten.RemoveAt(num); _turned++; } } List<Player> hunted = list.Where((Player p) => !Disguised(p)).ToList(); List<(NPC, Vector3)> prey = (List<(NPC, Vector3)>)(Settings.AttackNpcs ? ((IList)Prey()) : ((IList)new List<(NPC, Vector3)>())); int num2 = Math.Min(24, (BloodMoon ? Settings.BloodMoonZombies : Settings.Zombies) + _turned); int num3 = 0; foreach (Zombie item in Pool()) { Hook(item); if (((NPC)item).IsDead) { Risen.Remove(item); if (!Graves.TryGetValue(item, out var value)) { Graves[item] = now + (float)Settings.RespawnSeconds; } else if (now >= value) { Graves.Remove(item); Safely((Action)((NPC)item).Revive); Bury(item); } } else if (num3++ >= num2) { if (Risen.ContainsKey(item)) { Bury(item); } } else { Tend(item, list, hunted, prey, now); } } Zombie zombie = Boss(); if (zombie != null) { Hook(zombie); if (BloodMoon && Settings.PatientZero && !((NPC)zombie).IsDead) { Tend(zombie, list, hunted, prey, now); } else if (Risen.ContainsKey(zombie)) { Bury(zombie); } } } private static void Tend(Zombie zombie, List<Player> players, List<Player> hunted, List<(NPC npc, Vector3 position)> prey, float now) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) if (!Risen.TryGetValue(zombie, out var value)) { Raise(zombie, players, now); return; } Player val = players.OrderBy((Player p) => Vector3.Distance(((NPC)zombie).Position, p.Position)).First(); if (Vector3.Distance(((NPC)zombie).Position, val.Position) > Settings.DespawnDistance) { Bury(zombie); } else { if (now < value || !((NPC)zombie).IsConscious) { return; } float sight = zombie.Stats.Sight * (BloodMoon ? 2f : 1f); (Player, float) tuple = (from p in hunted select (player: p, distance: Vector3.Distance(((NPC)zombie).Position, p.Position)) into t where t.distance <= sight orderby t.distance select t).FirstOrDefault(); (NPC, Vector3, float) tuple2 = (from p in prey select (npc: p.npc, position: p.position, distance: Vector3.Distance(((NPC)zombie).Position, p.position)) into t where t.distance <= sight orderby t.distance select t).FirstOrDefault(); if (tuple.Item1 != null && ((Object)(object)tuple2.Item1 == (Object)null || tuple.Item2 <= tuple2.Item3)) { Go(zombie, tuple.Item1.Position); Risen[zombie] = now + 0.75f; } else if ((Object)(object)tuple2.Item1 != (Object)null) { if (tuple2.Item3 <= Settings.AttackRange) { BiteNpc(zombie, tuple2.Item1, now); } Go(zombie, tuple2.Item2); Risen[zombie] = now + 0.75f; } else if (!Moving(zombie)) { Vector3 position = ((NPC)zombie).Position; Vector3 val2 = val.Position - ((NPC)zombie).Position; Vector3 centre = position + ((Vector3)(ref val2)).normalized * 10f * 0.5f; Go(zombie, (Vector3)(((??)NearbyPoint(centre, 2f, 10f)) ?? ((NPC)zombie).Position)); Risen[zombie] = now + 4f + (float)Rng.NextDouble() * 4f; } } } private static void Bite(float now) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) Player local = Player.Local; if (local == null || local.IsDead) { return; } bool num = Disguised(local); if (num && !_disguised) { Hud.Announce(Strings.Disguised, danger: false); } _disguised = num; if (num) { return; } foreach (Zombie item in Everyone()) { if (((NPC)item).IsDead || !((NPC)item).IsConscious) { continue; } NPC val = Live(item); if (val != null && val.isVisible && !(Vector3.Distance(((NPC)item).Position, local.Position) > Settings.AttackRange) && (!NextBite.TryGetValue(item, out var value) || !(now < value))) { NextBite[item] = now + Settings.AttackCooldown; int num2 = BiteDamage(item, Settings.Damage); if (num2 > 0) { local.Damage(num2); } Infection.Bitten(); } } } private static void BiteNpc(Zombie zombie, NPC victim, float now) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) if (NextBite.TryGetValue(zombie, out var value) && now < value) { return; } NextBite[zombie] = now + Settings.AttackCooldown; int damage = BiteDamage(zombie, Settings.NpcDamage); if (damage > 0) { Safely(delegate { victim.Health.TakeDamage((float)damage, true); }); } Safely((Action)victim.SetPanicked_Server); if (Settings.BittenRise && (Object)(object)victim.Health != (Object)null && victim.Health.IsDead) { Bitten.Add((now + Settings.BittenRiseSeconds, ((Component)victim).transform.position)); } } private static void ClearTurning() { Bitten.Clear(); Turning.Clear(); _turned = 0; } private static bool Disguised(Player player) { if (!Settings.ZombifyingDisguise) { return false; } try { Player component = ((Component)player.Transform).GetComponent<Player>(); object obj; if (component == null) { obj = null; } else { ProductItemInstance consumedProduct = component.ConsumedProduct; obj = ((consumedProduct != null) ? ((ItemInstance)consumedProduct).Definition : null); } PropertyItemDefinition obj2 = ((obj != null) ? ((Il2CppObjectBase)obj).TryCast<PropertyItemDefinition>() : null); List<Effect> val = ((obj2 != null) ? obj2.Properties : null); if (val == null) { return false; } for (int i = 0; i < val.Count; i++) { if ((Object)(object)val[i] != (Object)null && val[i].Name.IndexOf("Zombif", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } catch (Exception) { return false; } } private static void Glow(Zombie zombie) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)((NPC)zombie).gameObject == (Object)null) && !((Object)(object)((NPC)zombie).gameObject.transform.Find("TMLInfectionGlow") != (Object)null)) { GameObject val = new GameObject("TMLInfectionGlow"); val.transform.SetParent(((NPC)zombie).gameObject.transform, false); val.transform.localPosition = new Vector3(0f, 1.7f, 0f); Light obj = val.AddComponent<Light>(); obj.type = (LightType)2; obj.color = new Color(1f, 0.08f, 0.04f); obj.range = 5f; obj.intensity = 3f; } } private static int BiteDamage(Zombie zombie, int baseDamage) { return Mathf.RoundToInt((float)baseDamage * zombie.Stats.Damage * (BloodMoon ? Settings.BloodMoonDamage : 1f)); } private static List<(NPC npc, Vector3 position)> Prey() { //IL_0081: Unknown result type (might be due to invalid IL or missing references) List<(NPC, Vector3)> list = new List<(NPC, Vector3)>(); List<NPC> nPCRegistry = NPCManager.NPCRegistry; if (nPCRegistry == null) { return list; } for (int i = 0; i < nPCRegistry.Count; i++) { try { NPC val = nPCRegistry[i]; if (!((Object)(object)val == (Object)null) && !val.ID.StartsWith("tmlinfection_zombie_") && val.isVisible && !val.isInBuilding && !val.IsInVehicle && val.IsConscious && !((Object)(object)val.Health == (Object)null) && !val.Health.IsDead) { list.Add((val, ((Component)val).transform.position)); } } catch (Exception) { } } return list; } private static void Reward(Zombie zombie) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (zombie.Kind == ZombieKind.PatientZero) { Hud.Announce(Strings.PatientZeroDown, danger: false); } Player local = Player.Local; if (local != null && !local.IsDead && !(Vector3.Distance(((NPC)zombie).Position, local.Position) > Settings.RewardRange)) { Stats.Kill(); float num = ((zombie.Kind == ZombieKind.PatientZero) ? Settings.PatientZeroReward : Settings.KillReward); if (num > 0f) { Money.ChangeCashBalance(num, true, true); } Core.Debug($"{((NPC)zombie).FullName} put down. +{num} cash."); } } private static void Raise(Zombie zombie, List<Player> players, float now) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) NPC npc = Live(zombie); if (npc == null) { return; } Vector3? spot = null; NavMeshHit val = default(NavMeshHit); if (zombie.Kind != ZombieKind.PatientZero && Turning.Count > 0 && NavMesh.SamplePosition(Turning.Dequeue(), ref val, 3f, -1)) { spot = ((NavMeshHit)(ref val)).position; } Vector3 position = players[Rng.Next(players.Count)].Position; for (int i = 0; i < 16; i++) { if (spot.HasValue) { break; } Vector3? val2 = NearbyPoint(position, Settings.SpawnMinDistance, Settings.SpawnMaxDistance); if (val2.HasValue) { Vector3 p = val2.GetValueOrDefault(); if (players.All((Player pl) => Vector3.Distance(pl.Position, p) >= Settings.SpawnMinDistance * 0.8f) && Risen.Keys.All((Zombie other) => other == zombie || Vector3.Distance(((NPC)other).Position, p) >= Settings.Spacing)) { spot = p; } } } if (!spot.HasValue) { Core.Debug($"No ground to raise {((NPC)zombie).FullName} near {position}."); return; } HideContact(zombie); Safely(delegate { npc.Movement.SetAgentEnabled(true); }); Safely(delegate { //IL_0011: Unknown result type (might be due to invalid IL or missing references) ((NPC)zombie).Movement.Warp(spot.Value); }); npc.SetVisible(true, true); if (((NPC)zombie).Movement.DoesSpeedControlExist("tmlinfection_shamble")) { ((NPC)zombie).Movement.RemoveSpeedControl("tmlinfection_shamble"); } ((NPC)zombie).Movement.AddSpeedControl("tmlinfection_shamble", 60, (BloodMoon ? Settings.BloodMoonSpeed : Settings.Speed) * zombie.Stats.Speed); Safely(zombie.Toughen); Risen[zombie] = now; Core.Debug($"{((NPC)zombie).FullName} rose at {spot.Value}."); } private static void Bury(Zombie zombie) { Risen.Remove(zombie); NPC val = Live(zombie); if (val != null) { Safely(delegate { ((NPC)zombie).Movement.Stop(); }); Safely(delegate { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((NPC)zombie).Movement.Warp(Crypt); }); if (((NPC)zombie).Movement.DoesSpeedControlExist("tmlinfection_shamble")) { ((NPC)zombie).Movement.RemoveSpeedControl("tmlinfection_shamble"); } val.SetVisible(false, true); } } private static Vector3? NearbyPoint(Vector3 centre, float min, float max) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) float num = (float)(Rng.NextDouble() * Math.PI * 2.0); float num2 = min + (float)Rng.NextDouble() * (max - min); NavMeshHit val = default(NavMeshHit); if (!NavMesh.SamplePosition(centre + new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num)) * num2, ref val, 4f, -1)) { return null; } return ((NavMeshHit)(ref val)).position; } private static void Go(Zombie zombie, Vector3 destination) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) Safely(delegate { //IL_000c: Unknown result type (might be due to invalid IL or missing references) ((NPC)zombie).Movement.SetDestination(destination); }); } private static bool Moving(Zombie zombie) { try { NPC? obj = Live(zombie); return obj != null && obj.Movement.IsMoving; } catch (Exception) { return false; } } private static void HideContact(Zombie zombie) { NPC? obj = Live(zombie); MSGConversation val = ((obj != null) ? obj.MSGConversation : null); if (val != null && val.uiCreated) { val.SetEntryVisibility(false); } } private static bool IsNightTime() { int num = TimeManager.CurrentTime / 100; if (Settings.RiseHour >= Settings.DawnHour) { if (num < Settings.RiseHour) { return num < Settings.DawnHour; } return true; } if (num >= Settings.RiseHour) { return num < Settings.DawnHour; } return false; } private static bool RollsTonight() { if (Settings.RiseChance >= 100) { return true; } return new Random(((TimeManager.CurrentTime / 100 < Settings.DawnHour && Settings.RiseHour > Settings.DawnHour) ? (TimeManager.ElapsedDays - 1) : TimeManager.ElapsedDays) * 7919 + 17).Next(100) < Settings.RiseChance; } private static bool IsBloodMoonTonight() { DateTime now = DateTime.Now; if (Settings.BloodMoonOnHalloween && now.Month == 10 && now.Day == 31) { return true; } if (Settings.BloodMoonEvery > 0) { return (TimeManager.ElapsedDays + 1) % Settings.BloodMoonEvery == 0; } return false; } private static IEnumerable<Zombie> Pool() { return from z in NPC.All.OfType<Zombie>().Where(delegate(Zombie z) { if (z.Kind != ZombieKind.PatientZero) { NPC val = Live(z); if (val != null) { return ((NetworkBehaviour)val).IsSpawned; } return false; } return false; }) orderby ((NPC)z).ID select z; } private static Zombie? Boss() { return NPC.All.OfType<Zombie>().FirstOrDefault(delegate(Zombie z) { if (z.Kind == ZombieKind.PatientZero) { NPC val = Live(z); if (val != null) { return ((NetworkBehaviour)val).IsSpawned; } return false; } return false; }); } private static IEnumerable<Zombie> Everyone() { Zombie zombie = Boss(); if (zombie == null) { return Pool(); } return Pool().Append(zombie); } private static NPC? Live(Zombie zombie) { if (!((Object)(object)((NPC)zombie).gameObject == (Object)null)) { return ((NPC)zombie).gameObject.GetComponent<NPC>(); } return null; } private static void Safely(Action action) { try { action(); } catch (Exception ex) { Core.Debug("Zombie action failed: " + ex.Message); } } } internal static class Hud { private const float BannerSeconds = 4f; private static readonly Color Backdrop = new Color(0.06f, 0.07f, 0.06f, 0.72f); private static readonly Color Danger = new Color(1f, 0.42f, 0.32f); private static readonly Color Calm = new Color(0.85f, 0.88f, 0.8f); private static readonly Color Sick = new Color(0.62f, 0.86f, 0.4f); private static GameObject? _root; private static GameObject? _banner; private static GameObject? _meter; private static Text? _bannerText; private static Text? _meterText; private static int _run; public static void Announce(string text, bool danger) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Ensure(); _bannerText.text = text; ((Graphic)_bannerText).color = (danger ? Danger : Calm); _banner.SetActive(true); MelonCoroutines.Start(HideBanner(++_run)); } public static void AnnounceAfter(float seconds, string text, bool danger) { MelonCoroutines.Start(Later(seconds, text, danger)); } public static void ShowInfection(float level) { if (!(level <= 0f) || !((Object)(object)_meter == (Object)null)) { Ensure(); _meter.SetActive(level > 0f); _meterText.text = Strings.Infection(Mathf.CeilToInt(level)); } } public static void Hide() { _run++; if ((Object)(object)_banner != (Object)null) { _banner.SetActive(false); } if ((Object)(object)_meter != (Object)null) { _meter.SetActive(false); } } private static IEnumerator Later(float seconds, string text, bool danger) { int run = _run; yield return (object)new WaitForSeconds(seconds); if (run == _run || (Object)(object)_banner == (Object)null || !_banner.activeSelf) { Announce(text, danger); } } private static IEnumerator HideBanner(int run) { yield return (object)new WaitForSeconds(4f); if (run == _run && (Object)(object)_banner != (Object)null) { _banner.SetActive(false); } } private static void Ensure() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root != (Object)null)) { _root = new GameObject("TMLInfectionHud"); Object.DontDestroyOnLoad((Object)(object)_root); Canvas obj = _root.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 500; CanvasScaler obj2 = _root.AddComponent<CanvasScaler>(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(1920f, 1080f); (_banner, _bannerText) = Panel("Banner", new Vector2(0.34f, 0.9f), new Vector2(0.66f, 0.95f), 22, (FontStyle)1); (_meter, _meterText) = Panel("Infection", new Vector2(0.44f, 0.86f), new Vector2(0.56f, 0.89f), 16, (FontStyle)0); ((Graphic)_meterText).color = Sick; _banner.SetActive(false); _meter.SetActive(false); } } private static (GameObject, Text) Panel(string name, Vector2 min, Vector2 max, int size, FontStyle style) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.Panel(name, _root.transform, Backdrop, (Vector2?)min, (Vector2?)max, false); Stretch(val.GetComponent<RectTransform>()); Text val2 = UIFactory.Text("Label", "", val.transform, size, (TextAnchor)4, style); RectTransform component = ((Component)val2).GetComponent<RectTransform>(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; Stretch(component); return (val, val2); } private static void Stretch(RectTransform rect) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) rect.offsetMin = Vector2.zero; rect.offsetMax = Vector2.zero; } } internal static class Infection { private const float Full = 100f; public static float Level { get; private set; } public static void Bitten() { Set(Level + Settings.InfectionPerHit); } public static void OnHour() { if (Level <= 0f) { return; } if (Level < 100f) { Set(Level - Settings.InfectionDecayPerHour); } else if (Settings.InfectionDamage > 0) { Player local = Player.Local; if (local != null && !local.IsDead) { local.Damage(Settings.InfectionDamage); } } } public static void Cure() { Set(0f); } public static void Set(float level) { bool flag = Level >= 100f; Level = Mathf.Clamp(level, 0f, 100f); if (Level >= 100f && !flag) { Hud.Announce(Strings.Infected, danger: true); } Hud.ShowInfection(Level); } } internal static class Lang { private const string Fallback = "en"; private static Dictionary<string, string> _fallback = new Dictionary<string, string>(); private static Dictionary<string, string> _active = new Dictionary<string, string>(); public static string Folder => Path.Combine(MelonEnvironment.UserDataDirectory, "TMLInfection", "lang"); public static bool Exists(string language) { if (!File.Exists(Path.Combine(Folder, language + ".json"))) { return Embedded(language) != null; } return true; } public static void Load(string language) { Directory.CreateDirectory(Folder); string[] array = new string[2] { "en", "es" }; for (int i = 0; i < array.Length; i++) { Export(array[i]); } _fallback = Read("en"); _active = ((language == "en") ? _fallback : Read(language)); if (_active.Count == 0) { Core.Log.Warning("No translations found for '" + language + "'. Using English."); _active = _fallback; } } public static string T(string key) { if (!_active.TryGetValue(key, out string value)) { if (!_fallback.TryGetValue(key, out string value2)) { return key; } return value2; } return value; } public static string T(string key, params object[] args) { return string.Format(CultureInfo.InvariantCulture, T(key), args); } private static Dictionary<string, string> Read(string language) { Dictionary<string, string> dictionary = Parse(Embedded(language)) ?? new Dictionary<string, string>(); string text = Path.Combine(Folder, language + ".json"); if (!File.Exists(text)) { return dictionary; } Dictionary<string, string> dictionary2 = Parse(File.ReadAllText(text)); if (dictionary2 == null) { Core.Log.Warning(text + " isn't valid JSON. Using the built-in text."); return dictionary; } foreach (KeyValuePair<string, string> item in dictionary2) { item.Deconstruct(out var key, out var value); string key2 = key; string value2 = value; dictionary[key2] = value2; } return dictionary; } private static void Export(string language) { string path = Path.Combine(Folder, language + ".json"); if (!File.Exists(path)) { string text = Embedded(language); if (text != null) { File.WriteAllText(path, text); } } } private static string? Embedded(string language) { using Stream stream = typeof(Lang).Assembly.GetManifestResourceStream("TMLInfection.lang." + language + ".json"); if (stream == null) { return null; } using StreamReader streamReader = new StreamReader(stream); return streamReader.ReadToEnd(); } private static Dictionary<string, string>? Parse(string? json) { if (string.IsNullOrEmpty(json)) { return null; } return Json.Read<Dictionary<string, string>>(json); } } internal static class MenuTheme { private sealed class Drip { public RectTransform Streak; public RectTransform Bulb; public float Width; public float Length; public float Target; public float Speed; public float Delay; public float DropY; public float DropSpeed; public bool Falling; } private const string MenuScene = "Menu"; private const float ZombifySeconds = 20f; private const int VignetteSize = 128; private const int DripCount = 18; private const float BandHeight = 34f; private const float DropGravity = 1400f; private static readonly Color Blood = new Color(0.55f, 0.03f, 0.03f); private static readonly Color DarkBlood = new Color(0.3f, 0.01f, 0.01f); private static readonly Color DeadSkin = new Color(0.47f, 0.53f, 0.43f); private static readonly Color Tagline = new Color(0.85f, 0.12f, 0.1f); private static int _run; public static void OnScene(string sceneName) { _run++; if (sceneName == "Menu" && Settings.MenuTheme) { MelonCoroutines.Start(Haunt(_run)); } } private static IEnumerator Haunt(int run) { yield return null; if (run != _run) { yield break; } List<(Light light, float intensity)> lights = TintScene(); List<Drip> drips; RawImage vignette = Overlay(out drips); bool zombified = false; float start = Time.realtimeSinceStartup; while (run == _run && (Object)(object)vignette != (Object)null) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!zombified && realtimeSinceStartup - start < 20f) { zombified = Zombify(); } ((Graphic)vignette).color = new Color(1f, 1f, 1f, 0.75f + 0.2f * Mathf.Sin(realtimeSinceStartup * 2.2f)); Flicker(lights); Bleed(drips, realtimeSinceStartup - start, Time.unscaledDeltaTime); yield return null; } } private static List<(Light light, float intensity)> TintScene() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) List<(Light, float)> list = new List<(Light, float)>(); foreach (Light item in Object.FindObjectsOfType<Light>()) { item.color = Color.Lerp(item.color, Blood, 0.6f); list.Add((item, item.intensity)); } RenderSettings.ambientLight = Color.Lerp(RenderSettings.ambientLight, Blood * 0.5f, 0.6f); RenderSettings.fogColor = Blood * 0.4f; return list; } private static void Flicker(List<(Light light, float intensity)> lights) { foreach (var (val, num) in lights) { if ((Object)(object)val != (Object)null) { val.intensity = num * ((Random.value < 0.03f) ? 0.35f : 1f); } } } private static bool Zombify() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) try { MainMenuRig val = Object.FindObjectOfType<MainMenuRig>(); if ((Object)(object)val == (Object)null || (Object)(object)val.Avatar == (Object)null || !((Component)val.Avatar).gameObject.activeInHierarchy) { return false; } val.Avatar.Appearance.SetSkinColor(DeadSkin); val.Avatar.Appearance.SetEyeShadowVisible(true); return true; } catch (Exception ex) { Core.Debug("Could not zombify the menu character: " + ex.Message); return true; } } private static RawImage Overlay(out List<Drip> drips) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("TMLInfectionMenu"); Canvas obj = val.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = -10; CanvasScaler obj2 = val.AddComponent<CanvasScaler>(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(1920f, 1080f); RawImage obj3 = Child<RawImage>(val, "Vignette", Vector2.zero, Vector2.one); obj3.texture = (Texture)(object)VignetteTexture(); ((Graphic)obj3).raycastTarget = false; drips = Drips(val); DateTime now = DateTime.Now; string text = ((now.Month == 10 && now.Day == 31) ? Strings.MenuHalloween : Strings.MenuTagline); Text obj4 = UIFactory.Text("Tagline", text, val.transform, 30, (TextAnchor)8, (FontStyle)1); RectTransform component = ((Component)obj4).GetComponent<RectTransform>(); component.anchorMin = new Vector2(0.55f, 0.095f); component.anchorMax = new Vector2(0.98f, 0.145f); component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; ((Graphic)obj4).color = Tagline; ((Graphic)obj4).raycastTarget = false; return obj3; } private static T Child<T>(GameObject parent, string name, Vector2 min, Vector2 max) where T : Component { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent.transform, false); T result = val.AddComponent<T>(); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = min; component.anchorMax = max; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; return result; } private static List<Drip> Drips(GameObject root) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) RawImage obj = Child<RawImage>(root, "BloodBand", new Vector2(0f, 1f), Vector2.one); ((Graphic)obj).rectTransform.offsetMin = new Vector2(0f, -34f); obj.texture = (Texture)(object)BandTexture(); obj.uvRect = new Rect(0f, 0f, 6f, 1f); ((Graphic)obj).raycastTarget = false; Texture2D texture = StreakTexture(); Texture2D texture2 = BulbTexture(); List<Drip> list = new List<Drip>(); for (int i = 0; i < 18; i++) { float num = ((float)i + 0.5f + Random.Range(-0.35f, 0.35f)) / 18f; float num2 = Mathf.Abs(num - 0.5f) * 2f; float num3 = Random.Range(7f, 15f); RawImage val = Child<RawImage>(root, "Drip", new Vector2(num, 1f), new Vector2(num, 1f)); val.texture = (Texture)(object)texture; ((Graphic)val).raycastTarget = false; ((Graphic)val).rectTransform.pivot = new Vector2(0.5f, 1f); ((Graphic)val).rectTransform.anchoredPosition = new Vector2(0f, -20.400002f); RawImage val2 = Child<RawImage>(root, "DripBulb", new Vector2(num, 1f), new Vector2(num, 1f)); val2.texture = (Texture)(object)texture2; ((Graphic)val2).raycastTarget = false; ((Graphic)val2).rectTransform.sizeDelta = new Vector2(num3 * 1.5f, num3 * 1.8f); list.Add(new Drip { Streak = ((Graphic)val).rectTransform, Bulb = ((Graphic)val2).rectTransform, Width = num3, Target = Random.Range(30f, 110f) + num2 * num2 * Random.Range(150f, 420f), Speed = Random.Range(12f, 40f), Delay = Random.Range(0f, 5f) }); } return list; } private static void Bleed(List<Drip> drips, float elapsed, float delta) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) foreach (Drip drip in drips) { if ((Object)(object)drip.Streak == (Object)null || elapsed < drip.Delay) { continue; } if (drip.Length < drip.Target) { drip.Length = Mathf.Min(drip.Target, drip.Length + drip.Speed * delta * (1f - 0.7f * drip.Length / drip.Target)); } drip.Streak.sizeDelta = new Vector2(drip.Width, drip.Length); float dropY = -20.400002f - drip.Length; if (drip.Falling) { drip.DropSpeed += 1400f * delta; drip.DropY -= drip.DropSpeed * delta; if (drip.DropY < -1200f) { drip.Falling = false; drip.Target += Random.Range(5f, 25f); drip.DropY = dropY; } } else { drip.DropY = dropY; if (drip.Length >= drip.Target && Random.value < delta * 0.06f) { drip.Falling = true; drip.DropSpeed = 0f; } } float num = Mathf.Clamp01(drip.Length / 25f); ((Transform)drip.Bulb).localScale = new Vector3(num, num, 1f); drip.Bulb.anchoredPosition = new Vector2(0f, drip.DropY); } } private static Color Shade(float across) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Exp((0f - (across + 0.35f)) * (across + 0.35f) * 18f) * 0.3f; Color val = Color.Lerp(DarkBlood, Blood, 1f - Mathf.Abs(across) * 0.8f); return new Color(val.r + num, val.g + num * 0.3f, val.b + num * 0.3f); } private static Texture2D StreakTexture() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(16, 1, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1 }; for (int i = 0; i < 16; i++) { float num = ((float)i + 0.5f) / 16f * 2f - 1f; Color val2 = Shade(num); val2.a = Mathf.Clamp01((1f - Mathf.Abs(num)) * 3f); val.SetPixel(i, 0, val2); } val.Apply(); return val; } private static Texture2D BulbTexture() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1 }; for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { float num = ((float)j + 0.5f) / 32f * 2f - 1f; float num2 = ((float)i + 0.5f) / 32f * 2f - 1f; float num3 = Mathf.Lerp(1f, 0.55f, Mathf.Clamp01(num2)); float num4 = Mathf.Sqrt(num * num / (num3 * num3) + num2 * num2); Color val2 = Shade(num / num3); val2.a = Mathf.Clamp01((1f - num4) * 6f); val.SetPixel(j, i, val2); } } val.Apply(); return val; } private static Texture2D BandTexture() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(256, 32, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)0 }; for (int i = 0; i < 256; i++) { float num = (float)i * 2f * (float)Math.PI / 256f; float num2 = 0.55f + 0.18f * Mathf.Sin(num * 3f) + 0.12f * Mathf.Sin(num * 7f + 1f) + 0.07f * Mathf.Sin(num * 13f + 2f); for (int j = 0; j < 32; j++) { float num3 = 1f - ((float)j + 0.5f) / 32f; Color val2 = Color.Lerp(Blood, DarkBlood, 1f - num3 / num2); val2.a = Mathf.Clamp01((num2 - num3) * 12f); val.SetPixel(i, j, val2); } } val.Apply(); return val; } private static Texture2D VignetteTexture() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1 }; float num = 63.5f; for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { float num2 = ((float)j - num) / num; float num3 = ((float)i - num) / num; float num4 = Mathf.Clamp01((Mathf.Sqrt(num2 * num2 + num3 * num3) - 0.55f) / 0.6f); val.SetPixel(j, i, new Color(Blood.r * 0.6f, 0f, 0f, num4 * num4 * 0.85f)); } } val.Apply(); return val; } } internal static class Settings { internal sealed class KindStats { public int Health { get; } public float Sight { get; } public float Speed { get; } public float Damage { get; } public KindStats(int health, float sight, float speed, float damage) { Health = health; Sight = sight; Speed = speed; Damage = damage; } } public const int PoolSize = 24; private static MelonPreferences_Category _category = null; private static MelonPreferences_Entry<bool> _debugLogs = null; private static MelonPreferences_Entry<bool> _enabled = null; public static readonly Dictionary<ZombieKind, KindStats> Kinds = new Dictionary<ZombieKind, KindStats>(); public static bool DebugLogs { get { return _debugLogs.Value; } set { _debugLogs.Value = value; _category.SaveToFile(false); } } public static bool Enabled { get { return _enabled.Value; } set { _enabled.Value = value; _category.SaveToFile(false); } } public static string Language { get; private set; } = "en"; public static int RiseHour { get; private set; } public static int DawnHour { get; private set; } public static int RiseChance { get; private set; } public static int Zombies { get; private set; } public static float SpawnMinDistance { get; private set; } public static float SpawnMaxDistance { get; private set; } public static float DespawnDistance { get; private set; } public static float Spacing { get; private set; } public static float Speed { get; private set; } public static int RespawnSeconds { get; private set; } public static int Damage { get; private set; } public static float AttackRange { get; private set; } public static bool AttackNpcs { get; private set; } public static int NpcDamage { get; private set; } public static float AttackCooldown { get; private set; } public static float KillReward { get; private set; } public static float RewardRange { get; private set; } public static float InfectionPerHit { get; private set; } public static float InfectionDecayPerHour { get; private set; } public static int InfectionDamage { get; private set; } public static int BloodMoonEvery { get; private set; } public static bool BloodMoonOnHalloween { get; private set; } public static int BloodMoonZombies { get; private set; } public static float BloodMoonSpeed { get; private set; } public static float BloodMoonDamage { get; private set; } public static bool BittenRise { get; private set; } public static float BittenRiseSeconds { get; private set; } public static bool PatientZero { get; private set; } public static float PatientZeroReward { get; private set; } public static bool ZombifyingDisguise { get; private set; } public static float BloodMoonFog { get; private set; } public static bool MenuTheme { get; private set; } public static bool BloodSky { get; private set; } public static Color BloodSkyColor { get; private set; } public static float BloodSkyStrength { get; private set; } public static float BloodMoonSkyStrength { get; private set; } public static float BloodSkyFadeSeconds { get; private set; } public static float BloodMoonSize { get; private set; } public static void Load() { //IL_0630: Unknown result type (might be due to invalid IL or missing references) _category = MelonPreferences.CreateCategory("TMLInfection", "TML Infection"); _enabled = _category.CreateEntry<bool>("Enabled", true, (string)null, "Zombies rise at night. Turn off to keep the mod installed but quiet.", false, false, (ValueValidator)null, (string)null); _debugLogs = _category.CreateEntry<bool>("DebugLogs", false, (string)null, "Log horde events to the MelonLoader console.", false, false, (ValueValidator)null, (string)null); Language = _category.CreateEntry<string>("Language", "en", (string)null, "Text language: en or es. Files are in UserData/TMLInfection/lang.", false, false, (ValueValidator)null, (string)null).Value.Trim().ToLowerInvariant(); RiseHour = Int("RiseHour", 21, 0, 23, "Hour the dead rise (24-hour clock)."); DawnHour = Int("DawnHour", 6, 0, 23, "Hour the dead go back to their graves."); if (RiseHour == DawnHour) { Core.Log.Warning($"RiseHour and DawnHour are both {RiseHour}. Using 21 and 6."); RiseHour = 21; DawnHour = 6; } RiseChance = Int("RiseChance", 100, 0, 100, "Percent chance the dead rise on a normal night. Blood Moons always rise."); Zombies = Int("Zombies", 12, 1, 24, $"Zombies hunting at once on a normal night (most {24})."); SpawnMinDistance = Float("SpawnMinDistance", 20f, 5f, 200f, "Closest a zombie rises to a player, in metres."); SpawnMaxDistance = Float("SpawnMaxDistance", 80f, 5f, 200f, "Furthest a zombie rises from a player, in metres."); if (SpawnMaxDistance < SpawnMinDistance) { Core.Log.Warning($"SpawnMaxDistance ({SpawnMaxDistance}) is below SpawnMinDistance ({SpawnMinDistance}). Using 20 and 80."); SpawnMinDistance = 20f; SpawnMaxDistance = 80f; } DespawnDistance = Float("DespawnDistance", 130f, 20f, 500f, "Zombies further than this from every player sink and rise again nearer."); Spacing = Float("Spacing", 15f, 0f, 100f, "Fewest metres between zombies when they rise, so the horde spreads out."); Speed = Float("Speed", 0.45f, 0.1f, 2f, "Base zombie walk speed (1 = normal pedestrian). Each type multiplies this."); RespawnSeconds = Int("RespawnSeconds", 45, 5, 3600, "Seconds before a killed zombie rises again."); Damage = Int("Damage", 8, 0, 100, "Health a zombie bite takes from a player. Each type multiplies this."); AttackRange = Float("AttackRange", 1.6f, 0.5f, 5f, "Reach of a zombie bite, in metres."); AttackNpcs = _category.CreateEntry<bool>("AttackNpcs", true, (string)null, "Zombies also hunt and bite NPCs, cops included. They never attack each other.", false, false, (ValueValidator)null, (string)null).Value; NpcDamage = Int("NpcDamage", 40, 0, 100, "Health a zombie bite takes from an NPC. Each type multiplies this."); AttackCooldown = Float("AttackCooldown", 1.5f, 0.2f, 30f, "Seconds between bites from the same zombie."); KillReward = Float("KillReward", 40f, 0f, 100000f, "Cash for every player near a zombie when it dies."); RewardRange = Float("RewardRange", 20f, 1f, 200f, "Players this close to a dying zombie get the reward."); InfectionPerHit = Float("InfectionPerHit", 12f, 0f, 100f, "Infection added by each bite, out of 100."); InfectionDecayPerHour = Float("InfectionDecayPerHour", 4f, 0f, 100f, "Infection lost each in-game hour while below 100."); InfectionDamage = Int("InfectionDamage", 5, 0, 100, "Health lost each in-game hour while fully infected. Sleep cures it."); BloodMoonEvery = Int("BloodMoonEvery", 7, 0, 365, "Every this many nights is a Blood Moon (0 = never)."); BloodMoonOnHalloween = _category.CreateEntry<bool>("BloodMoonOnHalloween", true, (string)null, "Every night is a Blood Moon on 31 October (real date).", false, false, (ValueValidator)null, (string)null).Value; BloodMoonZombies = Int("BloodMoonZombies", 24, 1, 24, "Zombies hunting at once on a Blood Moon."); BloodMoonSpeed = Float("BloodMoonSpeed", 0.7f, 0.1f, 2f, "Zombie walk speed on a Blood Moon."); BloodMoonDamage = Float("BloodMoonDamage", 1.5f, 0.1f, 10f, "Bite damage multiplier on a Blood Moon."); Kind(ZombieKind.Shambler, 100, 2f, 1f, 1f); Kind(ZombieKind.Nightwalker, 50, 12f, 1.6f, 0.6f); Kind(ZombieKind.Hunter, 100, 8f, 1.2f, 1.2f); Kind(ZombieKind.Brute, 200, 4f, 0.7f, 2f); Kind(ZombieKind.PatientZero, 1000, 15f, 0.9f, 3f); BittenRise = _category.CreateEntry<bool>("BittenRise", true, (string)null, "NPCs killed by a zombie get back up as zombies.", false, false, (ValueValidator)null, (string)null).Value; BittenRiseSeconds = Float("BittenRiseSeconds", 6f, 0f, 120f, "Seconds before a bitten NPC rises."); PatientZero = _category.CreateEntry<bool>("PatientZero", true, (string)null, "Patient Zero, a giant boss zombie, walks on every Blood Moon.", false, false, (ValueValidator)null, (string)null).Value; PatientZeroReward = Float("PatientZeroReward", 2500f, 0f, 1000000f, "Cash for every player near Patient Zero when it dies."); ZombifyingDisguise = _category.CreateEntry<bool>("ZombifyingDisguise", true, (string)null, "Zombies ignore players under a product with the Zombifying effect.", false, false, (ValueValidator)null, (string)null).Value; BloodMoonFog = Float("BloodMoonFog", 2.5f, 1f, 10f, "How much thicker the fog gets on a Blood Moon (1 = normal)."); MenuTheme = _category.CreateEntry<bool>("MenuTheme", true, (string)null, "Give the main menu a blood-red, zombified Halloween look.", false, false, (ValueValidator)null, (string)null).Value; BloodSky = _category.CreateEntry<bool>("BloodSky", true, (string)null, "The night sky turns blood red while the dead are out.", false, false, (ValueValidator)null, (string)null).Value; BloodSkyColor = Hex("BloodSkyColor", "#8A0A0A", "Colour of the blood sky, as a hex code like #8A0A0A."); BloodSkyStrength = Float("BloodSkyStrength", 0.6f, 0f, 1f, "How red the sky gets on a normal zombie night (0 = untouched, 1 = full)."); BloodMoonSkyStrength = Float("BloodMoonSkyStrength", 1f, 0f, 1f, "How red the sky gets on a Blood Moon."); BloodSkyFadeSeconds = Float("BloodSkyFadeSeconds", 10f, 0f, 120f, "Seconds the sky takes to turn red at nightfall and back at dawn."); BloodMoonSize = Float("BloodMoonSize", 1.6f, 1f, 4f, "How much bigger the moon looks on a Blood Moon (1 = normal)."); } private static float Float(string id, float fallback, float min, float max, string description) { MelonPreferences_Entry<float> val = _category.CreateEntry<float>(id, fallback, (string)null, description, false, false, (ValueValidator)null, (string)null); if (val.Value >= min && val.Value <= max) { return val.Value; } Core.Log.Warning($"{id} must be between {min} and {max}, got {val.Value}. Using {fallback}."); return fallback; } private static void Kind(ZombieKind kind, int health, float sight, float speed, float damage) { string text = kind.ToString(); Kinds[kind] = new KindStats(Int(text + "Health", health, 1, 5000, "Health of a " + text + "."), Float(text + "Sight", sight, 0.5f, 200f, "Metres at which a " + text + " notices and chases someone."), Float(text + "Speed", speed, 0.1f, 5f, text + " walk speed, as a multiple of Speed."), Float(text + "Damage", damage, 0f, 10f, text + " bite damage, as a multiple of Damage and NpcDamage.")); } private static Color Hex(string id, string fallback, string description) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) MelonPreferences_Entry<string> val = _category.CreateEntry<string>(id, fallback, (string)null, description, false, false, (ValueValidator)null, (string)null); if (TryHex(val.Value, out var color)) { return color; } Core.Log.Warning($"{id} must be a hex colour like {fallback}, got {val.Value}. Using {fallback}."); TryHex(fallback, out color); return color; } private static bool TryHex(string value, out Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) color = default(Color); string text = value.Trim().TrimStart('#'); if (text.Length != 6 || !int.TryParse(text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result)) { return false; } color = new Color((float)((result >> 16) & 0xFF) / 255f, (float)((result >> 8) & 0xFF) / 255f, (float)(result & 0xFF) / 255f); return true; } private static int Int(string id, int fallback, int min, int max, string description) { MelonPreferences_Entry<int> val = _category.CreateEntry<int>(id, fallback, (string)null, description, false, false, (ValueValidator)null, (string)null); if (val.Value >= min && val.Value <= max) { return val.Value; } Core.Log.Warning($"{id} must be between {min} and {max}, got {val.Value}. Using {fallback}."); return fallback; } } internal static class Stats { private sealed class Record { public int Nights { get; set; } public int BloodMoons { get; set; } public int Kills { get; set; } public int BestNight { get; set; } } private static Record _record = new Record(); private static int _tonight; private static bool _night; private static bool _died; private static string FilePath => Path.Combine(MelonEnvironment.UserDataDirectory, "TMLInfection", "stats.json"); public static void Load() { try { if (File.Exists(FilePath)) { _record = Json.Read<Record>(File.ReadAllText(FilePath)) ?? new Record(); } } catch (IOException ex) { Core.Log.Warning("Could not read survival stats: " + ex.Message + ". Starting fresh."); } } public static void NightStart() { _night = true; _died = false; _tonight = 0; } public static void Kill() { if (_night) { _tonight++; _record.Kills++; } } public static void Died() { _died = true; } public static void Dawn(bool bloodMoon, float delay) { if (!_night) { return; } _night = false; _record.BestNight = Math.Max(_record.BestNight, _tonight); if (!_died) { _record.Nights++; if (bloodMoon) { _record.BloodMoons++; } } Save(); string text = (_died ? Strings.Fallen(_tonight, _record.Kills) : Strings.Survived(_tonight, _record.Nights, _record.Kills, _record.BestNight)); Hud.AnnounceAfter(delay, text, danger: false); } public static void Abandon() { if (_night) { _night = false; Save(); } } private static void Save() { try { Directory.CreateDirectory(Path.GetDirectoryName(FilePath)); File.WriteAllText(FilePath, Json.Write(_record)); } catch (Exception ex) when (((ex is IOException || ex is UnauthorizedAccessException) ? 1 : 0) != 0) { Core.Log.Warning("Could not save survival stats: " + ex.Message); } } } internal static class Strings { public static string Rise => Lang.T("hud.rise"); public static string BloodMoon => Lang.T("hud.bloodmoon"); public static string Dawn => Lang.T("hud.dawn"); public static string Infected => Lang.T("hud.infected"); public static string PatientZero => Lang.T("hud.patientzero"); public static string PatientZeroDown => Lang.T("hud.patientzerodown"); public static string Disguised => Lang.T("hud.disguised"); public static string MenuTagline => Lang.T("menu.tagline"); public static string MenuHalloween => Lang.T("menu.halloween"); public static string Infection(int percent) { return Lang.T("hud.infection", percent); } public static string Survived(int tonight, int nights, int total, int best) { return Lang.T("hud.survived", tonight, nights, total, best); } public static string Fallen(int tonight, int total) { return Lang.T("hud.fallen", tonight, total); } } internal enum ZombieKind { Shambler, Nightwalker, Hunter, Brute, PatientZero } public abstract class Zombie : NPC { private static readonly Color[] Skins = (Color[])(object)new Color[5] { new Color(0.5f, 0.56f, 0.46f), new Color(0.44f, 0.47f, 0.43f), new Color(0.58f, 0.58f, 0.52f), new Color(0.4f, 0.45f, 0.4f), new Color(0.52f, 0.54f, 0.38f) }; private static readonly Color[] Tops = (Color[])(object)new Color[5] { new Color(0.28f, 0.1f, 0.08f), new Color(0.3f, 0.28f, 0.24f), new Color(0.14f, 0.17f, 0.2f), new Color(0.26f, 0.22f, 0.15f), new Color(0.2f, 0.22f, 0.18f) }; private static readonly string[] TopLayers = new string[5] { "Avatar/Layers/Top/Buttonup", "Avatar/Layers/Top/FlannelButtonup", "Avatar/Layers/Top/Tucked T", "Avatar/Layers/Top/RolledButtonup", "Avatar/Layers/Top/Overalls" }; private static readonly Color[] Bottoms = (Color[])(object)new Color[3] { new Color(0.16f, 0.16f, 0.17f), new Color(0.22f, 0.19f, 0.14f), new Color(0.12f, 0.14f, 0.18f) }; private static readonly string[] Hair = new string[6] { "Avatar/Hair/Balding/Balding", "Avatar/Hair/Receding/Receding", "Avatar/Hair/MessyBob/MessyBob", "Avatar/Hair/LongCurly/LongCurly", "Avatar/Hair/BuzzCut/BuzzCut", "" }; private static readonly string[] Mouths = new string[4] { "Avatar/Layers/Face/Face_Agape", "Avatar/Layers/Face/Face_Agitated", "Avatar/Layers/Face/Face_Agape", "Avatar/Layers/Face/Face_Scared" }; private static readonly string[] Wounds = new string[4] { "Avatar/Layers/Tattoos/Face/Face_Teardrop", "Avatar/Layers/Tattoos/Face/Face_ForeheadCross", "Avatar/Layers/Tattoos/Face/Face_Tribal", "" }; private static readonly Color Blood = new Color(0.45f, 0.02f, 0.02f); private static readonly Color Bruise = new Color(0.22f, 0.14f, 0.2f); private static readonly Color DeadHair = new Color(0.32f, 0.31f, 0.28f); private static readonly Color Milky = new Color(0.92f, 0.9f, 0.72f); private static readonly Color Bloodshot = new Color(0.95f, 0.5f, 0.45f); private static readonly Color Hazmat = new Color(0.62f, 0.58f, 0.2f); private static readonly Color PatientZeroSkin = new Color(0.3f, 0.34f, 0.3f); private static readonly Color Glowing = new Color(1f, 0.05f, 0.05f); internal const string IdPrefix = "tmlinfection_zombie_"; internal const int PatientZeroNumber = 25; private static readonly ZombieKind[] Mix = new ZombieKind[6] { ZombieKind.Shambler, ZombieKind.Nightwalker, ZombieKind.Shambler, ZombieKind.Hunter, ZombieKind.Shambler, ZombieKind.Brute }; protected abstract int Number { get; } internal ZombieKind Kind { get { if (Number != 25) { return Mix[(Number - 1) % Mix.Length]; } return ZombieKind.PatientZero; } } internal Settings.KindStats Stats => Settings.Kinds[Kind]; public override bool IsPhysical => true; protected override void ConfigurePrefab(NPCPrefabBuilder builder) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) bool patientZero = Kind == ZombieKind.PatientZero; builder.WithIdentity($"{"tmlinfection_zombie_"}{Number}", patientZero ? "Patient" : "Zombie", patientZero ? "Zero" : $"#{Number}").WithSpawnPosition(Horde.Crypt).WithAppearanceDefaults((Action<AvatarDefaultsBuilder>)delegate(AvatarDefaultsBuilder avatar) { //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) avatar.Gender = ((Number % 3 == 0) ? 1f : 0f); AvatarDefaultsBuilder val = avatar; val.Height = Kind switch { ZombieKind.PatientZero => 1.25f, ZombieKind.Brute => 1.1f, ZombieKind.Nightwalker => 1.02f, _ => 0.94f + (float)(Number % 4) * 0.03f, }; val = avatar; float weight; switch (Kind) { case ZombieKind.Brute: case ZombieKind.PatientZero: weight = 1f; break; case ZombieKind.Nightwalker: weight = 0.05f; break; default: weight = 0.25f + (float)(Number % 5) * 0.12f; break; } val.Weight = weight; avatar.SkinColor = Color32.op_Implicit(patientZero ? PatientZeroSkin : Skins[Number % Skins.Length]); avatar.LeftEyeLidColor = Bruise; avatar.RightEyeLidColor = Bruise; avatar.EyeBallTint = (patientZero ? Glowing : ((Number % 2 == 0) ? Bloodshot : Milky)); avatar.PupilDilation = (patientZero ? 0f : 0.05f); avatar.LeftEye = (0.95f, 0.8f); avatar.RightEye = ((Number % 3 == 0) ? (0.35f, 0.6f) : (0.9f, 0.75f)); avatar.EyebrowThickness = 0.6f; avatar.HairPath = Hair[Number % Hair.Length]; avatar.HairColor = DeadHair; avatar.WithFaceLayer(Mouths[Number % Mouths.Length], Color.black); avatar.WithFaceLayer("Avatar/Layers/Face/TiredEyes", Bruise); avatar.WithFaceLayer("Avatar/Layers/Face/EyeShadow", Blood); avatar.WithFaceLayer("Avatar/Layers/Face/OldPersonWrinkles", Bruise); string text = Wounds[Number % Wounds.Length]; if (text.Length > 0) { avatar.WithFaceLayer(text, Blood); } if (Number % 8 == 0 && !patientZero) { avatar.WithBodyLayer("Avatar/Layers/Top/HazmatSuit", Hazmat); avatar.WithAccessoryLayer("Avatar/Accessories/Waist/HazmatSuit/HazmatSuit", Hazmat); avatar.WithAccessoryLayer("Avatar/Accessories/Head/Respirator/Respirator", Color.black); } else { avatar.WithBodyLayer(TopLayers[Number % TopLayers.Length], Tops[Number % Tops.Length]); avatar.WithBodyLayer("Avatar/Layers/Bottom/CargoPants", Bottoms[Number % Bottoms.Length]); } }); } protected override void OnCreated() { ((NPC)this).OnCreated(); ((NPC)this).Appearance.Build(); Toughen(); } internal void Toughen() { ((NPC)this).MaxHealth = Stats.Health; ((NPC)this).Heal(Stats.Health); } } public sealed class ZombieOne : Zombie { protected override int Number => 1; } public sealed class ZombieTwo : Zombie { protected override int Number => 2; } public sealed class ZombieThree : Zombie { protected override int Number => 3; } public sealed class ZombieFour : Zombie { protected override int Number => 4; } public sealed class ZombieFive : Zombie { protected override int Number => 5; } public sealed class ZombieSix : Zombie { protected override int Number => 6; } public sealed class ZombieSeven : Zombie { protected override int Number => 7; } public sealed class ZombieEight : Zombie { protected override int Number => 8; } public sealed class ZombieNine : Zombie { protected override int Number => 9; } public sealed class ZombieTen : Zombie { protected override int Number => 10; } public sealed class ZombieEleven : Zombie { protected override int Number => 11; } public sealed class ZombieTwelve : Zombie { protected override int Number => 12; } public sealed class ZombieThirteen : Zombie { protected override int Number => 13; } public sealed class ZombieFourteen : Zombie { protected override int Number => 14; } public sealed class ZombieFifteen : Zombie { protected override int Number => 15; } public sealed class ZombieSixteen : Zombie { protected override int Number => 16; } public sealed class ZombieSeventeen : Zombie { protected override int Number => 17; } public sealed class ZombieEighteen : Zombie { protected override int Number => 18; } public sealed class ZombieNineteen : Zombie { protected override int Number => 19; } public sealed class ZombieTwenty : Zombie { protected override int Number => 20; } public sealed class ZombieTwentyOne : Zombie { protected override int Number => 21; } public sealed class ZombieTwentyTwo : Zombie { protected override int Number => 22; } public sealed class ZombieTwentyThree : Zombie { protected override int Number => 23; } public sealed class ZombieTwentyFour : Zombie { protected override int Number => 24; } public sealed class PatientZero : Zombie { protected override int Number => 25; } public class ZombieCommand : BaseConsoleCommand { private static readonly Dictionary<string, string> Help = new Dictionary<string, string> { ["status"] = "show the horde and your infection", ["rise"] = "the dead rise now", ["bloodmoon"] = "a Blood Moon rises now", ["dawn"] = "send the dead back now", ["infect <0-100>"] = "set your infection", ["cure"] = "clear your infection", ["on|off"] = "turn nightly outbreaks on or off", ["debug on|off"] = "log horde events" }; public override string CommandWord => "zombies"; public override string CommandDescription => "TML Infection tools. Type 'zombies help' for options."; public override string ExampleUsage => "zombies bloodmoon"; public override void ExecuteCommand(List<string> args) { string text = ((args.Count > 0) ? args[0].ToLowerInvariant() : "status"); string text2 = ((args.Count > 1) ? args[1].ToLowerInvariant() : ""); if (text == "help") { foreach (var (value, value2) in Help) { Core.Log.Msg($"zombies {value,-16} {value2}"); } return; } if (Player.Local == null) { Core.Log.Warning("Load a save first."); return; } switch (text) { case "status": Core.Log.Msg($"Outbreaks {(Settings.Enabled ? "on" : "off")}. Horde {(Horde.Active ? "active" : "resting")}{(Horde.BloodMoon ? " (Blood Moon)" : "")}: {Horde.RisenCount} risen, {Horde.DeadCount} down. Your infection {Infection.Level:0}%."); return; case "rise": Horde.Rise(bloodMoon: false); return; case "bloodmoon": Horde.Rise(bloodMoon: true); return; case "dawn": Horde.Dawn(); return; case "infect": { if (float.TryParse(text2, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { Infection.Set(result); return; } break; } case "cure": Infection.Cure(); return; case "on": case "off": Settings.Enabled = text == "on"; Core.Log.Msg("Outbreaks " + text + "."); Horde.OnHour(); return; case "debug": if ((text2 == "on" || text2 == "off") ? true : false) { Settings.DebugLogs = text2 == "on"; Core.Log.Msg("Debug logs " + text2 + "."); return; } break; } Core.Log.Warning("Unknown option. Type 'zombies help'."); } } }