Decompiled source of LC DOOM v1.1.2
LethalCompany.Doom.dll
Decompiled 11 months 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 11 months 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