Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LC DOOM v1.1.2
LethalCompany.Doom.dll
Decompiled 2 years agousing System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using LethalCompany.Doom.Unity; using ManagedDoom; using ManagedDoom.Audio; using ManagedDoom.UserInput; using ManagedDoom.Video; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Samuel Steele")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("2023 Samuel Steele")] [assembly: AssemblyDescription("Play DOOM in the Ship's Terminal")] [assembly: AssemblyFileVersion("1.1.2.0")] [assembly: AssemblyInformationalVersion("1.1.2+2e3fe63fa663bee40733b6ea1c95f8150d066ecf")] [assembly: AssemblyProduct("LethalCompany.Doom")] [assembly: AssemblyTitle("LethalCompany.Doom")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/cryptoc1/lc-doom.git")] [assembly: AssemblyVersion("1.1.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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 System.Runtime.Versioning { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class RequiresPreviewFeaturesAttribute : Attribute { public string? Message { get; } public string? Url { get; set; } public RequiresPreviewFeaturesAttribute() { } public RequiresPreviewFeaturesAttribute(string? message) { Message = message; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CallerArgumentExpressionAttribute : Attribute { public string ParameterName { get; } public CallerArgumentExpressionAttribute(string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CollectionBuilderAttribute : Attribute { public Type BuilderType { get; } public string MethodName { get; } public CollectionBuilderAttribute(Type builderType, string methodName) { BuilderType = builderType; MethodName = methodName; } } [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CompilerFeatureRequiredAttribute : Attribute { public const string RefStructs = "RefStructs"; public const string RequiredMembers = "RequiredMembers"; public string FeatureName { get; } public bool IsOptional { get; set; } public CompilerFeatureRequiredAttribute(string featureName) { FeatureName = featureName; } } [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute { public string[] Arguments { get; } public InterpolatedStringHandlerArgumentAttribute(string argument) { Arguments = new string[1] { argument }; } public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) { Arguments = arguments; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class InterpolatedStringHandlerAttribute : Attribute { } [EditorBrowsable(EditorBrowsableState.Never)] [ExcludeFromCodeCoverage] internal static class IsExternalInit { } [AttributeUsage(AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ModuleInitializerAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class RequiredMemberAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [EditorBrowsable(EditorBrowsableState.Never)] [ExcludeFromCodeCoverage] internal sealed class RequiresLocationAttribute : Attribute { } [AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class SkipLocalsInitAttribute : Attribute { } } namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ExperimentalAttribute : Attribute { public string DiagnosticId { get; } public string? UrlFormat { get; set; } public ExperimentalAttribute(string diagnosticId) { DiagnosticId = diagnosticId; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] internal sealed class MemberNotNullAttribute : Attribute { public string[] Members { get; } public MemberNotNullAttribute(string member) { Members = new string[1] { member }; } public MemberNotNullAttribute(params string[] members) { Members = members; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] internal sealed class MemberNotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public string[] Members { get; } public MemberNotNullWhenAttribute(bool returnValue, string member) { ReturnValue = returnValue; Members = new string[1] { member }; } public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { ReturnValue = returnValue; Members = members; } } [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class SetsRequiredMembersAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class StringSyntaxAttribute : Attribute { public const string CompositeFormat = "CompositeFormat"; public const string DateOnlyFormat = "DateOnlyFormat"; public const string DateTimeFormat = "DateTimeFormat"; public const string EnumFormat = "EnumFormat"; public const string GuidFormat = "GuidFormat"; public const string Json = "Json"; public const string NumericFormat = "NumericFormat"; public const string Regex = "Regex"; public const string TimeOnlyFormat = "TimeOnlyFormat"; public const string TimeSpanFormat = "TimeSpanFormat"; public const string Uri = "Uri"; public const string Xml = "Xml"; public string Syntax { get; } public object?[] Arguments { get; } public StringSyntaxAttribute(string syntax) { Syntax = syntax; Arguments = new object[0]; } public StringSyntaxAttribute(string syntax, params object?[] arguments) { Syntax = syntax; Arguments = arguments; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class UnscopedRefAttribute : Attribute { } } namespace LethalCompany.Doom { [BepInPlugin("cryptoc1.lethalcompany.doom", "LC-DOOM", "1.1.2")] public sealed class DoomPlugin : BaseUnityPlugin { public static DoomPlugin Value { get; private set; } public ManualLogSource Logger => ((BaseUnityPlugin)this).Logger; public void Awake() { Value = this; Harmony.CreateAndPatchAll(typeof(DoomPlugin).Assembly, "cryptoc1.lethalcompany.doom"); } } internal static class GeneratedPluginInfo { public const string Identifier = "cryptoc1.lethalcompany.doom"; public const string Name = "LC-DOOM"; public const string Version = "1.1.2"; } } namespace LethalCompany.Doom.Unity { internal static class DoomAudioSource { public static AudioSource Create(string name) { //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) AudioSource obj = new GameObject("LCDoomAudio_" + name) { hideFlags = (HideFlags)61 }.AddComponent<AudioSource>(); obj.playOnAwake = false; obj.spatialBlend = 0f; return obj; } } internal sealed class UnityDoom : IDisposable { private readonly DoomConfigBinder binder; private readonly Config config; private readonly GameContent content; private readonly Doom? doom; private readonly int fpsScale; private int frameCount; private UnitySound? sound; private UnityUserInput? userInput; private UnityVideo? video; public Texture2D? Texture => video?.Texture; public UnityDoom(DoomConfigBinder binder) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown try { CommandLineArgs val = new CommandLineArgs(new string[2] { "iwad", binder.Wad.Value }); this.binder = binder; config = binder.GetConfig(); content = new GameContent(val); doom = new Doom(val, config, content, (IVideo)(object)(video = new UnityVideo(config, content)), (ISound)(object)(sound = ((!val.nosound.Present && !val.nosfx.Present) ? new UnitySound(config, content) : null)), (IMusic)null, (IUserInput)(object)(userInput = new UnityUserInput(config))); frameCount = -1; fpsScale = (val.timedemo.Present ? 1 : config.video_fpsscale); } catch (Exception source) { Dispose(); ExceptionDispatchInfo.Throw(source); } } public void Dispose() { if (sound != null) { sound.Dispose(); sound = null; } if (video != null) { video.Dispose(); video = null; } if (userInput != null) { userInput.Dispose(); userInput = null; } binder.Save(config); } public unsafe void Input(Mouse mouse, Keyboard keyboard) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) userInput.MoveMouse(Unsafe.Read<Vector2>((void*)((InputControl<Vector2>)(object)((Pointer)mouse).delta).value)); foreach (var (val, button2) in UnityUserInput.MouseButtons(Mouse.current)) { if (val.wasPressedThisFrame) { PressButton(button2); } if (val.wasReleasedThisFrame) { ReleaseButton(button2); } } foreach (var (val2, key2) in UnityUserInput.Keys) { if (((ButtonControl)keyboard[val2]).wasPressedThisFrame) { PressKey(key2); } if (((ButtonControl)keyboard[val2]).wasReleasedThisFrame) { ReleaseKey(key2); } } void PressButton(DoomMouseButton button) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_000f: 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) if ((int)button != -1 && !userInput.PressedButtons.Contains(button)) { userInput.PressedButtons.Add(button); } doom.PostEvent(new DoomEvent((EventType)2, (DoomKey)(-1))); } void PressKey(DoomKey key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_000f: 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) if ((int)key != -1 && !userInput.PressedKeys.Contains(key)) { userInput.PressedKeys.Add(key); } doom.PostEvent(new DoomEvent((EventType)0, key)); } void ReleaseButton(DoomMouseButton button) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown userInput.PressedButtons.Remove(button); doom.PostEvent(new DoomEvent((EventType)2, (DoomKey)(-1))); } void ReleaseKey(DoomKey key) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown userInput.PressedKeys.Remove(key); doom.PostEvent(new DoomEvent((EventType)1, key)); } } public bool Render() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (++frameCount % fpsScale == 0) { if ((int)doom.Update() == 1) { return false; } video.Render(doom, Fixed.FromInt(frameCount % fpsScale + 1) / fpsScale); } return true; } } public sealed class DoomConfigBinder { [CompilerGenerated] private ConfigFile <file>P; [CompilerGenerated] private (int width, int height) <size>P; private static readonly Lazy<string> DefaultWadPath = new Lazy<string>(GetWadPath); private static readonly Lazy<string> DefaultSoundFontPath = new Lazy<string>(GetSoundFontPath); public ConfigEntry<bool> AlwaysRun { get; } public ConfigEntry<string?> Wad { get; } public ConfigEntry<string> KeyBackward { get; } public ConfigEntry<string> KeyForward { get; } public ConfigEntry<string> KeyFire { get; } public ConfigEntry<string> KeyRun { get; } public ConfigEntry<string> KeyStrafe { get; } public ConfigEntry<string> KeyStrafeLeft { get; } public ConfigEntry<string> KeyStrafeRight { get; } public ConfigEntry<string> KeyTurnLeft { get; } public ConfigEntry<string> KeyTurnRight { get; } public ConfigEntry<string> KeyUse { get; } public ConfigEntry<bool> MouseDisableYAxis { get; } public ConfigEntry<int> MouseSensitivity { get; } public ConfigEntry<bool> SfxRandomPitch { get; } public ConfigEntry<int> SfxVolume { get; } public ConfigEntry<bool> VideoDisplayMessage { get; } public ConfigEntry<int> VideoFpsScale { get; } public ConfigEntry<int> VideoGammaCorrection { get; } public ConfigEntry<bool> VideoHighResolution { get; } public ConfigEntry<int> VideoScreenSize { get; } public DoomConfigBinder(ConfigFile file, (int width, int height) size) { <file>P = file; <size>P = size; AlwaysRun = <file>P.Bind<bool>("General", "Always Run", true, (ConfigDescription)null); Wad = <file>P.Bind<string>("General", "WAD", DefaultWadPath.Value, "An absolute path to the WAD file to be loaded."); KeyBackward = <file>P.Bind<string>("Keybinds", "Backward", "s,down", (ConfigDescription)null); KeyForward = <file>P.Bind<string>("Keybinds", "Forward", "w,up", (ConfigDescription)null); KeyFire = <file>P.Bind<string>("Keybinds", "Fire", "mouse1,f,lcontrol,rcontrol", (ConfigDescription)null); KeyRun = <file>P.Bind<string>("Keybinds", "Run", "lshift,rshift", (ConfigDescription)null); KeyStrafe = <file>P.Bind<string>("Keybinds", "Strafe", "lalt,ralt", (ConfigDescription)null); KeyStrafeLeft = <file>P.Bind<string>("Keybinds", "Strafe Left", "a", (ConfigDescription)null); KeyStrafeRight = <file>P.Bind<string>("Keybinds", "Strafe Right", "d", (ConfigDescription)null); KeyTurnLeft = <file>P.Bind<string>("Keybinds", "Turn Left", "left", (ConfigDescription)null); KeyTurnRight = <file>P.Bind<string>("Keybinds", "Turn Right", "right", (ConfigDescription)null); KeyUse = <file>P.Bind<string>("Keybinds", "Use", "space,mouse2", (ConfigDescription)null); MouseDisableYAxis = <file>P.Bind<bool>("Mouse", "Disable Y-Axis", false, (ConfigDescription)null); MouseSensitivity = <file>P.Bind<int>("Mouse", "Sensitivity", 8, (ConfigDescription)null); SfxRandomPitch = <file>P.Bind<bool>("Sfx", "Random Pitch", true, (ConfigDescription)null); SfxVolume = <file>P.Bind<int>("Sfx", "Volume", 8, "The volume of sound effects."); VideoDisplayMessage = <file>P.Bind<bool>("Video", "Display Message", true, (ConfigDescription)null); VideoFpsScale = <file>P.Bind<int>("Video", "FPS Scale", 2, (ConfigDescription)null); VideoGammaCorrection = <file>P.Bind<int>("Video", "Gamma Correction", 2, (ConfigDescription)null); VideoHighResolution = <file>P.Bind<bool>("Video", "High Resolution", true, (ConfigDescription)null); VideoScreenSize = <file>P.Bind<int>("Video", "Screen Size", 7, (ConfigDescription)null); base..ctor(); } public Config GetConfig() { //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_0016: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Expected O, but got Unknown return new Config { audio_randompitch = SfxRandomPitch.Value, audio_soundvolume = SfxVolume.Value, game_alwaysrun = AlwaysRun.Value, key_backward = KeyBinding.Parse(KeyBackward.Value), key_fire = KeyBinding.Parse(KeyFire.Value), key_forward = KeyBinding.Parse(KeyForward.Value), key_run = KeyBinding.Parse(KeyRun.Value), key_strafe = KeyBinding.Parse(KeyStrafe.Value), key_strafeleft = KeyBinding.Parse(KeyStrafeLeft.Value), key_straferight = KeyBinding.Parse(KeyStrafeRight.Value), key_turnleft = KeyBinding.Parse(KeyTurnLeft.Value), key_turnright = KeyBinding.Parse(KeyTurnRight.Value), key_use = KeyBinding.Parse(KeyUse.Value), mouse_disableyaxis = MouseDisableYAxis.Value, mouse_sensitivity = MouseSensitivity.Value, video_displaymessage = VideoDisplayMessage.Value, video_fpsscale = VideoFpsScale.Value, video_fullscreen = false, video_gamescreensize = VideoScreenSize.Value, video_gammacorrection = VideoGammaCorrection.Value, video_highresolution = VideoHighResolution.Value, video_screenheight = <size>P.height, video_screenwidth = <size>P.width }; } private static string GetSoundFontPath() { return Path.Combine(Path.GetDirectoryName(typeof(DoomPlugin).Assembly.Location), "RLNDGM.SF2"); } private static string GetWadPath() { return Path.Combine(Path.GetDirectoryName(typeof(DoomPlugin).Assembly.Location), "DOOM1.WAD"); } public void Save(Config config) { AlwaysRun.Value = config.game_alwaysrun; KeyBackward.Value = ((object)config.key_backward).ToString(); KeyForward.Value = ((object)config.key_forward).ToString(); KeyFire.Value = ((object)config.key_fire).ToString(); KeyRun.Value = ((object)config.key_run).ToString(); KeyStrafe.Value = ((object)config.key_strafe).ToString(); KeyStrafeLeft.Value = ((object)config.key_strafeleft).ToString(); KeyStrafeRight.Value = ((object)config.key_straferight).ToString(); KeyTurnLeft.Value = ((object)config.key_turnleft).ToString(); KeyTurnRight.Value = ((object)config.key_turnright).ToString(); KeyUse.Value = ((object)config.key_use).ToString(); MouseDisableYAxis.Value = config.mouse_disableyaxis; MouseSensitivity.Value = config.mouse_sensitivity; SfxRandomPitch.Value = config.audio_randompitch; SfxVolume.Value = config.audio_soundvolume; VideoDisplayMessage.Value = config.video_displaymessage; VideoFpsScale.Value = config.video_fpsscale; VideoGammaCorrection.Value = config.video_gammacorrection; VideoHighResolution.Value = config.video_highresolution; VideoScreenSize.Value = config.video_gamescreensize; <file>P.Save(); } } internal sealed class UnitySound : ISound, IDisposable { private sealed class ChannelInfo { public Sfx Reserved; public Sfx Playing; public float Priority; public Mobj? Source; public SfxType Type; public int Volume; public Fixed LastX; public Fixed LastY; public void Clear() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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) //IL_002f: 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) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) Reserved = (Sfx)0; Playing = (Sfx)0; Priority = 0f; Source = null; Type = (SfxType)0; Volume = 0; LastX = Fixed.Zero; LastY = Fixed.Zero; } } private static readonly int ChannelCount = 8; private static readonly float FastDecay = (float)Math.Pow(0.5, 1.0 / 7.0); private static readonly float SlowDecay = (float)Math.Pow(0.5, 1.0 / 35.0); private static readonly float ClipDist = 1200f; private static readonly float CloseDist = 160f; private static readonly float Attenuator = ClipDist - CloseDist; private readonly float[] amplitudes; private readonly Config config; private readonly ChannelInfo[] infos; private readonly DoomRandom? random; private AudioClip[] buffers; private AudioSource[] channels; private AudioSource uiChannel; private Sfx uiReserved; private DateTime lastUpdate; private Mobj listener; private float masterVolumeDecay; public int MaxVolume => 15; public int Volume { get { return config.audio_soundvolume; } set { config.audio_soundvolume = value; masterVolumeDecay = (float)config.audio_soundvolume / (float)MaxVolume; } } public UnitySound(Config config, GameContent content) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0176: Unknown result type (might be due to invalid IL or missing references) try { this.config = config; config.audio_soundvolume = Math.Clamp(config.audio_soundvolume, 0, MaxVolume); buffers = (AudioClip[])(object)new AudioClip[DoomInfo.SfxNames.Length]; amplitudes = new float[DoomInfo.SfxNames.Length]; if (config.audio_randompitch) { random = new DoomRandom(); } for (int i = 0; i < DoomInfo.SfxNames.Length; i++) { string text = $"DS{DoomInfo.SfxNames[i]}".ToUpperInvariant(); if (content.Wad.GetLumpNumber(text) != -1) { int sampleRate; int sampleCount; float[] samples = GetSamples(content.Wad, text, out sampleRate, out sampleCount); if ((samples?.Length).HasValue) { AudioClip val = AudioClip.Create(text, samples.Length, 1, sampleRate, false); val.SetData(samples, 0); buffers[i] = val; amplitudes[i] = GetAmplitude(samples, sampleRate, samples.Length); } } } channels = CreateChannels(ChannelCount); infos = new ChannelInfo[ChannelCount]; for (int j = 0; j < ChannelCount; j++) { infos[j] = new ChannelInfo(); } lastUpdate = DateTime.MinValue; masterVolumeDecay = (float)config.audio_soundvolume / (float)MaxVolume; uiChannel = DoomAudioSource.Create("UI"); uiReserved = (Sfx)0; } catch { Dispose(); throw; } } private AudioSource[] CreateChannels(int channelCount) { AudioSource[] array = (AudioSource[])(object)new AudioSource[channelCount]; for (int i = 0; i < channelCount; i++) { AudioSource val = DoomAudioSource.Create($"SFX_{i}"); val.spatialBlend = 0f; val.playOnAwake = false; array[i] = val; } return array; } public void Dispose() { if (channels != null) { for (int i = 0; i < channels.Length; i++) { if (channels[i] != null) { channels[i].Stop(); Object.DestroyImmediate((Object)(object)((Component)channels[i]).gameObject, true); channels[i] = null; } } channels = null; } if (buffers != null) { for (int j = 0; j < buffers.Length; j++) { if (buffers[j] != null) { Object.DestroyImmediate((Object)(object)buffers[j], true); buffers[j] = null; } } buffers = null; } if (uiChannel != null) { Object.DestroyImmediate((Object)(object)((Component)uiChannel).gameObject, true); uiChannel = null; } } private static float[]? GetSamples(Wad wad, string name, out int sampleRate, out int sampleCount) { byte[] array = wad.ReadLump(name); if (array.Length < 8) { sampleRate = -1; sampleCount = -1; return null; } sampleRate = BitConverter.ToUInt16(array, 2); sampleCount = BitConverter.ToInt32(array, 4); int num = 8; if (ContainsDmxPadding(array)) { num += 16; sampleCount -= 32; } if (sampleCount > 0) { float[] array2 = new float[sampleCount]; for (int i = num; i < sampleCount; i++) { array2[i] = (float)(int)array[i] / 127f - 1f; } return array2; } return Array.Empty<float>(); } private static bool ContainsDmxPadding(byte[] data) { int num = BitConverter.ToInt32(data, 4); if (num < 32) { return false; } byte b = data[8]; for (int i = 1; i < 16; i++) { if (data[8 + i] != b) { return false; } } byte b2 = data[8 + num - 1]; for (int j = 1; j < 16; j++) { if (data[8 + num - j - 1] != b2) { return false; } } return true; } private static float GetAmplitude(float[] samples, int sampleRate, int sampleCount) { float num = 0f; if (sampleCount > 0) { int num2 = Math.Min(sampleRate / 5, sampleCount); for (int i = 0; i < num2; i++) { float num3 = samples[i] - 0.5f; if (num3 < 0f) { num3 = 0f - num3; } if (num3 > num) { num = num3; } } } return num; } public void SetListener(Mobj listener) { this.listener = listener; } public void Update() { //IL_0041: 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) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0148: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) DateTime now = DateTime.Now; if ((now - lastUpdate).TotalSeconds < 0.01) { return; } for (int i = 0; i < infos.Length; i++) { ChannelInfo channelInfo = infos[i]; AudioSource val = channels[i]; if ((int)channelInfo.Playing != 0) { channelInfo.Priority *= (((int)channelInfo.Type == 0) ? SlowDecay : FastDecay); UpdateAudioSource(val, channelInfo); } if ((int)channelInfo.Reserved != 0) { if ((int)channelInfo.Playing != 0) { val.Stop(); } val.clip = buffers[channelInfo.Reserved]; UpdateAudioSource(val, channelInfo); val.pitch = GetPitch(channelInfo.Type, channelInfo.Reserved); val.PlayOneShot(val.clip); channelInfo.Playing = channelInfo.Reserved; channelInfo.Reserved = (Sfx)0; } } if ((int)uiReserved != 0) { if (uiChannel.isPlaying) { uiChannel.Stop(); } uiChannel.volume = masterVolumeDecay; uiChannel.clip = buffers[uiReserved]; uiChannel.Play(); uiReserved = (Sfx)0; } lastUpdate = now; } public void StartSound(Sfx sfx) { //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_000d: Unknown result type (might be due to invalid IL or missing references) if (buffers[sfx] != null) { uiReserved = sfx; } } public void StartSound(Mobj mobj, Sfx sfx, SfxType type) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) StartSound(mobj, sfx, type, 100); } public void StartSound(Mobj mobj, Sfx sfx, SfxType type, int volume) { //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_0017: Unknown result type (might be due to invalid IL or missing references) //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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) if (buffers[sfx] == null) { return; } Fixed val = mobj.X - listener.X; float num = ((Fixed)(ref val)).ToFloat(); val = mobj.Y - listener.Y; float num2 = ((Fixed)(ref val)).ToFloat(); float dist = MathF.Sqrt(num * num + num2 * num2); float num3 = (((int)type == 0) ? ((float)volume) : (amplitudes[sfx] * GetDistanceDecay(dist) * (float)volume)); for (int i = 0; i < infos.Length; i++) { ChannelInfo channelInfo = infos[i]; if (channelInfo.Source == mobj && channelInfo.Type == type) { channelInfo.Reserved = sfx; channelInfo.Priority = num3; channelInfo.Volume = volume; return; } } for (int j = 0; j < infos.Length; j++) { ChannelInfo channelInfo2 = infos[j]; if ((int)channelInfo2.Reserved == 0 && (int)channelInfo2.Playing == 0) { channelInfo2.Reserved = sfx; channelInfo2.Priority = num3; channelInfo2.Source = mobj; channelInfo2.Type = type; channelInfo2.Volume = volume; return; } } float num4 = float.MaxValue; int num5 = -1; for (int k = 0; k < infos.Length; k++) { ChannelInfo channelInfo3 = infos[k]; if (channelInfo3.Priority < num4) { num4 = channelInfo3.Priority; num5 = k; } } if (num3 >= num4) { ChannelInfo obj = infos[num5]; obj.Reserved = sfx; obj.Priority = num3; obj.Source = mobj; obj.Type = type; obj.Volume = volume; } } public void StopSound(Mobj mobj) { //IL_001d: 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) //IL_002e: 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) for (int i = 0; i < infos.Length; i++) { ChannelInfo channelInfo = infos[i]; if (channelInfo.Source == mobj) { channelInfo.LastX = channelInfo.Source.X; channelInfo.LastY = channelInfo.Source.Y; channelInfo.Source = null; channelInfo.Volume /= 5; } } } public void Reset() { DoomRandom? obj = random; if (obj != null) { obj.Clear(); } for (int i = 0; i < infos.Length; i++) { channels[i].Stop(); infos[i].Clear(); } listener = null; } public void Pause() { } public void Resume() { } private void UpdateAudioSource(AudioSource sound, ChannelInfo info) { //IL_0001: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_003c: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_006f: 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) //IL_0080: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) if ((int)info.Type == 0) { sound.panStereo = 0f; sound.volume = 0.01f * masterVolumeDecay * (float)info.Volume; return; } Fixed val; Fixed val2; if (info.Source == null) { val = info.LastX; val2 = info.LastY; } else { val = info.Source.X; val2 = info.Source.Y; } Fixed val3 = val - listener.X; float num = ((Fixed)(ref val3)).ToFloat(); val3 = val2 - listener.Y; float num2 = ((Fixed)(ref val3)).ToFloat(); if (Math.Abs(num) < 16f && Math.Abs(num2) < 16f) { sound.panStereo = 0f; sound.volume = 0.01f * masterVolumeDecay * (float)info.Volume; return; } float dist = MathF.Sqrt(num * num + num2 * num2); float num3 = MathF.Atan2(num2, num); Angle angle = listener.Angle; float num4 = num3 - (float)((Angle)(ref angle)).ToRadian(); num4 = num4 * 57.29578f / 180f; num4 = Mathf.Clamp(num4, -0.2f, 0.2f); sound.panStereo = num4; sound.volume = 0.01f * masterVolumeDecay * GetDistanceDecay(dist) * (float)info.Volume; } private float GetDistanceDecay(float dist) { if (dist < CloseDist) { return 1f; } return Math.Max((ClipDist - dist) / Attenuator, 0f); } private float GetPitch(SfxType type, Sfx sfx) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 if (random != null) { if ((int)type == 2) { return 1f + 0.075f * (float)(random.Next() - 128) / 128f; } if ((int)sfx == 32 || (int)sfx == 87 || (int)sfx == 108) { return 1f; } return 1f + 0.025f * (float)(random.Next() - 128) / 128f; } return 1f; } } internal sealed class UnityUserInput : IUserInput, IDisposable { private readonly Config config; private readonly bool[] weaponKeys; private Vector2 mouseDelta = new Vector2(0f, 0f); private Vector2 mousePosition = new Vector2(0f, 0f); private Vector2 mousePreviousPosition = new Vector2(0f, 0f); private int turnHeld; public static readonly IEnumerable<(Key InputKey, DoomKey Key)> Keys = new <>z__ReadOnlyArray<(Key, DoomKey)>(new(Key, DoomKey)[95] { ((Key)1, (DoomKey)57), ((Key)7, (DoomKey)49), ((Key)13, (DoomKey)68), ((Key)8, (DoomKey)50), ((Key)9, (DoomKey)52), ((Key)50, (DoomKey)26), ((Key)41, (DoomKey)27), ((Key)42, (DoomKey)28), ((Key)43, (DoomKey)29), ((Key)44, (DoomKey)30), ((Key)45, (DoomKey)31), ((Key)46, (DoomKey)32), ((Key)47, (DoomKey)33), ((Key)48, (DoomKey)34), ((Key)49, (DoomKey)35), ((Key)6, (DoomKey)48), ((Key)83, (DoomKey)55), ((Key)15, (DoomKey)0), ((Key)16, (DoomKey)1), ((Key)17, (DoomKey)2), ((Key)18, (DoomKey)3), ((Key)19, (DoomKey)4), ((Key)20, (DoomKey)5), ((Key)21, (DoomKey)6), ((Key)22, (DoomKey)7), ((Key)23, (DoomKey)8), ((Key)24, (DoomKey)9), ((Key)25, (DoomKey)10), ((Key)26, (DoomKey)11), ((Key)27, (DoomKey)12), ((Key)28, (DoomKey)13), ((Key)29, (DoomKey)14), ((Key)30, (DoomKey)15), ((Key)31, (DoomKey)16), ((Key)32, (DoomKey)17), ((Key)33, (DoomKey)18), ((Key)34, (DoomKey)19), ((Key)35, (DoomKey)20), ((Key)36, (DoomKey)21), ((Key)37, (DoomKey)22), ((Key)38, (DoomKey)23), ((Key)39, (DoomKey)24), ((Key)40, (DoomKey)25), ((Key)11, (DoomKey)46), ((Key)10, (DoomKey)53), ((Key)12, (DoomKey)47), ((Key)60, (DoomKey)36), ((Key)2, (DoomKey)58), ((Key)3, (DoomKey)60), ((Key)65, (DoomKey)59), ((Key)70, (DoomKey)65), ((Key)71, (DoomKey)66), ((Key)62, (DoomKey)72), ((Key)61, (DoomKey)71), ((Key)64, (DoomKey)74), ((Key)63, (DoomKey)73), ((Key)67, (DoomKey)61), ((Key)66, (DoomKey)62), ((Key)68, (DoomKey)64), ((Key)69, (DoomKey)63), ((Key)76, (DoomKey)100), ((Key)94, (DoomKey)85), ((Key)95, (DoomKey)86), ((Key)96, (DoomKey)87), ((Key)97, (DoomKey)88), ((Key)98, (DoomKey)89), ((Key)99, (DoomKey)90), ((Key)100, (DoomKey)91), ((Key)101, (DoomKey)92), ((Key)102, (DoomKey)93), ((Key)103, (DoomKey)94), ((Key)104, (DoomKey)95), ((Key)105, (DoomKey)96), ((Key)84, (DoomKey)75), ((Key)85, (DoomKey)76), ((Key)86, (DoomKey)77), ((Key)87, (DoomKey)78), ((Key)88, (DoomKey)79), ((Key)89, (DoomKey)80), ((Key)90, (DoomKey)81), ((Key)91, (DoomKey)82), ((Key)92, (DoomKey)83), ((Key)93, (DoomKey)84), ((Key)78, (DoomKey)70), ((Key)79, (DoomKey)69), ((Key)81, (DoomKey)68), ((Key)80, (DoomKey)67), ((Key)77, (DoomKey)58), ((Key)51, (DoomKey)38), ((Key)55, (DoomKey)37), ((Key)53, (DoomKey)39), ((Key)52, (DoomKey)42), ((Key)56, (DoomKey)41), ((Key)54, (DoomKey)43), ((Key)59, (DoomKey)45) }); public int MaxMouseSensitivity => 15; public int MouseSensitivity { get { return config.mouse_sensitivity; } set { config.mouse_sensitivity = value; } } public List<DoomMouseButton> PressedButtons { get; } = new List<DoomMouseButton>(); public List<DoomKey> PressedKeys { get; } = new List<DoomKey>(); public UnityUserInput(Config config) { //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_0020: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) try { this.config = config; weaponKeys = new bool[7]; } catch { Dispose(); throw; } } public void BuildTicCmd(TicCmd cmd) { bool num = IsPressed(config.key_forward); bool flag = IsPressed(config.key_backward); bool flag2 = IsPressed(config.key_strafeleft); bool flag3 = IsPressed(config.key_straferight); bool flag4 = IsPressed(config.key_turnleft); bool flag5 = IsPressed(config.key_turnright); bool flag6 = IsPressed(config.key_fire); bool flag7 = IsPressed(config.key_use); bool flag8 = IsPressed(config.key_run); bool num2 = IsPressed(config.key_strafe); weaponKeys[0] = PressedKeys.Contains((DoomKey)27); weaponKeys[1] = PressedKeys.Contains((DoomKey)28); weaponKeys[2] = PressedKeys.Contains((DoomKey)29); weaponKeys[3] = PressedKeys.Contains((DoomKey)30); weaponKeys[4] = PressedKeys.Contains((DoomKey)31); weaponKeys[5] = PressedKeys.Contains((DoomKey)32); weaponKeys[6] = PressedKeys.Contains((DoomKey)33); cmd.Clear(); bool flag9 = num2; int num3 = (flag8 ? 1 : 0); int num4 = 0; int num5 = 0; if (config.game_alwaysrun) { num3 = 1 - num3; } turnHeld = ((flag4 || flag5) ? (turnHeld + 1) : (turnHeld = 0)); int num6 = ((turnHeld < PlayerBehavior.SlowTurnTics) ? 2 : num3); if (flag9) { if (flag5) { num5 += PlayerBehavior.SideMove[num3]; } if (flag4) { num5 -= PlayerBehavior.SideMove[num3]; } } else { if (flag5) { cmd.AngleTurn -= (short)PlayerBehavior.AngleTurn[num6]; } if (flag4) { cmd.AngleTurn += (short)PlayerBehavior.AngleTurn[num6]; } } if (num) { num4 += PlayerBehavior.ForwardMove[num3]; } if (flag) { num4 -= PlayerBehavior.ForwardMove[num3]; } if (flag2) { num5 -= PlayerBehavior.SideMove[num3]; } if (flag3) { num5 += PlayerBehavior.SideMove[num3]; } if (flag6) { cmd.Buttons |= TicCmdButtons.Attack; } if (flag7) { cmd.Buttons |= TicCmdButtons.Use; } for (int i = 0; i < weaponKeys.Length; i++) { if (weaponKeys[i]) { cmd.Buttons |= TicCmdButtons.Change; cmd.Buttons |= (byte)(i << (int)TicCmdButtons.WeaponShift); break; } } float num7 = 0.5f * (float)config.mouse_sensitivity; int num8 = (int)MathF.Round(num7 * mouseDelta.x); int num9 = (int)MathF.Round(num7 * (0f - mouseDelta.y)); num4 += num9; if (flag9) { num5 += num8 * 2; } else { cmd.AngleTurn -= (short)(num8 * 22); } if (num4 > PlayerBehavior.MaxMove) { num4 = PlayerBehavior.MaxMove; } else if (num4 < -PlayerBehavior.MaxMove) { num4 = -PlayerBehavior.MaxMove; } if (num5 > PlayerBehavior.MaxMove) { num5 = PlayerBehavior.MaxMove; } else if (num5 < -PlayerBehavior.MaxMove) { num5 = -PlayerBehavior.MaxMove; } cmd.ForwardMove += (sbyte)num4; cmd.SideMove += (sbyte)num5; } public void Dispose() { PressedButtons.Clear(); PressedKeys.Clear(); } public void GrabMouse() { } private bool IsPressed(KeyBinding binding) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0057: Unknown result type (might be due to invalid IL or missing references) foreach (DoomKey key in binding.Keys) { if (PressedKeys.Contains(key)) { return true; } } foreach (DoomMouseButton mouseButton in binding.MouseButtons) { if (PressedButtons.Contains(mouseButton)) { return true; } } return false; } public void MoveMouse(Vector2 delta) { //IL_002e: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_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_0050: Unknown result type (might be due to invalid IL or missing references) mousePreviousPosition.x = mousePosition.x; mousePreviousPosition.y = mousePosition.y; mousePosition += delta; mouseDelta = mousePosition - mousePreviousPosition; if (config.mouse_disableyaxis) { mouseDelta.y = 0f; } } public void ReleaseMouse() { } public void Reset() { } public static IEnumerable<(ButtonControl Control, DoomMouseButton Button)> MouseButtons(Mouse mouse) { return new <>z__ReadOnlyArray<(ButtonControl, DoomMouseButton)>(new(ButtonControl, DoomMouseButton)[5] { (mouse.leftButton, (DoomMouseButton)0), (mouse.rightButton, (DoomMouseButton)1), (mouse.middleButton, (DoomMouseButton)2), (mouse.backButton, (DoomMouseButton)3), (mouse.forwardButton, (DoomMouseButton)4) }); } } internal sealed class UnityVideo : IVideo, IDisposable { private byte[] buffer; private readonly Renderer renderer; public Texture2D Texture { get; private set; } public bool DisplayMessage { get { return renderer.DisplayMessage; } set { renderer.DisplayMessage = value; } } public int GammaCorrectionLevel { get { return renderer.GammaCorrectionLevel; } set { renderer.GammaCorrectionLevel = value; } } public int MaxGammaCorrectionLevel => renderer.MaxGammaCorrectionLevel; public int MaxWindowSize => renderer.MaxWindowSize; public int WindowSize { get { return renderer.WindowSize; } set { renderer.WindowSize = value; } } public int WipeBandCount => renderer.WipeBandCount; public int WipeHeight => renderer.WipeHeight; public UnityVideo(Config config, GameContent content) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown try { int num; int num2; if (!config.video_highresolution) { num = 200; num2 = 320; } else { num = 400; num2 = 640; } renderer = new Renderer(config, content); buffer = new byte[4 * num * num2]; Texture = new Texture2D(num, num2, (TextureFormat)4, false, false) { filterMode = (FilterMode)0, name = "LCDoomScreen" }; } catch { Dispose(); throw; } } public void Dispose() { buffer = null; if (Texture != null) { Object.DestroyImmediate((Object)(object)Texture, true); Texture = null; } } public bool HasFocus() { return true; } public void InitializeWipe() { renderer.InitializeWipe(); } public void Render(Doom doom, Fixed frame) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) renderer.Render(doom, buffer, frame); Texture.SetPixelData<byte>(buffer, 0, 0); Texture.Apply(false, false); } } } namespace LethalCompany.Doom.Patches { [HarmonyPatch(typeof(Terminal))] internal static class TerminalPatches { private static readonly Vector2 TerminalImageSize = new Vector2(428f, 500f); private static readonly Vector3 TerminalImageRotation = new Vector3(0f, 0f, 90f); private static readonly Vector2 TerminalImageTranslation = new Vector2(11.65f, -48f); private static Coroutine? cpu; private static UnityDoom? doom; private static Vector2? terminalImageSize; [MemberNotNullWhen(true, new string[] { "cpu", "doom", "terminalImageSize" })] private static bool isRunning { [MemberNotNullWhen(true, new string[] { "cpu", "doom", "terminalImageSize" })] get { if (cpu != null && doom != null) { return terminalImageSize.HasValue; } return false; } } private static void DestroyDoom(Terminal terminal) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_006e: Unknown result type (might be due to invalid IL or missing references) if (isRunning) { ((MonoBehaviour)terminal).StopCoroutine(cpu); cpu = null; doom.Dispose(); doom = null; RectTransform rectTransform = ((Graphic)terminal.terminalImage).rectTransform; rectTransform.anchoredPosition -= TerminalImageTranslation; ((Transform)((Graphic)terminal.terminalImage).rectTransform).Rotate(TerminalImageRotation); ((Graphic)terminal.terminalImage).rectTransform.sizeDelta = terminalImageSize.Value; terminalImageSize = null; ((Behaviour)terminal.screenText).enabled = true; TMP_InputField screenText = terminal.screenText; screenText.text += "EXIT (0)\n\n"; ((Behaviour)terminal.terminalImageMask).enabled = true; ((Behaviour)terminal.topRightText).enabled = true; GC.Collect(); DoomPlugin.Value.Logger.LogInfo((object)"TerminalPatches: destroyed doom instance"); } } [HarmonyPatch("Awake")] [HarmonyPostfix] public static void OnPostAwake(Terminal __instance) { TerminalNodesList terminalNodes = __instance.terminalNodes; TerminalKeyword[] allKeywords = __instance.terminalNodes.allKeywords; int num = 0; TerminalKeyword[] array = (TerminalKeyword[])(object)new TerminalKeyword[1 + allKeywords.Length]; TerminalKeyword[] array2 = allKeywords; foreach (TerminalKeyword val in array2) { array[num] = val; num++; } array[num] = DoomKeyword(); num++; terminalNodes.allKeywords = array; TerminalKeyword val2 = __instance.terminalNodes.allKeywords.First((TerminalKeyword keyword) => keyword.word == "other"); val2.specialKeywordResult.displayText = val2.specialKeywordResult.displayText.TrimEnd() + "\n\n>DOOM\nBoss makes a dollar, I make a dime. That's why I play DOOM on company time.\n\n"; static TerminalKeyword DoomKeyword() { TerminalNode val3 = ScriptableObject.CreateInstance<TerminalNode>(); val3.clearPreviousText = true; val3.displayText = "LC-DOOM v1.1.2\nhttps://github.com/cryptoc1/lc-doom\n \nCREDITS:\n• id Software\n https://www.idsoftware.com\n\n• " + ApplicationInfo.Title + "\n https://github.com/sinshu/managed-doom\n\n• DoomInUnityInspector\n https://github.com/xabblll/DoomInUnityInspector\n\nLOADING .... "; val3.persistentImage = true; val3.terminalEvent = "doom"; TerminalKeyword obj = ScriptableObject.CreateInstance<TerminalKeyword>(); obj.isVerb = false; obj.specialKeywordResult = val3; obj.word = "doom"; return obj; } } [HarmonyPatch("QuitTerminal")] [HarmonyPostfix] public static void OnPostQuitTerminal(Terminal __instance) { DestroyDoom(__instance); } [HarmonyPatch("LoadTerminalImage")] [HarmonyPostfix] public static void OnPostLoadTerminalImage(Terminal __instance, TerminalNode node) { if (node.terminalEvent == "doom" && doom != null) { TMP_InputField screenText = __instance.screenText; screenText.text += "LOADED!\n\n"; __instance.terminalImage.texture = (Texture)(object)doom.Texture; if (StartOfRound.Instance.inShipPhase) { __instance.displayingPersistentImage = (Texture)(object)doom.Texture; } cpu = ((MonoBehaviour)__instance).StartCoroutine(RenderDoom(__instance)); ((Behaviour)__instance.terminalImage).enabled = true; ((Behaviour)__instance.topRightText).enabled = false; ((Behaviour)__instance.screenText).enabled = false; } } [HarmonyPatch("LoadNewNode")] [HarmonyPrefix] public static void OnPreLoadNewNode(Terminal __instance, TerminalNode node) { //IL_002a: 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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0079: 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) if (node.terminalEvent == "doom" && doom == null) { terminalImageSize = ((Graphic)__instance.terminalImage).rectTransform.sizeDelta; ((Graphic)__instance.terminalImage).rectTransform.sizeDelta = TerminalImageSize; ((Transform)((Graphic)__instance.terminalImage).rectTransform).Rotate(-TerminalImageRotation); RectTransform rectTransform = ((Graphic)__instance.terminalImage).rectTransform; rectTransform.anchoredPosition += TerminalImageTranslation; ((Behaviour)__instance.terminalImageMask).enabled = false; doom = new UnityDoom(new DoomConfigBinder(((BaseUnityPlugin)DoomPlugin.Value).Config, (Mathf.RoundToInt(TerminalImageSize.x), Mathf.RoundToInt(TerminalImageSize.y)))); } } [HarmonyPatch("PressESC")] [HarmonyPrefix] public static bool OnPrePressESC() { return !isRunning; } [HarmonyPatch("Update")] [HarmonyPrefix] public static void OnPreUpdate() { if (isRunning) { doom.Input(Mouse.current, Keyboard.current); } } private static IEnumerator RenderDoom(Terminal terminal) { while (doom != null) { if (!doom.Render()) { DestroyDoom(terminal); break; } yield return (object)new WaitForEndOfFrame(); } } } } internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { int IReadOnlyCollection<T>.Count => _items.Length; T IReadOnlyList<T>.this[int index] => _items[index]; int ICollection<T>.Count => _items.Length; bool ICollection<T>.IsReadOnly => true; T IList<T>.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } public <>z__ReadOnlyArray(T[] items) { _items = items; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return ((IEnumerable<T>)_items).GetEnumerator(); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } void ICollection<T>.Clear() { throw new NotSupportedException(); } bool ICollection<T>.Contains(T item) { return ((ICollection<T>)_items).Contains(item); } void ICollection<T>.CopyTo(T[] array, int arrayIndex) { ((ICollection<T>)_items).CopyTo(array, arrayIndex); } bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); } int IList<T>.IndexOf(T item) { return ((IList<T>)_items).IndexOf(item); } void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); } void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); } }
LethalCompany.Doom.ManagedDoom.dll
Decompiled 2 years ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using ManagedDoom.Audio; using ManagedDoom.UserInput; using ManagedDoom.Video; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Samuel Steele")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("2023 Samuel Steele")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+2e3fe63fa663bee40733b6ea1c95f8150d066ecf")] [assembly: AssemblyProduct("LethalCompany.Doom.ManagedDoom")] [assembly: AssemblyTitle("LethalCompany.Doom.ManagedDoom")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/cryptoc1/lc-doom.git")] [assembly: AssemblyVersion("1.0.0.0")] namespace ManagedDoom { public static class ApplicationInfo { public static readonly string Title = "Managed Doom v2.1a"; } public enum Bgm { NONE, E1M1, E1M2, E1M3, E1M4, E1M5, E1M6, E1M7, E1M8, E1M9, E2M1, E2M2, E2M3, E2M4, E2M5, E2M6, E2M7, E2M8, E2M9, E3M1, E3M2, E3M3, E3M4, E3M5, E3M6, E3M7, E3M8, E3M9, INTER, INTRO, BUNNY, VICTOR, INTROA, RUNNIN, STALKS, COUNTD, BETWEE, DOOM, THE_DA, SHAWN, DDTBLU, IN_CIT, DEAD, STLKS2, THEDA2, DOOM2, DDTBL2, RUNNI2, DEAD2, STLKS3, ROMERO, SHAWN2, MESSAG, COUNT2, DDTBL3, AMPIE, THEDA3, ADRIAN, MESSG2, ROMER2, TENSE, SHAWN3, OPENIN, EVIL, ULTIMA, READ_M, DM2TTL, DM2INT } public enum Sfx { NONE, PISTOL, SHOTGN, SGCOCK, DSHTGN, DBOPN, DBCLS, DBLOAD, PLASMA, BFG, SAWUP, SAWIDL, SAWFUL, SAWHIT, RLAUNC, RXPLOD, FIRSHT, FIRXPL, PSTART, PSTOP, DOROPN, DORCLS, STNMOV, SWTCHN, SWTCHX, PLPAIN, DMPAIN, POPAIN, VIPAIN, MNPAIN, PEPAIN, SLOP, ITEMUP, WPNUP, OOF, TELEPT, POSIT1, POSIT2, POSIT3, BGSIT1, BGSIT2, SGTSIT, CACSIT, BRSSIT, CYBSIT, SPISIT, BSPSIT, KNTSIT, VILSIT, MANSIT, PESIT, SKLATK, SGTATK, SKEPCH, VILATK, CLAW, SKESWG, PLDETH, PDIEHI, PODTH1, PODTH2, PODTH3, BGDTH1, BGDTH2, SGTDTH, CACDTH, SKLDTH, BRSDTH, CYBDTH, SPIDTH, BSPDTH, VILDTH, KNTDTH, PEDTH, SKEDTH, POSACT, BGACT, DMACT, BSPACT, BSPWLK, VILACT, NOWAY, BAREXP, PUNCH, HOOF, METAL, CHGUN, TINK, BDOPN, BDCLS, ITMBK, FLAME, FLAMST, GETPOW, BOSPIT, BOSCUB, BOSSIT, BOSPN, BOSDTH, MANATK, MANDTH, SSSIT, SSDTH, KEENPN, KEENDT, SKEACT, SKESIT, SKEATK, RADIO } public enum SfxType { Diffuse, Weapon, Voice, Footstep, Misc } public sealed class CommandLineArgs { public class Arg { private bool present; public bool Present => present; public Arg() { present = false; } public Arg(bool present) { this.present = present; } } public class Arg<T> { private bool present; private T value; public bool Present => present; public T Value => value; public Arg() { present = false; value = default(T); } public Arg(T value) { present = true; this.value = value; } } public readonly Arg<string> iwad; public readonly Arg<string[]> file; public readonly Arg<string[]> deh; public readonly Arg<Tuple<int, int>> warp; public readonly Arg<int> episode; public readonly Arg<int> skill; public readonly Arg deathmatch; public readonly Arg altdeath; public readonly Arg fast; public readonly Arg respawn; public readonly Arg nomonsters; public readonly Arg solonet; public readonly Arg<string> playdemo; public readonly Arg<string> timedemo; public readonly Arg<int> loadgame; public readonly Arg nomouse; public readonly Arg nosound; public readonly Arg nosfx; public readonly Arg nomusic; public readonly Arg nodeh; public CommandLineArgs(string[] args) { iwad = GetString(args, "-iwad"); file = Check_file(args); deh = Check_deh(args); warp = Check_warp(args); episode = GetInt(args, "-episode"); skill = GetInt(args, "-skill"); deathmatch = new Arg(args.Contains("-deathmatch")); altdeath = new Arg(args.Contains("-altdeath")); fast = new Arg(args.Contains("-fast")); respawn = new Arg(args.Contains("-respawn")); nomonsters = new Arg(args.Contains("-nomonsters")); solonet = new Arg(args.Contains("-solo-net")); playdemo = GetString(args, "-playdemo"); timedemo = GetString(args, "-timedemo"); loadgame = GetInt(args, "-loadgame"); nomouse = new Arg(args.Contains("-nomouse")); nosound = new Arg(args.Contains("-nosound")); nosfx = new Arg(args.Contains("-nosfx")); nomusic = new Arg(args.Contains("-nomusic")); nodeh = new Arg(args.Contains("-nodeh")); if (args.Length == 0 || !args.All((string arg) => arg.FirstOrDefault() != '-')) { return; } string text = null; List<string> list = new List<string>(); List<string> list2 = new List<string>(); foreach (string text2 in args) { string text3 = Path.GetExtension(text2).ToLower(); if (text3 == ".wad") { if (ConfigUtilities.IsIwad(text2)) { text = text2; } else { list.Add(text2); } } else if (text3 == ".deh") { list2.Add(text2); } } if (text != null) { iwad = new Arg<string>(text); } if (list.Count > 0) { file = new Arg<string[]>(list.ToArray()); } if (list2.Count > 0) { deh = new Arg<string[]>(list2.ToArray()); } } private static Arg<string[]> Check_file(string[] args) { string[] values = GetValues(args, "-file"); if (values.Length >= 1) { return new Arg<string[]>(values); } return new Arg<string[]>(); } private static Arg<string[]> Check_deh(string[] args) { string[] values = GetValues(args, "-deh"); if (values.Length >= 1) { return new Arg<string[]>(values); } return new Arg<string[]>(); } private static Arg<Tuple<int, int>> Check_warp(string[] args) { string[] values = GetValues(args, "-warp"); int result2; int result3; if (values.Length == 1) { if (int.TryParse(values[0], out var result)) { return new Arg<Tuple<int, int>>(Tuple.Create(1, result)); } } else if (values.Length == 2 && int.TryParse(values[0], out result2) && int.TryParse(values[1], out result3)) { return new Arg<Tuple<int, int>>(Tuple.Create(result2, result3)); } return new Arg<Tuple<int, int>>(); } private static Arg<string> GetString(string[] args, string name) { string[] values = GetValues(args, name); if (values.Length == 1) { return new Arg<string>(values[0]); } return new Arg<string>(); } private static Arg<int> GetInt(string[] args, string name) { string[] values = GetValues(args, name); if (values.Length == 1 && int.TryParse(values[0], out var result)) { return new Arg<int>(result); } return new Arg<int>(); } private static string[] GetValues(string[] args, string name) { return args.SkipWhile((string arg) => arg != name).Skip(1).TakeWhile((string arg) => arg[0] != '-') .ToArray(); } } public sealed class Config { public KeyBinding key_forward; public KeyBinding key_backward; public KeyBinding key_strafeleft; public KeyBinding key_straferight; public KeyBinding key_turnleft; public KeyBinding key_turnright; public KeyBinding key_fire; public KeyBinding key_use; public KeyBinding key_run; public KeyBinding key_strafe; public int mouse_sensitivity; public bool mouse_disableyaxis; public bool game_alwaysrun; public int video_screenwidth; public int video_screenheight; public bool video_fullscreen; public bool video_highresolution; public bool video_displaymessage; public int video_gamescreensize; public int video_gammacorrection; public int video_fpsscale; public int audio_soundvolume; public int audio_musicvolume; public bool audio_randompitch; public string audio_soundfont; public bool audio_musiceffect; private bool isRestoredFromFile; public bool IsRestoredFromFile => isRestoredFromFile; public Config() { key_forward = new KeyBinding(new DoomKey[2] { DoomKey.Up, DoomKey.W }); key_backward = new KeyBinding(new DoomKey[2] { DoomKey.Down, DoomKey.S }); key_strafeleft = new KeyBinding(new DoomKey[1]); key_straferight = new KeyBinding(new DoomKey[1] { DoomKey.D }); key_turnleft = new KeyBinding(new DoomKey[1] { DoomKey.Left }); key_turnright = new KeyBinding(new DoomKey[1] { DoomKey.Right }); key_fire = new KeyBinding(new DoomKey[2] { DoomKey.LControl, DoomKey.RControl }, new DoomMouseButton[1]); key_use = new KeyBinding(new DoomKey[1] { DoomKey.Space }, new DoomMouseButton[1] { DoomMouseButton.Mouse2 }); key_run = new KeyBinding(new DoomKey[2] { DoomKey.LShift, DoomKey.RShift }); key_strafe = new KeyBinding(new DoomKey[2] { DoomKey.LAlt, DoomKey.RAlt }); mouse_sensitivity = 8; mouse_disableyaxis = false; game_alwaysrun = true; video_screenwidth = 640; video_screenheight = 400; video_fullscreen = false; video_highresolution = true; video_gamescreensize = 7; video_displaymessage = true; video_gammacorrection = 2; video_fpsscale = 2; audio_soundvolume = 8; audio_musicvolume = 8; audio_randompitch = true; audio_soundfont = "TimGM6mb.sf2"; audio_musiceffect = true; isRestoredFromFile = false; } public Config(string path) : this() { try { Console.Write("Restore settings: "); Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (string item in File.ReadLines(path)) { string[] array = item.Split('=', StringSplitOptions.RemoveEmptyEntries); if (array.Length == 2) { dictionary[array[0].Trim()] = array[1].Trim(); } } key_forward = GetKeyBinding(dictionary, "key_forward", key_forward); key_backward = GetKeyBinding(dictionary, "key_backward", key_backward); key_strafeleft = GetKeyBinding(dictionary, "key_strafeleft", key_strafeleft); key_straferight = GetKeyBinding(dictionary, "key_straferight", key_straferight); key_turnleft = GetKeyBinding(dictionary, "key_turnleft", key_turnleft); key_turnright = GetKeyBinding(dictionary, "key_turnright", key_turnright); key_fire = GetKeyBinding(dictionary, "key_fire", key_fire); key_use = GetKeyBinding(dictionary, "key_use", key_use); key_run = GetKeyBinding(dictionary, "key_run", key_run); key_strafe = GetKeyBinding(dictionary, "key_strafe", key_strafe); mouse_sensitivity = GetInt(dictionary, "mouse_sensitivity", mouse_sensitivity); mouse_disableyaxis = GetBool(dictionary, "mouse_disableyaxis", mouse_disableyaxis); game_alwaysrun = GetBool(dictionary, "game_alwaysrun", game_alwaysrun); video_screenwidth = GetInt(dictionary, "video_screenwidth", video_screenwidth); video_screenheight = GetInt(dictionary, "video_screenheight", video_screenheight); video_fullscreen = GetBool(dictionary, "video_fullscreen", video_fullscreen); video_highresolution = GetBool(dictionary, "video_highresolution", video_highresolution); video_displaymessage = GetBool(dictionary, "video_displaymessage", video_displaymessage); video_gamescreensize = GetInt(dictionary, "video_gamescreensize", video_gamescreensize); video_gammacorrection = GetInt(dictionary, "video_gammacorrection", video_gammacorrection); video_fpsscale = GetInt(dictionary, "video_fpsscale", video_fpsscale); audio_soundvolume = GetInt(dictionary, "audio_soundvolume", audio_soundvolume); audio_musicvolume = GetInt(dictionary, "audio_musicvolume", audio_musicvolume); audio_randompitch = GetBool(dictionary, "audio_randompitch", audio_randompitch); audio_soundfont = GetString(dictionary, "audio_soundfont", audio_soundfont); audio_musiceffect = GetBool(dictionary, "audio_musiceffect", audio_musiceffect); isRestoredFromFile = true; Console.WriteLine("OK"); } catch { Console.WriteLine("Failed"); } } public void Save(string path) { try { using StreamWriter streamWriter = new StreamWriter(path); streamWriter.WriteLine("key_forward = " + key_forward); streamWriter.WriteLine("key_backward = " + key_backward); streamWriter.WriteLine("key_strafeleft = " + key_strafeleft); streamWriter.WriteLine("key_straferight = " + key_straferight); streamWriter.WriteLine("key_turnleft = " + key_turnleft); streamWriter.WriteLine("key_turnright = " + key_turnright); streamWriter.WriteLine("key_fire = " + key_fire); streamWriter.WriteLine("key_use = " + key_use); streamWriter.WriteLine("key_run = " + key_run); streamWriter.WriteLine("key_strafe = " + key_strafe); streamWriter.WriteLine("mouse_sensitivity = " + mouse_sensitivity); streamWriter.WriteLine("mouse_disableyaxis = " + BoolToString(mouse_disableyaxis)); streamWriter.WriteLine("game_alwaysrun = " + BoolToString(game_alwaysrun)); streamWriter.WriteLine("video_screenwidth = " + video_screenwidth); streamWriter.WriteLine("video_screenheight = " + video_screenheight); streamWriter.WriteLine("video_fullscreen = " + BoolToString(video_fullscreen)); streamWriter.WriteLine("video_highresolution = " + BoolToString(video_highresolution)); streamWriter.WriteLine("video_displaymessage = " + BoolToString(video_displaymessage)); streamWriter.WriteLine("video_gamescreensize = " + video_gamescreensize); streamWriter.WriteLine("video_gammacorrection = " + video_gammacorrection); streamWriter.WriteLine("video_fpsscale = " + video_fpsscale); streamWriter.WriteLine("audio_soundvolume = " + audio_soundvolume); streamWriter.WriteLine("audio_musicvolume = " + audio_musicvolume); streamWriter.WriteLine("audio_randompitch = " + BoolToString(audio_randompitch)); streamWriter.WriteLine("audio_soundfont = " + audio_soundfont); streamWriter.WriteLine("audio_musiceffect = " + BoolToString(audio_musiceffect)); } catch { } } private static int GetInt(Dictionary<string, string> dic, string name, int defaultValue) { if (dic.TryGetValue(name, out var value) && int.TryParse(value, out var result)) { return result; } return defaultValue; } private static string GetString(Dictionary<string, string> dic, string name, string defaultValue) { if (dic.TryGetValue(name, out var value)) { return value; } return defaultValue; } private static bool GetBool(Dictionary<string, string> dic, string name, bool defaultValue) { if (dic.TryGetValue(name, out var value)) { if (value == "true") { return true; } if (value == "false") { return false; } } return defaultValue; } private static KeyBinding GetKeyBinding(Dictionary<string, string> dic, string name, KeyBinding defaultValue) { if (dic.TryGetValue(name, out var value)) { return KeyBinding.Parse(value); } return defaultValue; } private static string BoolToString(bool value) { if (!value) { return "false"; } return "true"; } } public static class ConfigUtilities { private static readonly string[] iwadNames = new string[7] { "DOOM2.WAD", "PLUTONIA.WAD", "TNT.WAD", "DOOM.WAD", "DOOM1.WAD", "FREEDOOM2.WAD", "FREEDOOM1.WAD" }; public static string GetExeDirectory() { return Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); } public static string GetConfigPath() { return Path.Combine(GetExeDirectory(), "managed-doom.cfg"); } public static string GetDefaultIwadPath() { string exeDirectory = GetExeDirectory(); string[] array = iwadNames; foreach (string path in array) { string text = Path.Combine(exeDirectory, path); if (File.Exists(text)) { return text; } } string currentDirectory = Directory.GetCurrentDirectory(); array = iwadNames; foreach (string path2 in array) { string text2 = Path.Combine(currentDirectory, path2); if (File.Exists(text2)) { return text2; } } throw new Exception("No IWAD was found!"); } public static bool IsIwad(string path) { string value = Path.GetFileName(path).ToUpper(); return iwadNames.Contains(value); } public static string[] GetWadPaths(CommandLineArgs args) { List<string> list = new List<string>(); if (args.iwad.Present) { list.Add(args.iwad.Value); } else { list.Add(GetDefaultIwadPath()); } if (args.file.Present) { string[] value = args.file.Value; foreach (string item in value) { list.Add(item); } } return list.ToArray(); } } public static class DoomDebug { public static int CombineHash(int a, int b) { return (3 * a) ^ b; } public static int GetMobjHash(Mobj mobj) { return CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(0, mobj.X.Data), mobj.Y.Data), mobj.Z.Data), (int)mobj.Angle.Data), (int)mobj.Sprite), mobj.Frame), mobj.FloorZ.Data), mobj.CeilingZ.Data), mobj.Radius.Data), mobj.Height.Data), mobj.MomX.Data), mobj.MomY.Data), mobj.MomZ.Data), mobj.Tics), (int)mobj.Flags), mobj.Health), (int)mobj.MoveDir), mobj.MoveCount), mobj.ReactionTime), mobj.Threshold); } public static int GetMobjHash(World world) { int num = 0; foreach (Thinker thinker in world.Thinkers) { if (thinker is Mobj mobj) { num = CombineHash(num, GetMobjHash(mobj)); } } return num; } private static string GetMobjCsv(Mobj mobj) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(mobj.X.Data).Append(","); stringBuilder.Append(mobj.Y.Data).Append(","); stringBuilder.Append(mobj.Z.Data).Append(","); stringBuilder.Append((int)mobj.Angle.Data).Append(","); stringBuilder.Append((int)mobj.Sprite).Append(","); stringBuilder.Append(mobj.Frame).Append(","); stringBuilder.Append(mobj.FloorZ.Data).Append(","); stringBuilder.Append(mobj.CeilingZ.Data).Append(","); stringBuilder.Append(mobj.Radius.Data).Append(","); stringBuilder.Append(mobj.Height.Data).Append(","); stringBuilder.Append(mobj.MomX.Data).Append(","); stringBuilder.Append(mobj.MomY.Data).Append(","); stringBuilder.Append(mobj.MomZ.Data).Append(","); stringBuilder.Append(mobj.Tics).Append(","); stringBuilder.Append((int)mobj.Flags).Append(","); stringBuilder.Append(mobj.Health).Append(","); stringBuilder.Append((int)mobj.MoveDir).Append(","); stringBuilder.Append(mobj.MoveCount).Append(","); stringBuilder.Append(mobj.ReactionTime).Append(","); stringBuilder.Append(mobj.Threshold); return stringBuilder.ToString(); } public static void DumpMobjCsv(string path, World world) { using StreamWriter streamWriter = new StreamWriter(path); foreach (Thinker thinker in world.Thinkers) { if (thinker is Mobj mobj) { streamWriter.WriteLine(GetMobjCsv(mobj)); } } } public static int GetSectorHash(Sector sector) { return CombineHash(CombineHash(CombineHash(0, sector.FloorHeight.Data), sector.CeilingHeight.Data), sector.LightLevel); } public static int GetSectorHash(World world) { int num = 0; Sector[] sectors = world.Map.Sectors; foreach (Sector sector in sectors) { num = CombineHash(num, GetSectorHash(sector)); } return num; } } public static class DoomInterop { public static string ToString(byte[] data, int offset, int maxLength) { int num = 0; for (int i = 0; i < maxLength && data[offset + i] != 0; i++) { num++; } char[] array = new char[num]; for (int j = 0; j < array.Length; j++) { byte b = data[offset + j]; if (97 <= b && b <= 122) { b -= 32; } array[j] = (char)b; } return new string(array); } } public sealed class DoomRandom { private static readonly int[] table = new int[256] { 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36, 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188, 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224, 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242, 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0, 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235, 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113, 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75, 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196, 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113, 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241, 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224, 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226, 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136, 120, 163, 236, 249 }; private int index; public DoomRandom() { index = 0; } public DoomRandom(int seed) { index = seed & 0xFF; } public int Next() { index = (index + 1) & 0xFF; return table[index]; } public void Clear() { index = 0; } } public sealed class DoomString { private static Dictionary<string, DoomString> valueTable = new Dictionary<string, DoomString>(); private static Dictionary<string, DoomString> nameTable = new Dictionary<string, DoomString>(); private string original; private string replaced; public char this[int index] => replaced[index]; public DoomString(string original) { this.original = original; replaced = original; if (!valueTable.ContainsKey(original)) { valueTable.Add(original, this); } } public DoomString(string name, string original) : this(original) { nameTable.Add(name, this); } public override string ToString() { return replaced; } public static implicit operator string(DoomString ds) { return ds.replaced; } public static void ReplaceByValue(string original, string replaced) { if (valueTable.TryGetValue(original, out var value)) { value.replaced = replaced; } } public static void ReplaceByName(string name, string value) { if (nameTable.TryGetValue(name, out var value2)) { value2.replaced = value; } } } public static class DeHackEd { private enum Block { None, Thing, Frame, Pointer, Sound, Ammo, Weapon, Cheat, Misc, Text, Sprite, BexStrings, BexPars } private static Tuple<Action<World, Player, PlayerSpriteDef>, Action<World, Mobj>>[] sourcePointerTable; public static void Initialize(CommandLineArgs args, Wad wad) { if (args.deh.Present) { ReadFiles(args.deh.Value); } if (!args.nodeh.Present) { ReadDeHackEdLump(wad); } } private static void ReadFiles(params string[] fileNames) { string text = null; try { DoomInfo.Strings.PRESSKEY.GetHashCode(); Console.Write("Load DeHackEd patches: "); for (int i = 0; i < fileNames.Length; i++) { ProcessLines(File.ReadLines(text = fileNames[i])); } Console.WriteLine("OK (" + string.Join(", ", fileNames.Select((string x) => Path.GetFileName(x))) + ")"); } catch (Exception innerException) { Console.WriteLine("Failed"); throw new Exception("Failed to apply DeHackEd patch: " + text, innerException); } } private static void ReadDeHackEdLump(Wad wad) { int lumpNumber = wad.GetLumpNumber("DEHACKED"); if (lumpNumber != -1) { DoomInfo.Strings.PRESSKEY.GetHashCode(); try { Console.Write("Load DeHackEd patch from WAD: "); ProcessLines(ReadLines(wad.ReadLump(lumpNumber))); Console.WriteLine("OK"); } catch (Exception innerException) { Console.WriteLine("Failed"); throw new Exception("Failed to apply DeHackEd patch!", innerException); } } } private static IEnumerable<string> ReadLines(byte[] data) { using MemoryStream ms = new MemoryStream(data); using StreamReader sr = new StreamReader(ms); for (string text = sr.ReadLine(); text != null; text = sr.ReadLine()) { yield return text; } } private static void ProcessLines(IEnumerable<string> lines) { if (sourcePointerTable == null) { sourcePointerTable = new Tuple<Action<World, Player, PlayerSpriteDef>, Action<World, Mobj>>[DoomInfo.States.Length]; for (int i = 0; i < sourcePointerTable.Length; i++) { Action<World, Player, PlayerSpriteDef> playerAction = DoomInfo.States[i].PlayerAction; Action<World, Mobj> mobjAction = DoomInfo.States[i].MobjAction; sourcePointerTable[i] = Tuple.Create(playerAction, mobjAction); } } int num = 0; List<string> list = new List<string>(); Block type = Block.None; int lineNumber = 0; foreach (string line in lines) { num++; if (line.Length <= 0 || line[0] != '#') { Block blockType = GetBlockType(line.Split(' ')); if (blockType == Block.None) { list.Add(line); continue; } ProcessBlock(type, list, lineNumber); list.Clear(); list.Add(line); type = blockType; lineNumber = num; } } ProcessBlock(type, list, lineNumber); } private static void ProcessBlock(Block type, List<string> data, int lineNumber) { try { switch (type) { case Block.Thing: ProcessThingBlock(data); break; case Block.Frame: ProcessFrameBlock(data); break; case Block.Pointer: ProcessPointerBlock(data); break; case Block.Sound: ProcessSoundBlock(data); break; case Block.Ammo: ProcessAmmoBlock(data); break; case Block.Weapon: ProcessWeaponBlock(data); break; case Block.Cheat: ProcessCheatBlock(data); break; case Block.Misc: ProcessMiscBlock(data); break; case Block.Text: ProcessTextBlock(data); break; case Block.Sprite: ProcessSpriteBlock(data); break; case Block.BexStrings: ProcessBexStringsBlock(data); break; case Block.BexPars: ProcessBexParsBlock(data); break; } } catch (Exception innerException) { throw new Exception("Failed to process block: " + type.ToString() + " (line " + lineNumber + ")", innerException); } } private static void ProcessThingBlock(List<string> data) { int num = int.Parse(data[0].Split(' ')[1]) - 1; MobjInfo mobjInfo = DoomInfo.MobjInfos[num]; Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data); mobjInfo.DoomEdNum = GetInt(keyValuePairs, "ID #", mobjInfo.DoomEdNum); mobjInfo.SpawnState = (MobjState)GetInt(keyValuePairs, "Initial frame", (int)mobjInfo.SpawnState); mobjInfo.SpawnHealth = GetInt(keyValuePairs, "Hit points", mobjInfo.SpawnHealth); mobjInfo.SeeState = (MobjState)GetInt(keyValuePairs, "First moving frame", (int)mobjInfo.SeeState); mobjInfo.SeeSound = (Sfx)GetInt(keyValuePairs, "Alert sound", (int)mobjInfo.SeeSound); mobjInfo.ReactionTime = GetInt(keyValuePairs, "Reaction time", mobjInfo.ReactionTime); mobjInfo.AttackSound = (Sfx)GetInt(keyValuePairs, "Attack sound", (int)mobjInfo.AttackSound); mobjInfo.PainState = (MobjState)GetInt(keyValuePairs, "Injury frame", (int)mobjInfo.PainState); mobjInfo.PainChance = GetInt(keyValuePairs, "Pain chance", mobjInfo.PainChance); mobjInfo.PainSound = (Sfx)GetInt(keyValuePairs, "Pain sound", (int)mobjInfo.PainSound); mobjInfo.MeleeState = (MobjState)GetInt(keyValuePairs, "Close attack frame", (int)mobjInfo.MeleeState); mobjInfo.MissileState = (MobjState)GetInt(keyValuePairs, "Far attack frame", (int)mobjInfo.MissileState); mobjInfo.DeathState = (MobjState)GetInt(keyValuePairs, "Death frame", (int)mobjInfo.DeathState); mobjInfo.XdeathState = (MobjState)GetInt(keyValuePairs, "Exploding frame", (int)mobjInfo.XdeathState); mobjInfo.DeathSound = (Sfx)GetInt(keyValuePairs, "Death sound", (int)mobjInfo.DeathSound); mobjInfo.Speed = GetInt(keyValuePairs, "Speed", mobjInfo.Speed); mobjInfo.Radius = new Fixed(GetInt(keyValuePairs, "Width", mobjInfo.Radius.Data)); mobjInfo.Height = new Fixed(GetInt(keyValuePairs, "Height", mobjInfo.Height.Data)); mobjInfo.Mass = GetInt(keyValuePairs, "Mass", mobjInfo.Mass); mobjInfo.Damage = GetInt(keyValuePairs, "Missile damage", mobjInfo.Damage); mobjInfo.ActiveSound = (Sfx)GetInt(keyValuePairs, "Action sound", (int)mobjInfo.ActiveSound); mobjInfo.Flags = (MobjFlags)GetInt(keyValuePairs, "Bits", (int)mobjInfo.Flags); mobjInfo.Raisestate = (MobjState)GetInt(keyValuePairs, "Respawn frame", (int)mobjInfo.Raisestate); } private static void ProcessFrameBlock(List<string> data) { int num = int.Parse(data[0].Split(' ')[1]); MobjStateDef mobjStateDef = DoomInfo.States[num]; Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data); mobjStateDef.Sprite = (Sprite)GetInt(keyValuePairs, "Sprite number", (int)mobjStateDef.Sprite); mobjStateDef.Frame = GetInt(keyValuePairs, "Sprite subnumber", mobjStateDef.Frame); mobjStateDef.Tics = GetInt(keyValuePairs, "Duration", mobjStateDef.Tics); mobjStateDef.Next = (MobjState)GetInt(keyValuePairs, "Next frame", (int)mobjStateDef.Next); mobjStateDef.Misc1 = GetInt(keyValuePairs, "Unknown 1", mobjStateDef.Misc1); mobjStateDef.Misc2 = GetInt(keyValuePairs, "Unknown 2", mobjStateDef.Misc2); } private static void ProcessPointerBlock(List<string> data) { Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data); int num = data[0].IndexOf('(') + 1; int length = data[0].IndexOf(')') - num; int num2 = int.Parse(data[0].Substring(num, length).Split(' ')[1]); int @int = GetInt(keyValuePairs, "Codep Frame", -1); if (@int != -1) { MobjStateDef obj = DoomInfo.States[num2]; obj.PlayerAction = sourcePointerTable[@int].Item1; obj.MobjAction = sourcePointerTable[@int].Item2; } } private static void ProcessSoundBlock(List<string> data) { } private static void ProcessAmmoBlock(List<string> data) { int num = int.Parse(data[0].Split(' ')[1]); Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data); int[] max = DoomInfo.AmmoInfos.Max; int[] clip = DoomInfo.AmmoInfos.Clip; max[num] = GetInt(keyValuePairs, "Max ammo", max[num]); clip[num] = GetInt(keyValuePairs, "Per ammo", clip[num]); } private static void ProcessWeaponBlock(List<string> data) { int num = int.Parse(data[0].Split(' ')[1]); WeaponInfo weaponInfo = DoomInfo.WeaponInfos[num]; Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data); weaponInfo.Ammo = (AmmoType)GetInt(keyValuePairs, "Ammo type", (int)weaponInfo.Ammo); weaponInfo.UpState = (MobjState)GetInt(keyValuePairs, "Deselect frame", (int)weaponInfo.UpState); weaponInfo.DownState = (MobjState)GetInt(keyValuePairs, "Select frame", (int)weaponInfo.DownState); weaponInfo.ReadyState = (MobjState)GetInt(keyValuePairs, "Bobbing frame", (int)weaponInfo.ReadyState); weaponInfo.AttackState = (MobjState)GetInt(keyValuePairs, "Shooting frame", (int)weaponInfo.AttackState); weaponInfo.FlashState = (MobjState)GetInt(keyValuePairs, "Firing frame", (int)weaponInfo.FlashState); } private static void ProcessCheatBlock(List<string> data) { } private static void ProcessMiscBlock(List<string> data) { Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data); DoomInfo.DeHackEdConst.InitialHealth = GetInt(keyValuePairs, "Initial Health", DoomInfo.DeHackEdConst.InitialHealth); DoomInfo.DeHackEdConst.InitialBullets = GetInt(keyValuePairs, "Initial Bullets", DoomInfo.DeHackEdConst.InitialBullets); DoomInfo.DeHackEdConst.MaxHealth = GetInt(keyValuePairs, "Max Health", DoomInfo.DeHackEdConst.MaxHealth); DoomInfo.DeHackEdConst.MaxArmor = GetInt(keyValuePairs, "Max Armor", DoomInfo.DeHackEdConst.MaxArmor); DoomInfo.DeHackEdConst.GreenArmorClass = GetInt(keyValuePairs, "Green Armor Class", DoomInfo.DeHackEdConst.GreenArmorClass); DoomInfo.DeHackEdConst.BlueArmorClass = GetInt(keyValuePairs, "Blue Armor Class", DoomInfo.DeHackEdConst.BlueArmorClass); DoomInfo.DeHackEdConst.MaxSoulsphere = GetInt(keyValuePairs, "Max Soulsphere", DoomInfo.DeHackEdConst.MaxSoulsphere); DoomInfo.DeHackEdConst.SoulsphereHealth = GetInt(keyValuePairs, "Soulsphere Health", DoomInfo.DeHackEdConst.SoulsphereHealth); DoomInfo.DeHackEdConst.MegasphereHealth = GetInt(keyValuePairs, "Megasphere Health", DoomInfo.DeHackEdConst.MegasphereHealth); DoomInfo.DeHackEdConst.GodModeHealth = GetInt(keyValuePairs, "God Mode Health", DoomInfo.DeHackEdConst.GodModeHealth); DoomInfo.DeHackEdConst.IdfaArmor = GetInt(keyValuePairs, "IDFA Armor", DoomInfo.DeHackEdConst.IdfaArmor); DoomInfo.DeHackEdConst.IdfaArmorClass = GetInt(keyValuePairs, "IDFA Armor Class", DoomInfo.DeHackEdConst.IdfaArmorClass); DoomInfo.DeHackEdConst.IdkfaArmor = GetInt(keyValuePairs, "IDKFA Armor", DoomInfo.DeHackEdConst.IdkfaArmor); DoomInfo.DeHackEdConst.IdkfaArmorClass = GetInt(keyValuePairs, "IDKFA Armor Class", DoomInfo.DeHackEdConst.IdkfaArmorClass); DoomInfo.DeHackEdConst.BfgCellsPerShot = GetInt(keyValuePairs, "BFG Cells/Shot", DoomInfo.DeHackEdConst.BfgCellsPerShot); DoomInfo.DeHackEdConst.MonstersInfight = GetInt(keyValuePairs, "Monsters Infight", 0) == 221; } private static void ProcessTextBlock(List<string> data) { string[] array = data[0].Split(' '); int num = int.Parse(array[1]); int num2 = int.Parse(array[2]); int num3 = 1; int num4 = 0; StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < num; i++) { if (num4 == data[num3].Length) { stringBuilder.Append('\n'); num3++; num4 = 0; } else { stringBuilder.Append(data[num3][num4]); num4++; } } StringBuilder stringBuilder2 = new StringBuilder(); for (int j = 0; j < num2; j++) { if (num4 == data[num3].Length) { stringBuilder2.Append('\n'); num3++; num4 = 0; } else { stringBuilder2.Append(data[num3][num4]); num4++; } } DoomString.ReplaceByValue(stringBuilder.ToString(), stringBuilder2.ToString()); } private static void ProcessSpriteBlock(List<string> data) { } private static void ProcessBexStringsBlock(List<string> data) { string text = null; StringBuilder stringBuilder = null; foreach (string item in data.Skip(1)) { if (text == null) { int num = item.IndexOf('='); if (num != -1) { string text2 = item.Substring(0, num).Trim(); string text3 = item.Substring(num + 1).Trim().Replace("\\n", "\n"); if (text3.Last() != '\\') { DoomString.ReplaceByName(text2, text3); continue; } text = text2; stringBuilder = new StringBuilder(); stringBuilder.Append(text3, 0, text3.Length - 1); } } else { string text4 = item.Trim().Replace("\\n", "\n"); if (text4.Last() != '\\') { stringBuilder.Append(text4); DoomString.ReplaceByName(text, stringBuilder.ToString()); text = null; stringBuilder = null; } else { stringBuilder.Append(text4, 0, text4.Length - 1); } } } } private static void ProcessBexParsBlock(List<string> data) { foreach (string item in data.Skip(1)) { string[] array = item.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (array.Length < 3 || !(array[0] == "par")) { continue; } List<int> list = new List<int>(); using (IEnumerator<string> enumerator2 = array.Skip(1).GetEnumerator()) { int result; while (enumerator2.MoveNext() && int.TryParse(enumerator2.Current, out result)) { list.Add(result); } } if (list.Count == 2 && list[0] <= DoomInfo.ParTimes.Doom2.Count) { DoomInfo.ParTimes.Doom2[list[0] - 1] = list[1]; } if (list.Count >= 3 && list[0] <= DoomInfo.ParTimes.Doom1.Count && list[1] <= DoomInfo.ParTimes.Doom1[list[0] - 1].Count) { DoomInfo.ParTimes.Doom1[list[0] - 1][list[1] - 1] = list[2]; } } } private static Block GetBlockType(string[] split) { if (IsThingBlockStart(split)) { return Block.Thing; } if (IsFrameBlockStart(split)) { return Block.Frame; } if (IsPointerBlockStart(split)) { return Block.Pointer; } if (IsSoundBlockStart(split)) { return Block.Sound; } if (IsAmmoBlockStart(split)) { return Block.Ammo; } if (IsWeaponBlockStart(split)) { return Block.Weapon; } if (IsCheatBlockStart(split)) { return Block.Cheat; } if (IsMiscBlockStart(split)) { return Block.Misc; } if (IsTextBlockStart(split)) { return Block.Text; } if (IsSpriteBlockStart(split)) { return Block.Sprite; } if (IsBexStringsBlockStart(split)) { return Block.BexStrings; } if (IsBexParsBlockStart(split)) { return Block.BexPars; } return Block.None; } private static bool IsThingBlockStart(string[] split) { if (split.Length < 2) { return false; } if (split[0] != "Thing") { return false; } if (!IsNumber(split[1])) { return false; } return true; } private static bool IsFrameBlockStart(string[] split) { if (split.Length < 2) { return false; } if (split[0] != "Frame") { return false; } if (!IsNumber(split[1])) { return false; } return true; } private static bool IsPointerBlockStart(string[] split) { if (split.Length < 2) { return false; } if (split[0] != "Pointer") { return false; } return true; } private static bool IsSoundBlockStart(string[] split) { if (split.Length < 2) { return false; } if (split[0] != "Sound") { return false; } if (!IsNumber(split[1])) { return false; } return true; } private static bool IsAmmoBlockStart(string[] split) { if (split.Length < 2) { return false; } if (split[0] != "Ammo") { return false; } if (!IsNumber(split[1])) { return false; } return true; } private static bool IsWeaponBlockStart(string[] split) { if (split.Length < 2) { return false; } if (split[0] != "Weapon") { return false; } if (!IsNumber(split[1])) { return false; } return true; } private static bool IsCheatBlockStart(string[] split) { if (split.Length < 2) { return false; } if (split[0] != "Cheat") { return false; } if (split[1] != "0") { return false; } return true; } private static bool IsMiscBlockStart(string[] split) { if (split.Length < 2) { return false; } if (split[0] != "Misc") { return false; } if (split[1] != "0") { return false; } return true; } private static bool IsTextBlockStart(string[] split) { if (split.Length < 3) { return false; } if (split[0] != "Text") { return false; } if (!IsNumber(split[1])) { return false; } if (!IsNumber(split[2])) { return false; } return true; } private static bool IsSpriteBlockStart(string[] split) { if (split.Length < 2) { return false; } if (split[0] != "Sprite") { return false; } if (!IsNumber(split[1])) { return false; } return true; } private static bool IsBexStringsBlockStart(string[] split) { if (split[0] == "[STRINGS]") { return true; } return false; } private static bool IsBexParsBlockStart(string[] split) { if (split[0] == "[PARS]") { return true; } return false; } private static bool IsNumber(string value) { foreach (char c in value) { if ('0' > c || c > '9') { return false; } } return true; } private static Dictionary<string, string> GetKeyValuePairs(List<string> data) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (string datum in data) { string[] array = datum.Split('='); if (array.Length == 2) { dictionary[array[0].Trim()] = array[1].Trim(); } } return dictionary; } private static int GetInt(Dictionary<string, string> dic, string key, int defaultValue) { if (dic.TryGetValue(key, out var value) && int.TryParse(value, out var result)) { return result; } return defaultValue; } } public class Doom { private CommandLineArgs args; private Config config; private GameContent content; private IVideo video; private ISound sound; private IMusic music; private IUserInput userInput; private List<DoomEvent> events; private GameOptions options; private DoomMenu menu; private OpeningSequence opening; private DemoPlayback demoPlayback; private TicCmd[] cmds; private DoomGame game; private WipeEffect wipeEffect; private bool wiping; private DoomState currentState; private DoomState nextState; private bool needWipe; private bool sendPause; private bool quit; private string quitMessage; private bool mouseGrabbed; public DoomState State => currentState; public OpeningSequence Opening => opening; public DemoPlayback DemoPlayback => demoPlayback; public GameOptions Options => options; public DoomGame Game => game; public DoomMenu Menu => menu; public WipeEffect WipeEffect => wipeEffect; public bool Wiping => wiping; public string QuitMessage => quitMessage; public Doom(CommandLineArgs args, Config config, GameContent content, IVideo video, ISound sound, IMusic music, IUserInput userInput) { video = video ?? NullVideo.GetInstance(); sound = sound ?? NullSound.GetInstance(); music = music ?? NullMusic.GetInstance(); userInput = userInput ?? NullUserInput.GetInstance(); this.args = args; this.config = config; this.content = content; this.video = video; this.sound = sound; this.music = music; this.userInput = userInput; events = new List<DoomEvent>(); options = new GameOptions(args, content); options.Video = video; options.Sound = sound; options.Music = music; options.UserInput = userInput; menu = new DoomMenu(this); opening = new OpeningSequence(content, options); cmds = new TicCmd[Player.MaxPlayerCount]; for (int i = 0; i < Player.MaxPlayerCount; i++) { cmds[i] = new TicCmd(); } game = new DoomGame(content, options); wipeEffect = new WipeEffect(video.WipeBandCount, video.WipeHeight); wiping = false; currentState = DoomState.None; nextState = DoomState.Opening; needWipe = false; sendPause = false; quit = false; quitMessage = null; mouseGrabbed = false; CheckGameArgs(); } private void CheckGameArgs() { if (args.warp.Present) { nextState = DoomState.Game; options.Episode = args.warp.Value.Item1; options.Map = args.warp.Value.Item2; game.DeferedInitNew(); } else if (args.episode.Present) { nextState = DoomState.Game; options.Episode = args.episode.Value; options.Map = 1; game.DeferedInitNew(); } if (args.skill.Present) { options.Skill = (GameSkill)(args.skill.Value - 1); } if (args.deathmatch.Present) { options.Deathmatch = 1; } if (args.altdeath.Present) { options.Deathmatch = 2; } if (args.fast.Present) { options.FastMonsters = true; } if (args.respawn.Present) { options.RespawnMonsters = true; } if (args.nomonsters.Present) { options.NoMonsters = true; } if (args.loadgame.Present) { nextState = DoomState.Game; game.LoadGame(args.loadgame.Value); } if (args.playdemo.Present) { nextState = DoomState.DemoPlayback; demoPlayback = new DemoPlayback(args, content, options, args.playdemo.Value); } if (args.timedemo.Present) { nextState = DoomState.DemoPlayback; demoPlayback = new DemoPlayback(args, content, options, args.timedemo.Value); } } public void NewGame(GameSkill skill, int episode, int map) { game.DeferedInitNew(skill, episode, map); nextState = DoomState.Game; } public void EndGame() { nextState = DoomState.Opening; } private void DoEvents() { if (wiping) { return; } foreach (DoomEvent @event in events) { if (menu.DoEvent(@event) || (@event.Type == EventType.KeyDown && CheckFunctionKey(@event.Key))) { continue; } if (currentState == DoomState.Game) { if (@event.Key == DoomKey.Pause && @event.Type == EventType.KeyDown) { sendPause = true; } else if (!game.DoEvent(@event)) { } } else if (currentState == DoomState.DemoPlayback) { demoPlayback.DoEvent(@event); } } events.Clear(); } private bool CheckFunctionKey(DoomKey key) { switch (key) { case DoomKey.F1: menu.ShowHelpScreen(); return true; case DoomKey.F2: menu.ShowSaveScreen(); return true; case DoomKey.F3: menu.ShowLoadScreen(); return true; case DoomKey.F4: menu.ShowVolumeControl(); return true; case DoomKey.F6: menu.QuickSave(); return true; case DoomKey.F7: if (currentState == DoomState.Game) { menu.EndGame(); } else { options.Sound.StartSound(Sfx.OOF); } return true; case DoomKey.F8: video.DisplayMessage = !video.DisplayMessage; if (currentState == DoomState.Game && game.State == GameState.Level) { string message2 = ((!video.DisplayMessage) ? ((string)DoomInfo.Strings.MSGOFF) : ((string)DoomInfo.Strings.MSGON)); game.World.ConsolePlayer.SendMessage(message2); } menu.StartSound(Sfx.SWTCHN); return true; case DoomKey.F9: menu.QuickLoad(); return true; case DoomKey.F10: menu.Quit(); return true; case DoomKey.F11: { int gammaCorrectionLevel = video.GammaCorrectionLevel; gammaCorrectionLevel++; if (gammaCorrectionLevel > video.MaxGammaCorrectionLevel) { gammaCorrectionLevel = 0; } video.GammaCorrectionLevel = gammaCorrectionLevel; if (currentState == DoomState.Game && game.State == GameState.Level) { string message = ((gammaCorrectionLevel != 0) ? ("Gamma correction level " + gammaCorrectionLevel) : ((string)DoomInfo.Strings.GAMMALVL0)); game.World.ConsolePlayer.SendMessage(message); } return true; } case DoomKey.Quote: case DoomKey.Equal: case DoomKey.Add: if (currentState == DoomState.Game && game.State == GameState.Level && game.World.AutoMap.Visible) { return false; } video.WindowSize = Math.Min(video.WindowSize + 1, video.MaxWindowSize); sound.StartSound(Sfx.STNMOV); return true; case DoomKey.Semicolon: case DoomKey.Hyphen: case DoomKey.Subtract: if (currentState == DoomState.Game && game.State == GameState.Level && game.World.AutoMap.Visible) { return false; } video.WindowSize = Math.Max(video.WindowSize - 1, 0); sound.StartSound(Sfx.STNMOV); return true; default: return false; } } public UpdateResult Update() { DoEvents(); if (!wiping) { menu.Update(); if (nextState != currentState) { if (nextState != DoomState.Opening) { opening.Reset(); } if (nextState != DoomState.DemoPlayback) { demoPlayback = null; } currentState = nextState; } if (quit) { return UpdateResult.Completed; } if (needWipe) { needWipe = false; StartWipe(); } } if (!wiping) { switch (currentState) { case DoomState.Opening: if (opening.Update() == UpdateResult.NeedWipe) { StartWipe(); } break; case DoomState.DemoPlayback: switch (demoPlayback.Update()) { case UpdateResult.NeedWipe: StartWipe(); break; case UpdateResult.Completed: Quit("FPS: " + demoPlayback.Fps.ToString("0.0")); break; } break; case DoomState.Game: userInput.BuildTicCmd(cmds[options.ConsolePlayer]); if (sendPause) { sendPause = false; cmds[options.ConsolePlayer].Buttons |= (byte)(TicCmdButtons.Special | TicCmdButtons.Pause); } if (game.Update(cmds) == UpdateResult.NeedWipe) { StartWipe(); } break; default: throw new Exception("Invalid application state!"); } } if (wiping && wipeEffect.Update() == UpdateResult.Completed) { wiping = false; } sound.Update(); CheckMouseState(); return UpdateResult.None; } private void CheckMouseState() { bool flag = video.HasFocus() && (config.video_fullscreen || (currentState == DoomState.Game && !menu.Active)); if (mouseGrabbed) { if (!flag) { userInput.ReleaseMouse(); mouseGrabbed = false; } } else if (flag) { userInput.GrabMouse(); mouseGrabbed = true; } } private void StartWipe() { wipeEffect.Start(); video.InitializeWipe(); wiping = true; } public void PauseGame() { if (currentState == DoomState.Game && game.State == GameState.Level && !game.Paused && !sendPause) { sendPause = true; } } public void ResumeGame() { if (currentState == DoomState.Game && game.State == GameState.Level && game.Paused && !sendPause) { sendPause = true; } } public bool SaveGame(int slotNumber, string description) { if (currentState == DoomState.Game && game.State == GameState.Level) { game.SaveGame(slotNumber, description); return true; } return false; } public void LoadGame(int slotNumber) { game.LoadGame(slotNumber); nextState = DoomState.Game; } public void Quit() { quit = true; } public void Quit(string message) { quit = true; quitMessage = message; } public void PostEvent(DoomEvent e) { if (events.Count < 64) { events.Add(e); } } } public enum DoomState { None, Opening, DemoPlayback, Game } public sealed class DoomEvent { private EventType type; private DoomKey key; public EventType Type => type; public DoomKey Key => key; public DoomEvent(EventType type, DoomKey key) { this.type = type; this.key = key; } } public enum EventType { KeyDown, KeyUp, Mouse, Joystick } public sealed class Demo { private int p; private byte[] data; private GameOptions options; private int playerCount; public GameOptions Options => options; public Demo(byte[] data) { p = 0; if (data[p++] != 109) { throw new Exception("Demo is from a different game version!"); } this.data = data; options = new GameOptions(); options.Skill = (GameSkill)data[p++]; options.Episode = data[p++]; options.Map = data[p++]; options.Deathmatch = data[p++]; options.RespawnMonsters = data[p++] != 0; options.FastMonsters = data[p++] != 0; options.NoMonsters = data[p++] != 0; options.ConsolePlayer = data[p++]; options.Players[0].InGame = data[p++] != 0; options.Players[1].InGame = data[p++] != 0; options.Players[2].InGame = data[p++] != 0; options.Players[3].InGame = data[p++] != 0; options.DemoPlayback = true; playerCount = 0; for (int i = 0; i < Player.MaxPlayerCount; i++) { if (options.Players[i].InGame) { playerCount++; } } if (playerCount >= 2) { options.NetGame = true; } } public Demo(string fileName) : this(File.ReadAllBytes(fileName)) { } public bool ReadCmd(TicCmd[] cmds) { if (p == data.Length) { return false; } if (data[p] == 128) { return false; } if (p + 4 * playerCount > data.Length) { return false; } Player[] players = options.Players; for (int i = 0; i < Player.MaxPlayerCount; i++) { if (players[i].InGame) { TicCmd obj = cmds[i]; obj.ForwardMove = (sbyte)data[p++]; obj.SideMove = (sbyte)data[p++]; obj.AngleTurn = (short)(data[p++] << 8); obj.Buttons = data[p++]; } } return true; } } public sealed class DoomGame { private enum GameAction { Nothing, LoadLevel, NewGame, LoadGame, SaveGame, Completed, Victory, WorldDone } private GameContent content; private GameOptions options; private GameAction gameAction; private GameState gameState; private int gameTic; private World world; private Intermission intermission; private Finale finale; private bool paused; private int loadGameSlotNumber; private int saveGameSlotNumber; private string saveGameDescription; public GameOptions Options => options; public GameState State => gameState; public int GameTic => gameTic; public World World => world; public Intermission Intermission => intermission; public Finale Finale => finale; public bool Paused => paused; public DoomGame(GameContent content, GameOptions options) { this.content = content; this.options = options; gameAction = GameAction.Nothing; gameTic = 0; } public void DeferedInitNew() { gameAction = GameAction.NewGame; } public void DeferedInitNew(GameSkill skill, int episode, int map) { options.Skill = skill; options.Episode = episode; options.Map = map; gameAction = GameAction.NewGame; } public void LoadGame(int slotNumber) { loadGameSlotNumber = slotNumber; gameAction = GameAction.LoadGame; } public void SaveGame(int slotNumber, string description) { saveGameSlotNumber = slotNumber; saveGameDescription = description; gameAction = GameAction.SaveGame; } public UpdateResult Update(TicCmd[] cmds) { Player[] players = options.Players; for (int i = 0; i < Player.MaxPlayerCount; i++) { if (players[i].InGame && players[i].PlayerState == PlayerState.Reborn) { DoReborn(i); } } while (gameAction != 0) { switch (gameAction) { case GameAction.LoadLevel: DoLoadLevel(); break; case GameAction.NewGame: DoNewGame(); break; case GameAction.LoadGame: DoLoadGame(); break; case GameAction.SaveGame: DoSaveGame(); break; case GameAction.Completed: DoCompleted(); break; case GameAction.Victory: DoFinale(); break; case GameAction.WorldDone: DoWorldDone(); break; } } for (int j = 0; j < Player.MaxPlayerCount; j++) { if (players[j].InGame) { TicCmd cmd = players[j].Cmd; cmd.CopyFrom(cmds[j]); if (cmd.ForwardMove > GameConst.TurboThreshold && (world.LevelTime & 0x1F) == 0 && ((world.LevelTime >> 5) & 3) == j) { players[options.ConsolePlayer].SendMessage(players[j].Name + " is turbo!"); } } } for (int k = 0; k < Player.MaxPlayerCount; k++) { if (players[k].InGame && (players[k].Cmd.Buttons & TicCmdButtons.Special) != 0 && (players[k].Cmd.Buttons & TicCmdButtons.SpecialMask) == TicCmdButtons.Pause) { paused = !paused; if (paused) { options.Sound.Pause(); } else { options.Sound.Resume(); } } } UpdateResult updateResult = UpdateResult.None; switch (gameState) { case GameState.Level: if (!paused || world.FirstTicIsNotYetDone) { updateResult = world.Update(); if (updateResult == UpdateResult.Completed) { gameAction = GameAction.Completed; } } break; case GameState.Intermission: updateResult = intermission.Update(); if (updateResult != UpdateResult.Completed) { break; } gameAction = GameAction.WorldDone; if (world.SecretExit) { players[options.ConsolePlayer].DidSecret = true; } if (options.GameMode != GameMode.Commercial) { break; } switch (options.Map) { case 6: case 11: case 20: case 30: DoFinale(); updateResult = UpdateResult.NeedWipe; break; case 15: case 31: if (world.SecretExit) { DoFinale(); updateResult = UpdateResult.NeedWipe; } break; } break; case GameState.Finale: updateResult = finale.Update(); if (updateResult == UpdateResult.Completed) { gameAction = GameAction.WorldDone; } break; } gameTic++; if (updateResult == UpdateResult.NeedWipe) { return UpdateResult.NeedWipe; } return UpdateResult.None; } private void DoLoadLevel() { gameAction = GameAction.Nothing; gameState = GameState.Level; Player[] players = options.Players; for (int i = 0; i < Player.MaxPlayerCount; i++) { if (players[i].InGame && players[i].PlayerState == PlayerState.Dead) { players[i].PlayerState = PlayerState.Reborn; } Array.Clear(players[i].Frags, 0, players[i].Frags.Length); } intermission = null; options.Sound.Reset(); world = new World(content, options, this); options.UserInput.Reset(); } private void DoNewGame() { gameAction = GameAction.Nothing; InitNew(options.Skill, options.Episode, options.Map); } private void DoLoadGame() { gameAction = GameAction.Nothing; string path = Path.Combine(ConfigUtilities.GetExeDirectory(), "doomsav" + loadGameSlotNumber + ".dsg"); SaveAndLoad.Load(this, path); } private void DoSaveGame() { gameAction = GameAction.Nothing; string path = Path.Combine(ConfigUtilities.GetExeDirectory(), "doomsav" + saveGameSlotNumber + ".dsg"); SaveAndLoad.Save(this, saveGameDescription, path); world.ConsolePlayer.SendMessage(DoomInfo.Strings.GGSAVED); } private void DoCompleted() { gameAction = GameAction.Nothing; for (int i = 0; i < Player.MaxPlayerCount; i++) { if (options.Players[i].InGame) { options.Players[i].FinishLevel(); } } if (options.GameMode != GameMode.Commercial) { switch (options.Map) { case 8: gameAction = GameAction.Victory; return; case 9: { for (int j = 0; j < Player.MaxPlayerCount; j++) { options.Players[j].DidSecret = true; } break; } } } if (options.Map == 8 && options.GameMode != GameMode.Commercial) { gameAction = GameAction.Victory; return; } if (options.Map == 9 && options.GameMode != GameMode.Commercial) { for (int k = 0; k < Player.MaxPlayerCount; k++) { options.Players[k].DidSecret = true; } } IntermissionInfo intermissionInfo = options.IntermissionInfo; intermissionInfo.DidSecret = options.Players[options.ConsolePlayer].DidSecret; intermissionInfo.Episode = options.Episode - 1; intermissionInfo.LastLevel = options.Map - 1; if (options.GameMode == GameMode.Commercial) { if (world.SecretExit) { switch (options.Map) { case 15: intermissionInfo.NextLevel = 30; break; case 31: intermissionInfo.NextLevel = 31; break; } } else { int map = options.Map; if ((uint)(map - 31) <= 1u) { intermissionInfo.NextLevel = 15; } else { intermissionInfo.NextLevel = options.Map; } } } else if (world.SecretExit) { intermissionInfo.NextLevel = 8; } else if (options.Map == 9) { switch (options.Episode) { case 1: intermissionInfo.NextLevel = 3; break; case 2: intermissionInfo.NextLevel = 5; break; case 3: intermissionInfo.NextLevel = 6; break; case 4: intermissionInfo.NextLevel = 2; break; } } else { intermissionInfo.NextLevel = options.Map; } intermissionInfo.MaxKillCount = world.TotalKills; intermissionInfo.MaxItemCount = world.TotalItems; intermissionInfo.MaxSecretCount = world.TotalSecrets; intermissionInfo.TotalFrags = 0; if (options.GameMode == GameMode.Commercial) { intermissionInfo.ParTime = 35 * DoomInfo.ParTimes.Doom2[options.Map - 1]; } else { intermissionInfo.ParTime = 35 * DoomInfo.ParTimes.Doom1[options.Episode - 1][options.Map - 1]; } Player[] players = options.Players; for (int l = 0; l < Player.MaxPlayerCount; l++) { intermissionInfo.Players[l].InGame = players[l].InGame; intermissionInfo.Players[l].KillCount = players[l].KillCount; intermissionInfo.Players[l].ItemCount = players[l].ItemCount; intermissionInfo.Players[l].SecretCount = players[l].SecretCount; intermissionInfo.Players[l].Time = world.LevelTime; Array.Copy(players[l].Frags, intermissionInfo.Players[l].Frags, Player.MaxPlayerCount); } gameState = GameState.Intermission; intermission = new Intermission(options, intermissionInfo); } private void DoWorldDone() { gameAction = GameAction.Nothing; gameState = GameState.Level; options.Map = options.IntermissionInfo.NextLevel + 1; DoLoadLevel(); } private void DoFinale() { gameAction = GameAction.Nothing; gameState = GameState.Finale; finale = new Finale(options); } public void InitNew(GameSkill skill, int episode, int map) { options.Skill = (GameSkill)Math.Clamp((int)skill, 0, 4); if (options.GameMode == GameMode.Retail) { options.Episode = Math.Clamp(episode, 1, 4); } else if (options.GameMode == GameMode.Shareware) { options.Episode = 1; } else { options.Episode = Math.Clamp(episode, 1, 4); } if (options.GameMode == GameMode.Commercial) { options.Map = Math.Clamp(map, 1, 32); } else { options.Map = Math.Clamp(map, 1, 9); } options.Random.Clear(); for (int i = 0; i < Player.MaxPlayerCount; i++) { options.Players[i].PlayerState = PlayerState.Reborn; } DoLoadLevel(); } public bool DoEvent(DoomEvent e) { if (gameState == GameState.Level) { return world.DoEvent(e); } if (gameState == GameState.Finale) { return finale.DoEvent(e); } return false; } private void DoReborn(int playerNumber) { if (!options.NetGame) { gameAction = GameAction.LoadLevel; return; } options.Players[playerNumber].Mobj.Player = null; ThingAllocation thingAllocation = world.ThingAllocation; if (options.Deathmatch != 0) { thingAllocation.DeathMatchSpawnPlayer(playerNumber); return; } if (thingAllocation.CheckSpot(playerNumber, thingAllocation.PlayerStarts[playerNumber])) { thingAllocation.SpawnPlayer(thingAllocation.PlayerStarts[playerNumber]); return; } for (int i = 0; i < Player.MaxPlayerCount; i++) { if (thingAllocation.CheckSpot(playerNumber, thingAllocation.PlayerStarts[i])) { thingAllocation.PlayerStarts[i].Type = playerNumber + 1; world.ThingAllocation.SpawnPlayer(thingAllocation.PlayerStarts[i]); thingAllocation.PlayerStarts[i].Type = i + 1; return; } } world.ThingAllocation.SpawnPlayer(thingAllocation.PlayerStarts[playerNumber]); } } public static class GameConst { public static readonly int TicRate = 35; public static readonly Fixed MaxThingRadius = Fixed.FromInt(32); public static readonly int TurboThreshold = 50; } public sealed class GameContent : IDisposable { private Wad wad; private Palette palette; private ColorMap colorMap; private ITextureLookup textures; private IFlatLookup flats; private ISpriteLookup sprites; private TextureAnimation animation; public Wad Wad => wad; public Palette Palette => palette; public ColorMap ColorMap => colorMap; public ITextureLookup Textures => textures; public IFlatLookup Flats => flats; public ISpriteLookup Sprites => sprites; public TextureAnimation Animation => animation; private GameContent() { } public GameContent(CommandLineArgs args) { wad = new Wad(ConfigUtilities.GetWadPaths(args)); DeHackEd.Initialize(args, wad); palette = new Palette(wad); colorMap = new ColorMap(wad); textures = new TextureLookup(wad); flats = new FlatLookup(wad); sprites = new SpriteLookup(wad); animation = new TextureAnimation(textures, flats); } public static GameContent CreateDummy(params string[] wadPaths) { GameContent gameContent = new GameContent(); gameContent.wad = new Wad(wadPaths); gameContent.palette = new Palette(gameContent.wad); gameContent.colorMap = new ColorMap(gameContent.wad); gameContent.textures = new DummyTextureLookup(gameContent.wad); gameContent.flats = new DummyFlatLookup(gameContent.wad); gameContent.sprites = new DummySpriteLookup(gameContent.wad); gameContent.animation = new TextureAnimation(gameContent.textures, gameContent.flats); return gameContent; } public void Dispose() { if (wad != null) { wad.Dispose(); wad = null; } } } public enum GameMode { Shareware, Registered, Commercial, Retail, Indetermined } public sealed class GameOptions { private GameVersion gameVersion; private GameMode gameMode; private MissionPack missionPack; private Player[] players; private int consolePlayer; private int episode; private int map; private GameSkill skill; private bool demoPlayback; private bool netGame; private int deathmatch; private bool fastMonsters; private bool respawnMonsters; private bool noMonsters; private IntermissionInfo intermissionInfo; private DoomRandom random; private IVideo video; private ISound sound; private IMusic music; private IUserInput userInput; public GameVersion GameVersion { get { return gameVersion; } set { gameVersion = value; } } public GameMode GameMode { get { return gameMode; } set { gameMode = value; } } public MissionPack MissionPack { get { return missionPack; } set { missionPack = value; } } public Player[] Players => players; public int ConsolePlayer { get { return consolePlayer; } set { consolePlayer = value; } } public int Episode { get { return episode; } set { episode = value; } } public int Map { get { return map; } set { map = value; } } public GameSkill Skill { get { return skill; } set { skill = value; } } public bool DemoPlayback { get { return demoPlayback; } set { demoPlayback = value; } } public bool NetGame { get { return netGame; } set { netGame = value; } } public int Deathmatch { get { return deathmatch; } set { deathmatch = value; } } public bool FastMonsters { get { return fastMonsters; } set { fastMonsters = value; } } public bool RespawnMonsters { get { return respawnMonsters; } set { respawnMonsters = value; } } public bool NoMonsters { get { return noMonsters; } set { noMonsters = value; } } public IntermissionInfo IntermissionInfo => intermissionInfo; public DoomRandom Random => random; public IVideo Video { get { return video; } set { video = value; } } public ISound Sound { get { return sound; } set { sound = value; } } public IMusic Music { get { return music; } set { music = value; } } public IUserInput UserInput { get { return userInput; } set { userInput = value; } } public GameOptions() { gameVersion = GameVersion.Version109; gameMode = GameMode.Commercial; missionPack = MissionPack.Doom2; players = new Player[Player.MaxPlayerCount]; for (int i = 0; i < Player.MaxPlayerCount; i++) { players[i] = new Player(i); } players[0].InGame = true; consolePlayer = 0; episode = 1; map = 1; skill = GameSkill.Medium; demoPlayback = false; netGame = false; deathmatch = 0; fastMonsters = false; respawnMonsters = false; noMonsters = false; intermissionInfo = new IntermissionInfo(); random = new DoomRandom(); video = NullVideo.GetInstance(); sound = NullSound.GetInstance(); music = NullMusic.GetInstance(); userInput = NullUserInput.GetInstance(); } public GameOptions(CommandLineArgs args, GameContent content) : this() { if (args.solonet.Present) { netGame = true; } gameVersion = content.Wad.GameVersion; gameMode = content.Wad.GameMode; missionPack = content.Wad.MissionPack; } } public enum GameSkill { Baby, Easy, Medium, Hard, Nightmare } public enum GameState { Level, Intermission, Finale } public enum GameVersion { Version109, Ultimate, Final, Final2 } public enum MissionPack { Doom2, Plutonia, Tnt } public sealed class Player { public static readonly int MaxPlayerCount = 4; public static readonly Fixed NormalViewHeight = Fixed.FromInt(41); private static readonly string[] defaultPlayerNames = new string[4] { "Green", "Indigo", "Brown", "Red" }; private int number; private string name; private bool inGame; private Mobj mobj; private PlayerState playerState; private TicCmd cmd; private Fixed viewZ; private Fixed viewHeight; private Fixed deltaViewHeight; private Fixed bob; private int health; private int armorPoints; private int armorType; private int[] powers; private bool[] cards; private bool backpack; private int[] frags; private WeaponType readyWeapon; private WeaponType pendingWeapon; private bool[] weaponOwned; private int[] ammo; private int[] maxAmmo; private bool attackDown; private bool useDown; private CheatFlags cheats; private int refire; private int killCount; private int itemCount; private int secretCount; private string message; private int messageTime; private int damageCount; private int bonusCount; private Mobj attacker; private int extraLight; private int fixedColorMap; private int colorMap; private PlayerSpriteDef[] playerSprites; private bool didSecret; private bool interpolate; private Fixed oldViewZ; private Angle oldAngle; public int Number => number; public string Name => name; public bool InGame { get { return inGame; } set { inGame = value; } } public Mobj Mobj { get { return mobj; } set { mobj = value; } } public PlayerState PlayerState { get { return playerState; } set { playerState = value; } } public TicCmd Cmd => cmd; public Fixed ViewZ { get { return viewZ; } set { viewZ = value; } } public Fixed ViewHeight { get { return viewHeight; } set { viewHeight = value; } } public Fixed DeltaViewHeight { get { return deltaViewHeight; } set { deltaViewHeight = value; } } public Fixed Bob { get { return bob; } set { bob = value; } } public int Health { get { return health; } set { health = value; } } public int ArmorPoints { get { return armorPoints; } set { armorPoints = value; } } public int ArmorType { get { return armorType; } set { armorType = value; } } public int[] Powers => powers; public bool[] Cards => cards; public bool Backpack { get { return backpack; } set { backpack = value; } } public int[] Frags => frags; public WeaponType ReadyWeapon { get { return readyWeapon; } set { readyWeapon = value; } } public WeaponType PendingWeapon { get { return pendingWeapon; } set { pendingWeapon = value; } } public bool[] WeaponOwned => weaponOwned; public int[] Ammo => ammo; public int[] MaxAmmo => maxAmmo; public bool AttackDown { get { return attackDown; } set { attackDown = value; } } public bool UseDown { get { return useDown; } set { useDown = value; } } public CheatFlags Cheats { get { return cheats; } set { cheats = value; } } public int Refire { get { return refire; } set { refire = value; } } public int KillCount { get { return killCount; } set { killCount = value; } } public int ItemCount { get { return itemCount; } set { itemCount = value; } } public int SecretCount { get { return secretCount; } set { secretCount = value; } } public string Message { get { return message; } set { message = value; } } public int MessageTime { get { return messageTime; } set { messageTime = value; } } public int DamageCount { get { return damageCount; } set { damageCount = value; } } public int BonusCount { get { return bonusCount; } set { bonusCount = value; } } public Mobj Attacker { get { return attacker; } set { attacker = value; } } public int ExtraLight { get { return extraLight; } set { extraLight = value; } } public int FixedColorMap { get { return fixedColorMap; } set { fixedColorMap = value; } } public int ColorMap { get { return colorMap; } set { colorMap = value; } } public PlayerSpriteDef[] PlayerSprites => playerSprites; public bool DidSecret { get { return didSecret; } set { didSecret = value; } } public Player(int number) { this.number = number; name = defaultPlayerNames[number]; cmd = new TicCmd(); powers = new int[6]; cards = new bool[6]; frags = new int[MaxPlayerCount]; weaponOwned = new bool[9]; ammo = new int[4]; maxAmmo = new int[4]; playerSprites = new PlayerSpriteDef[2]; for (int i = 0; i < playerSprites.Length; i++) { playerSprites[i] = new PlayerSpriteDef(); } } public void Clear() { mobj = null; playerState = PlayerState.Live; cmd.Clear(); viewZ = Fixed.Zero; viewHeight = Fixed.Zero; deltaViewHeight = Fixed.Zero; bob = Fixed.Zero; health = 0; armorPoints = 0; armorType = 0; Array.Clear(powers, 0, powers.Length); Array.Clear(cards, 0, cards.Length); backpack = false; Array.Clear(frags, 0, frags.Length); readyWeapon = WeaponType.Fist; pendingWeapon = WeaponType.Fist; Array.Clear(weaponOwned, 0, weaponOwned.Length); Array.Clear(ammo, 0, ammo.Length); Array.Clear(maxAmmo, 0, maxAmmo.Length); useDown = false; attackDown = false; cheats = (CheatFlags)0; refire = 0; killCount = 0; itemCount = 0; secretCount = 0; message = null; messageTime = 0; damageCount = 0; bonusCount = 0; attacker = null; extraLight = 0; fixedColorMap = 0; colorMap = 0; PlayerSpriteDef[] array = playerSprites; for (int i = 0; i < array.Length; i++) { array[i].Clear(); } didSecret = false; interpolate = false; oldViewZ = Fixed.Zero; oldAngle = Angle.Ang0; } public void Reborn() { mobj = null; playerState = PlayerState.Live; cmd.Clear(); viewZ = Fixed.Zero; viewHeight = Fixed.Zero; deltaViewHeight = Fixed.Zero; bob = Fixed.Zero; health = DoomInfo.DeHackEdConst.InitialHealth; armorPoints = 0; armorType = 0; Array.Clear(powers, 0, powers.Length); Array.Clear(cards, 0, cards.Length); backpack = false; readyWeapon = WeaponType.Pistol; pendingWeapon = WeaponType.Pistol; Array.Clear(weaponOwned, 0, weaponOwned.Length); Array.Clear(ammo, 0, ammo.Length); Array.Clear(maxAmmo, 0, maxAmmo.Length); weaponOwned[0] = true; weaponOwned[1] = true; ammo[0] = DoomInfo.DeHackEdConst.InitialBullets; for (int i = 0; i < 4; i++) { maxAmmo[i] = DoomInfo.AmmoInfos.Max[i]; } useDown = true; attackDown = true; cheats = (CheatFlags)0; refire = 0; message = null; messageTime = 0; damageCount = 0; bonusCount = 0; attacker = null; extraLight = 0; fixedColorMap = 0; colorMap = 0; PlayerSpriteDef[] array = playerSprites; for (int j = 0; j < array.Length; j++) { array[j].Clear(); } didSecret = false; interpolate = false; oldViewZ = Fixed.Zero; oldAngle = Angle.Ang0; } public void FinishLevel() { Array.Clear(powers, 0, powers.Length); Array.Clear(cards, 0, cards.Length); mobj.Flags &= ~MobjFlags.Shadow; extraLight = 0; fixedColorMap = 0; damageCount = 0; bonusCount = 0; } public void SendMessage(string message) { if ((object)this.message != (string)DoomInfo.Strings.MSGOFF || (object)message == (string)DoomInfo.Strings.MSGON) { this.message = message; messageTime = 4 * GameConst.TicRate; } } public void UpdateFrameInterpolationInfo() { interpolate = true; oldViewZ = viewZ; oldAngle = mobj.Angle; } public void DisableFrameInterpolationForOneFrame() { interpolate = false; } public Fixed GetInterpolatedViewZ(Fixed frameFrac) { if (interpolate && mobj.World.LevelTime > 1) { return oldViewZ + frameFrac * (viewZ - oldViewZ); } return viewZ; } public Angle GetInterpolatedAngle(Fixed frameFrac) { if (interpolate) { Angle angle = mobj.Angle - oldAngle; if (angle < Angle.Ang180) { return oldAngle + Angle.FromDegree(frameFrac.ToDouble() * angle.ToDegree()); } return oldAngle - Angle.FromDegree(frameFrac.ToDouble() * (360.0 - angle.ToDegree())); } return mobj.Angle; } } public enum PlayerState { Live, Dead, Reborn } public static class SaveAndLoad { private enum ThinkerClass { End, Mobj } private enum SpecialClass { Ceiling, Door, Floor, Plat, Flash, Strobe, Glow, EndSpecials } private class SaveGame { private byte[] data; private int ptr; public SaveGame(string description) { data = new byte[saveBufferSize]; ptr = 0; WriteDescription(description); WriteVersion(); } private void WriteDescription(string description) { for (int i = 0; i < description.Length; i++) { data[i] = (byte)description[i]; } ptr += DescriptionSize; } private void WriteVersion() { string text = "version 109"; for (int i = 0; i < text.Length; i++) { data[ptr + i] = (byte)text[i]; } ptr += versionSize; } public void Save(DoomGame game, string path) { GameOptions options = game.World.Options; data[ptr++] = (byte)options.Skill; data[ptr++] = (byte)options.Episode; data[ptr++] = (byte)options.Map; for (int i = 0; i < Player.MaxPlayerCount; i++) { data[ptr++] = (options.Players[i].InGame ? ((byte)1) : ((byte)0)); } data[ptr++] = (byte)(game.World.LevelTime >> 16); data[ptr++] = (byte)(game.World.LevelTime >> 8); data[ptr++] = (byte)game.World.LevelTime; ArchivePlayers(game.World); ArchiveWorld(game.World); ArchiveThinkers(game.World); ArchiveSpecials(game.World); data[ptr++] = 29; using FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write); fileStream.Write(data, 0, ptr); } private void PadPointer() { ptr += (4 - (ptr & 3)) & 3; } private void ArchivePlayers(World world) { Player[] players = world.Options.Players; for (int i = 0; i < Player.MaxPlayerCount; i++) { if (players[i].InGame) { PadPointer(); ptr = ArchivePlayer(players[i], data, ptr); } } } private void ArchiveWorld(World world) { Sector[] sectors = world.Map.Sectors; for (int i = 0; i < sectors.Length; i++) { ptr = ArchiveSector(sectors[i], data, ptr); } LineDef[] lines = world.Map.Lines; for (int j = 0; j < lines.Length; j++) { ptr = ArchiveLine(lines[j], data, ptr); } } private void ArchiveThinkers(World world) { foreach (Thinker thinker in world.Thinkers) { if (thinker is Mobj mobj) { data[ptr++] = 1; PadPointer(); WriteThinkerState(data, ptr + 8, mobj.ThinkerState); Write(data, ptr + 12, mobj.X.Data); Write(data, ptr + 16, mobj.Y.Data); Write(data, ptr + 20, mobj.Z.Data); Write(data, ptr + 32, mobj.Angle.Data); Write(data, ptr + 36, (int)mobj.Sprite); Write(data, ptr + 40, mobj.Frame); Write(data, ptr + 56, mobj.FloorZ.Data); Write(data, ptr + 60, mobj.CeilingZ.Data); Write(data, ptr + 64, mobj.Radius.Data); Write(data, ptr + 68, mobj.Height.Data); Write(data, ptr + 72, mobj.MomX.Data); Write(data, ptr + 76, mobj.MomY.Data); Write(data, ptr + 80, mobj.MomZ.Data); Write(data, ptr + 88, (int)mobj.Type); Write(data, ptr + 96, mobj.Tics); Write(data, ptr + 100, mobj.State.Number); Write(data, ptr + 104, (int)mobj.Flags); Write(data, ptr + 108, mobj.Health); Write(data, ptr + 112, (int)mobj.MoveDir); Write(data, ptr + 116, mobj.MoveCount); Write(data, ptr + 124, mobj.ReactionTime); Write(data, ptr + 128, mobj.Threshold); if (mobj.Player == null) { Write(data, ptr + 132, 0); } else { Write(data, ptr + 132, mobj.Player.Number + 1); } Write(data, ptr + 136, mobj.LastLook); if (mobj.SpawnPoint == null) { Write(data, ptr + 140, (short)0); Write(data, ptr + 142, (short)0); Write(data, ptr + 144, (short)0); Write(data, ptr + 146, (short)0); Write(data, ptr + 148, (short)0); } else { Write(data, ptr + 140, (short)mobj.SpawnPoint.X.ToIntFloor()); Write(data, ptr + 142, (short)mobj.SpawnPoint.Y.ToIntFloor()); Write(data, ptr + 144, (short)Math.Round(mobj.SpawnPoint.Angle.ToDegree())); Write(data, ptr + 146, (short)mobj.SpawnPoint.Type); Write(data, ptr + 148, (short)mobj.SpawnPoint.Flags); } ptr += 154; } } data[ptr++] = 0; } private void ArchiveSpecials(World world) { Thinkers thinkers = world.Thinkers; SectorAction sectorAction = world.SectorAction; foreach (Thinker item in thinkers) { if (item.ThinkerState == ThinkerState.InStasis) { CeilingMove ceilingMove = item as CeilingMove; if (sectorAction.CheckActiveCeiling(ceilingMove)) { data[ptr++] = 0; PadPointer(); WriteThinkerState(data, ptr + 8, ceilingMove.ThinkerState); Write(data, ptr + 12, (int)ceilingMove.Type); Write(data, ptr + 16, ceilingMove.Sector.Number); Write(data, ptr + 20, ceilingMove.BottomHeight.Data); Write(data, ptr + 24, ceilingMove.TopHeight.Data); Write(data, ptr + 28, ceilingMove.Speed.Data); Write(data, ptr + 32, ceilingMove.Crush ? 1 : 0); Write(data, ptr + 36, ceilingMove.Direction); Write(data, ptr + 40, ceilingMove.Tag); Write(data, ptr + 44, ceilingMove.OldDirection); ptr += 48; } } else if (item is CeilingMove ceilingMove2) { data[ptr++] = 0; PadPointer(); WriteThinkerState(data, ptr + 8, ceilingMove2.ThinkerState); Write(data, ptr + 12, (int)ceilingMove2.Type); Write(data, ptr + 16, ceilingMove2.Sector.Number); Write(data, ptr + 20, ceilingMove2.BottomHeight.Data); Write(data, ptr + 24, ceilingMove2.TopHeight.Data); Write(data, ptr + 28, ceilingMove2.Speed.Data); Write(data, ptr + 32, ceilingMove2.Crush ? 1 : 0); Write(data, ptr + 36, ceilingMove2.Direction); Write(data, ptr + 40, ceilingMove2.Tag); Write(data, ptr + 44, ceilingMove2.OldDirection); ptr += 48; } else if (item is VerticalDoor verticalDoor) { data[ptr++] = 1; PadPointer(); WriteThinkerState(data, ptr + 8, verticalDoor.ThinkerState); Write(data, ptr + 12, (int)verticalDoor.Type); Write(data, ptr + 16, verticalDoor.Sector.Number); Write(data, ptr + 20, verticalDoor.TopHeight.Data); Write(data, ptr + 24, verticalDoor.Speed.Data); Write(data, ptr + 28, verticalDoor.Direction); Write(data, ptr + 32, verticalDoor.TopWait); Write(data, ptr + 36, verticalDoor.TopCountDown); ptr += 40; } else if (item is FloorMove floorMove) { data[ptr++] = 2; PadPointer(); WriteThinkerState(data, ptr + 8, floorMove.ThinkerState); Write(data, ptr + 12, (int)floorMove.Type); Write(data, ptr + 16, floorMove.Crush ? 1 : 0); Write(data, ptr + 20, floorMove.Sector.Number); Write(data, ptr + 24, floorMove.Direction); Write(data, ptr + 28, (int)floorMove.NewSpecial); Write(data, ptr + 32, floorMove.Texture); Write(data, ptr + 36, floorMove.FloorDestHeight.Data); Write(data, ptr + 40, floorMove.Speed.Data); ptr += 44; } else if (item is Platform platform) { data[ptr++] = 3; PadPointer(); WriteThinkerState(data, ptr + 8, platform.ThinkerState); Write(data, ptr + 12, platform.Sector.Number); Write(data, ptr + 16, platform.Speed.Data); Write(data, ptr + 20, platform.Low.Data); Write(data, ptr + 24, platform.High.Data); Write(data, ptr + 28, platform.Wait); Write(data, ptr + 32, platform.Count); Write(data, ptr + 36, (int)platform.Status); Write(data, ptr + 40, (int)platform.OldStatus); Write(data, ptr + 44, platform.Crush ? 1 : 0); Write(data, ptr + 48, platform.Tag); Write(data, ptr + 52, (int)platform.Type); ptr += 56; } else if (item is LightFlash lightFlash) { data[ptr++] = 4; PadPointer(); WriteThinkerState(data, ptr + 8, lightFlash.ThinkerState); Write(data, ptr + 12, lightFlash.Sector.Number); Write(data, ptr + 16, lightFlash.Count); Write(data, ptr + 20, lightFlash.MaxLight); Write(data, ptr + 24, lightFlash.MinLight); Write(data, ptr + 28, lightFlash.MaxTime); Write(data, ptr + 32, lightFlash.MinTime); ptr += 36; } else if (item is StrobeFlash strobeFlash) { data[ptr++] = 5; PadPointer(); WriteThinkerState(data, ptr + 8, strobeFlash.ThinkerState); Write(data, ptr + 12, strobeFlash.Sector.Number); Write(data, ptr + 16, strobeFlash.Count); Write(data, ptr + 20, strobeFlash.MinLight); Write(data, ptr + 24, strobeFlash.MaxLight); Write(data, ptr + 28, strobeFlash.DarkTime); Write(data, ptr + 32, strobeFlash.BrightTime); ptr += 36; } else if (item is GlowingLight glowingLight) { data[ptr++] = 6; PadPointer(); WriteThinkerState(data, ptr + 8, glowingLight.ThinkerState); Write(data, ptr + 12, glowingLight.Sector.Number); Write(data, ptr + 16, glowingLight.MinLight); Write(data, ptr + 20, glowingLight.MaxLight); Write(data, ptr + 24, glowingLight.Direction); ptr += 28; } } data[ptr++] = 7; } private static int ArchivePlayer(Player player, byte[] data, int p) { Write(data, p + 4, (int)player.PlayerState); Write(data, p + 16, player.ViewZ.Data); Write(data, p + 20, player.ViewHeight.Data); Write(data, p + 24, player.DeltaViewHeight.Data); Write(data, p + 28, player.Bob.Data); Write(data, p + 32, player.Health); Write(data, p + 36, player.ArmorPoints); Write(data, p + 40, player.ArmorType); for (int i = 0; i < 6; i++) { Write(data, p + 44 + 4 * i, player.Powers[i]); } for (int j = 0; j < 6; j++) { Write(data, p + 68 + 4 * j, player.Cards[j] ? 1 : 0); } Write(data, p + 92, player.Backpack ? 1 : 0); for (int k = 0; k < Player.MaxPlayerCount; k++) { Write(data, p + 96 + 4 * k, player.Frags[k]); } Write(data, p + 112, (int)player.ReadyWeapon); Write(data, p + 116, (int)player.PendingWeapon); for (int l = 0; l < 9; l++) { Write(data, p + 120 + 4 * l, player.WeaponOwned[l] ? 1 : 0); } for (int m = 0; m < 4; m++) { Write(data, p + 156 + 4 * m, player.Ammo[m]); } for (int n = 0; n < 4; n++) { Write(data, p + 172 + 4 * n, player.MaxAmmo[n]); } Write(data, p + 188, player.AttackDown ? 1 : 0); Write(data, p + 192, player.UseDown ? 1 : 0); Write(data, p + 196, (int)player.Cheats); Write(data, p + 200, player.Refire); Write(data, p + 204, player.KillCount); Write(data, p + 208, player.ItemCount); Write(data, p + 212, player.SecretCount); Write(data, p + 220, player.DamageCount); Write(data, p + 224, player.BonusCount); Write(data, p + 232, player.ExtraLight); Write(data, p + 236, player.FixedColorMap); Write(data, p + 240, player.ColorMap); for (int num = 0; num < 2; num++) { if (player.PlayerSprites[num].State == null) { Write(data, p + 244 + 16 * num, 0); } else { Write(data, p + 244 + 16 * num, player.PlayerSprites[num].State.Number); } Write(data, p + 244 + 16 * num + 4, player.PlayerSprites[num].Tics); Write(data, p + 244 + 16 * num + 8, player.PlayerSprites[num].Sx.Data); Write(data, p + 244 + 16 * num + 12, player.PlayerSprites[num].Sy.Data); } Write(data, p + 276, player.DidSecret ? 1 : 0); return p + 280; } private static int ArchiveSector(Sector sector, byte[] data, int p) { Write(data, p, (short)sector.FloorHeight.ToIntFloor()); Write(data, p + 2, (short)sector.CeilingHeight.ToIntFloor()); Write(data, p + 4, (short)sector.FloorFlat); Write(data, p + 6, (short)sector.CeilingFlat); Write(data, p + 8, (short)sector.LightLevel); Write(data, p + 10, (short)sector.Special); Write(data, p + 12, (short)sector.Tag); return p + 14; } private static int ArchiveLine(LineDef line, byte[] data, int p) { Write(data, p, (short)line.Flags); Write(data, p + 2, (short)line.Special); Write(data, p + 4, line.Tag); p += 6; if (line.FrontSide != null) { SideDef frontSide = line.FrontSide; Write(data, p, (short)frontSide.TextureOffset.ToIntFloor()); Write(data, p + 2, (short)frontSide.RowOffset.ToIntFloor()); Write(data, p + 4, (short)frontSide.TopTexture); Write(data, p + 6, (short)frontSide.BottomTexture); Write(data, p + 8, (short)frontSide.MiddleTexture); p += 10; } if (line.BackSide != null) { SideDef backSide = line.BackSide; Write(data, p, (short)backSide.TextureOffset.ToIntFloor()); Write(data, p + 2, (short)backSide.RowOffset.ToIntFloor()); Write(data, p + 4, (short)backSide.TopTexture); Write(data, p + 6, (short)backSide.BottomTexture); Write(data, p + 8, (short)backSide.MiddleTexture); p += 10; } return p; } private static void Write(byte[] data, int p, int value) { data[p] = (byte)value; data[p + 1] = (byte)(value >> 8); data[p + 2] = (byte)(value >> 16); data[p + 3] = (byte)(value >> 24); } private static void Write(byte[] data, int p, uint value) { data[p] = (byte)value; data[p + 1] = (byte)(value >> 8); data[p + 2] = (byte)(value >> 16); data[p + 3] = (byte)(value >> 24); } private static void Write(byte[] data, int p, short value) { data[p] = (byte)value; data[p + 1] = (byte)(value >> 8); } private static void WriteThinkerState(byte[] data, int p, ThinkerState state) { if (state == ThinkerState.InStasis) { Write(data, p, 0); } else { Write(data, p, 1); } } } private class LoadGame { private byte[] data; private int ptr; public LoadGame(byte[] data) { this.data = data; ptr = 0; ReadDescription(); if (ReadVersion() != "VERSION 109") { throw new Exception("Unsupported version!"); } } public void Load(DoomGame game) { GameOptions options = game.World.Options; options.Skill = (GameSkill)data[ptr++]; options.Episode = data[ptr++]; options.Map = data[ptr++]; for (int i = 0; i < Player.MaxPlayerCount; i++) { options.Players[i].InGame = data[ptr++] != 0; } game.InitNew(options.Skill, options.Episode, options.Map); byte num = data[ptr++]; byte b = data[ptr++]; byte b2 = data[ptr++]; int levelTime = (num << 16) + (b << 8) + b2; UnArchivePlayers(game.World); UnArchiveWorld(game.World); UnArchiveThinkers(game.World); UnArchiveSpecials(game.World); if (data[ptr] != 29) { throw new Exception("Bad savegame!"); } game.World.LevelTime = levelTime; options.Sound.SetListener(game.World.ConsolePlayer.Mobj); } private void PadPointer() { ptr += (4 - (ptr & 3)) & 3; } private string ReadDescription() { string result = DoomInterop.ToString(data, ptr, DescriptionSize); ptr += DescriptionSize; return result; } private string ReadVersion() { string result = DoomInterop.ToString(data, ptr, versionSize); ptr += versionSize; return result; } private void UnArchivePlayers(World world) { Player[] players = world.Options.Players; for (int i = 0; i < Player.MaxPlayerCount; i++) { if (players[i].InGame) { PadPointer(); ptr = UnArchivePlayer(players[i], data, ptr); } } } private void UnArchiveWorld(World world) { Sector[] sectors = world.Map.Sectors; for (int i = 0; i < sectors.Length; i++) { ptr = UnArchiveSector(sectors[i], data, ptr); } LineDef[] lines = world.Map.Lines; for (int j = 0; j < lines.Length; j++) { ptr = UnArchiveLine(lines[j], data, ptr); } } private void UnArchiveThinkers(World world) { Thinkers thinkers = world.Thinkers; ThingAllocation thingAllocation = world.ThingAllocation; foreach (Thinker item in thinkers) { if (item is Mobj mobj) { thingAllocation.RemoveMobj(mobj); } } thinkers.Reset(); while (true) { Mobj mobj2; switch ((ThinkerClass)data[ptr++]) { case ThinkerClass.End: return; case ThinkerClass.Mobj: { PadPointer(); mobj2 = new Mobj(world); mobj2.ThinkerState = ReadThinkerState(data, ptr + 8); mobj2.X = new Fixed(BitConverter.ToInt32(data, ptr + 12)); mobj2.Y = new Fixed(BitConverter.ToInt32(data, ptr + 16)); mobj2.Z = new Fixed(BitConverter.ToInt32(data, ptr + 20)); mobj2.Angle = new Angle(BitConverter.ToInt32(data, ptr + 32)); mobj2.Sprite = (Sprite)BitConverter.ToInt32(data, ptr + 36); mobj2.Frame = BitConverter.ToInt32(data, ptr + 40); mobj2.FloorZ = new Fixed(BitConverter.ToInt32(data, ptr + 56)); mobj2.CeilingZ = new Fixed(BitConverter.ToInt32(data, ptr + 60)); mobj2.Radius = new Fixed(BitConverter.ToInt32(data, ptr + 64)); mobj2.Height = new Fixed(BitConverter.ToInt32(data, ptr + 68)); mobj2.MomX = new Fixed(BitConverter.ToInt32(data, ptr + 72)); mobj2.MomY = new Fixed(BitConverter.ToInt32(data, ptr + 76)); mobj2.MomZ = new Fixed(BitConverter.ToInt32(data, ptr + 80)); mobj2.Type = (MobjType)BitConverter.ToInt32(data, ptr + 88); mobj2.Info = DoomInfo.MobjInfos[(int)mobj2.Type]; mobj2.Tics = BitConverter.ToInt32(data, ptr + 96); mobj2.State = DoomInfo.States[BitConverter.ToInt32(data, ptr + 100)]; mobj2.Flags = (MobjFlags)BitConverter.ToInt32(data, ptr + 104); mobj2.Health = BitConverter.ToInt32(data, ptr + 108); mobj2.MoveDir = (Direction)BitConverter.ToInt32(data, ptr + 112); mobj2.MoveCount = BitConverter.ToInt32(data, ptr + 116); mobj2.ReactionTime = BitConverter.ToInt32(data, ptr + 124); mobj2.Threshold = BitConverter.ToInt32(data, ptr + 128); int num = BitConverter.ToInt32(data, ptr + 132); if (num != 0) { mobj2.Player = world.Options.Players[num - 1]; mobj2.Player.Mobj = mobj2; } break; } default: throw new Exception("Unknown thinker class in savegame!"); } mobj2.LastLook = BitConverter.ToInt32(data, ptr + 136); mobj2.SpawnPoint = new MapThing(Fixed.FromInt(BitConverter.ToInt16(data, ptr + 140)), Fixed.FromInt(BitConverter.ToInt16(data, ptr + 142)), new Angle(Angle.Ang45.Data * (uint)(BitConverter.ToInt16(data, ptr + 144) / 45)), BitConverter.ToInt16(data, ptr + 146), (ThingFlags)BitConverter.ToInt16(data, ptr + 148)); ptr += 154; world.ThingMovement.SetThingPosition(mobj2); thinkers.Add(mobj2); } } private void UnArchiveSpecials(World world) { Thinkers thinkers = world.Thinkers; SectorAction sectorAction = world.SectorAction; while (true) { switch ((SpecialClass)data[ptr++]) { case SpecialClass.EndSpecials: return; case SpecialClass.Ceiling: { PadPointer(); CeilingMove ceilingMove = new CeilingMove(world); ceilingMove.ThinkerState = ReadThinkerState(data, ptr + 8); ceilingMove.Type = (CeilingMoveType)BitConverter.ToInt32(data, ptr + 12); ceilingMove.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 16)]; ceilingMove.Sector.SpecialData = ceilingMove; ceilingMove.BottomHeight = new Fixed(BitConverter.ToInt32(data, ptr + 20)); ceilingMove.TopHeight = new Fixed(BitConverter.ToInt32(data, ptr + 24)); ceilingMove.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 28)); ceilingMove.Crush = BitConverter.ToInt32(data, ptr + 32) != 0; ceilingMove.Direction = BitConverter.ToInt32(data, ptr + 36); ceilingMove.Tag = BitConverter.ToInt32(data, ptr + 40); ceilingMove.OldDirection = BitConverter.ToInt32(data, ptr + 44); ptr += 48; thinkers.Add(ceilingMove); sectorAction.AddActiveCeiling(ceilingMove); break; } case SpecialClass.Door: { PadPointer(); VerticalDoor verticalDoor = new VerticalDoor(world); verticalDoor.ThinkerState = ReadThinkerState(data, ptr + 8); verticalDoor.Type = (VerticalDoorType)BitConverter.ToInt32(data, ptr + 12); verticalDoor.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 16)]; verticalDoor.Sector.SpecialData = verticalDoor; verticalDoor.TopHeight = new Fixed(BitConverter.ToInt32(data, ptr + 20)); verticalDoor.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 24)); verticalDoor.Direction = BitConverter.ToInt32(data, ptr + 28); verticalDoor.TopWait = BitConverter.ToInt32(data, ptr + 32); verticalDoor.TopCountDown = BitConverter.ToInt32(data, ptr + 36); ptr += 40; thinkers.Add(verticalDoor); break; } case SpecialClass.Floor: { PadPointer(); FloorMove floorMove = new FloorMove(world); floorMove.ThinkerState = ReadThinkerState(data, ptr + 8); floorMove.Type = (FloorMoveType)BitConverter.ToInt32(data, ptr + 12); floorMove.Crush = BitConverter.ToInt32(data, ptr + 16) != 0; floorMove.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 20)]; floorMove.Sector.SpecialData = floorMove; floorMove.Direction = BitConverter.ToInt32(data, ptr + 24); floorMove.NewSpecial = (SectorSpecial)BitConverter.ToInt32(data, ptr + 28); floorMove.Texture = BitConverter.ToInt32(data, ptr + 32); floorMove.FloorDestHeight = new Fixed(BitConverter.ToInt32(data, ptr + 36)); floorMove.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 40)); ptr += 44; thinkers.Add(floorMove); break; } case SpecialClass.Plat: { PadPointer(); Platform platform = new Platform(world); platform.ThinkerState = ReadThinkerState(data, ptr + 8); platform.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 12)]; platform.Sector.SpecialData = platform; platform.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 16)); platform.Low = new Fixed(BitConverter.ToInt32(data, ptr + 20)); platform.High = new Fixed(BitConverter.ToInt32(data, ptr + 24)); platform.Wait = BitConverter.ToInt32(data, ptr + 28); platform.Count = BitConverter.ToInt32(data, ptr + 32); platform.Status = (PlatformState)BitConverter.ToInt32(data, ptr + 36); platform.OldStatus = (PlatformState)BitConverter.ToInt32(data, ptr + 40); platform.Crush = BitConverter.ToInt32(data, ptr + 44) != 0; platform.Tag = BitConverter.ToInt32(data, ptr + 48); platform.Type = (PlatformType)BitConverter.ToInt32(data, ptr + 52); ptr += 56; thinkers.Add(platform); sectorAction.AddAc