diff --git a/SRC/Aura_Boot/Aura_Boot.csproj b/SRC/Aura_Boot/Aura_Boot.csproj index 45453b67..0eea096d 100644 --- a/SRC/Aura_Boot/Aura_Boot.csproj +++ b/SRC/Aura_Boot/Aura_Boot.csproj @@ -12,7 +12,7 @@ Source False bin\Debug\net6.0\Aura_Boot.iso - True + False False VMware Pipe: Cosmos\Serial diff --git a/SRC/Aura_OS/Aura_OS.csproj b/SRC/Aura_OS/Aura_OS.csproj index cf8351d1..8cc1058d 100644 --- a/SRC/Aura_OS/Aura_OS.csproj +++ b/SRC/Aura_OS/Aura_OS.csproj @@ -24,10 +24,12 @@ + + diff --git a/SRC/Aura_OS/Files.cs b/SRC/Aura_OS/Files.cs index 86d7e1b7..121416a3 100644 --- a/SRC/Aura_OS/Files.cs +++ b/SRC/Aura_OS/Files.cs @@ -21,6 +21,9 @@ public class Files [ManifestResourceStream(ResourceName = "Aura_OS.Resources.wallpaper1920.bmp")] public static byte[] Wallpaper; + [ManifestResourceStream(ResourceName = "Aura_OS.Resources.doom1.wad")] + public static byte[] DoomWad; + public static void LoadFiles() { CustomConsole.WriteLineInfo("Checking for ISO9660 volume..."); diff --git a/SRC/Aura_OS/Kernel.cs b/SRC/Aura_OS/Kernel.cs index 3e696413..31fe8b0d 100644 --- a/SRC/Aura_OS/Kernel.cs +++ b/SRC/Aura_OS/Kernel.cs @@ -181,8 +181,6 @@ public static void BeforeRun() Running = true; } - private static int lastHeapCollectTime = -1; - public static void Run() { try @@ -192,18 +190,12 @@ public static void Run() _fps = _frames; _frames = 0; _deltaT = RTC.Second; - - lastHeapCollectTime++; - - if (lastHeapCollectTime >= 3) - { - FreeCount = Heap.Collect(); - lastHeapCollectTime = 0; - } } _frames++; + FreeCount = Heap.Collect(); + UpdateUI(); canvas.Display(); diff --git a/SRC/Aura_OS/Properties/VersionInfo.cs b/SRC/Aura_OS/Properties/VersionInfo.cs index 2b82cf75..aa85d742 100644 --- a/SRC/Aura_OS/Properties/VersionInfo.cs +++ b/SRC/Aura_OS/Properties/VersionInfo.cs @@ -2,6 +2,6 @@ namespace Aura_OS { public class VersionInfo { - public static string revision = "090120241014"; + public static string revision = "150120241337"; } } diff --git a/SRC/Aura_OS/Resources/doom1.wad b/SRC/Aura_OS/Resources/doom1.wad new file mode 100644 index 00000000..1a58f662 Binary files /dev/null and b/SRC/Aura_OS/Resources/doom1.wad differ diff --git a/SRC/Aura_OS/System/Processing/Application/Emulators/GameBoyEmu/Utils/DirectBitmap.cs b/SRC/Aura_OS/System/Graphics/UI/GUI/Components/DirectBitmap.cs similarity index 70% rename from SRC/Aura_OS/System/Processing/Application/Emulators/GameBoyEmu/Utils/DirectBitmap.cs rename to SRC/Aura_OS/System/Graphics/UI/GUI/Components/DirectBitmap.cs index aa0a857b..8a02b5ec 100644 --- a/SRC/Aura_OS/System/Processing/Application/Emulators/GameBoyEmu/Utils/DirectBitmap.cs +++ b/SRC/Aura_OS/System/Graphics/UI/GUI/Components/DirectBitmap.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using Cosmos.System.Graphics; -namespace Aura_OS.System.Processing.Application.Emulators.GameBoyEmu.Utils +namespace Aura_OS.System.Graphics.UI.GUI.Components { public class DirectBitmap { @@ -17,6 +17,18 @@ public DirectBitmap() Bitmap = new Bitmap((uint)Width, (uint)Height, ColorDepth.ColorDepth32); } + public DirectBitmap(byte[] data) + { + Bitmap = new Bitmap(data); + } + + public DirectBitmap(int width, int height) + { + Width = width; + Height = height; + Bitmap = new Bitmap((uint)Width, (uint)Height, ColorDepth.ColorDepth32); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetPixel(int x, int y, int colour) { diff --git a/SRC/Aura_OS/System/Processing/Application/ApplicationManager.cs b/SRC/Aura_OS/System/Processing/Application/ApplicationManager.cs index 2f453d19..fb59ab10 100644 --- a/SRC/Aura_OS/System/Processing/Application/ApplicationManager.cs +++ b/SRC/Aura_OS/System/Processing/Application/ApplicationManager.cs @@ -49,6 +49,7 @@ public void LoadApplications() RegisterApplication(typeof(SystemInfo), 40, 40, 402, 360); RegisterApplication(typeof(Cube), 40, 40, 200, 200); RegisterApplication(typeof(GameBoyEmu), 40, 40, 160 + 4, 144 + 22); + RegisterApplication(typeof(DoomApp), 40, 40, 320 + 4, 200 + 22 + 200); } public void RegisterApplication(ApplicationConfig config) @@ -92,6 +93,10 @@ public Graphics.UI.GUI.Application Instantiate(ApplicationConfig config) { app = new Explorer(Kernel.CurrentVolume, config.Weight, config.Height, config.X, config.Y); } + else if (config.Template == typeof(DoomApp)) + { + app = new DoomApp(config.Weight, config.Height, config.X, config.Y); + } else { throw new InvalidOperationException("Type d'application non reconnu."); diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/Debugger.cs b/SRC/Aura_OS/System/Processing/Application/Doom/Debugger.cs new file mode 100644 index 00000000..be4b3e1e --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/Debugger.cs @@ -0,0 +1,26 @@ +using Aura_OS.System.Graphics.UI.GUI.Components; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Aura_OS.System.Processing.Application.Doom +{ + public class Debugger + { + public string Text = ""; + + public Debugger() { } + + public void Write(string message) + { + Text += message; + } + + public void WriteLine(string message) + { + Text += message += "\n"; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/Doom.cs b/SRC/Aura_OS/System/Processing/Application/Doom/Doom.cs new file mode 100644 index 00000000..646fce46 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/Doom.cs @@ -0,0 +1,100 @@ +/* +* PROJECT: Aura Operating System Development +* CONTENT: Doom +* PROGRAMMERS: Valentin Charbonnier +*/ + +using Aura_OS.System.Graphics; +using Aura_OS.System.Graphics.UI.GUI.Components; +using Aura_OS.System.Processing.Application.Doom; +using Cosmos.System; +using Cosmos.System.FileSystem; +using System; +using System.IO; + +namespace Aura_OS.System.Processing.Application +{ + public class DoomApp : Graphics.UI.GUI.Application + { + public static string ApplicationName = "Doom"; + + public Panel TopPanel; + public Button Screenshot; + + static ManagedDoom.DoomApplication app = null; + + public static Debugger debugger = new Debugger(); + + bool calledAfterRender = false; + + static float framesPerSecond; + + private string[] args = { }; + private string[] configLines = { }; + + public DoomApp(int width, int height, int x = 0, int y = 0) : base(ApplicationName, width, height, x, y) + { + TopPanel = new Panel(Kernel.Gray, x + 1, y + 1, width - 6, 22); + TopPanel.Borders = true; + + string text = "Screenshot"; + int textWidth = text.Length * (Kernel.font.Width + 1); + Screenshot = new Button(text, x + 3, y + 3, textWidth, 18); + Screenshot.Action = new Action(() => + { + File.Create(Kernel.CurrentDirectory + "screenshot.bmp"); + app.renderer.bitmap.Bitmap.Save(Kernel.CurrentDirectory + "screenshot.bmp"); + }); + + app = null; + var commandLineArgs = new ManagedDoom.CommandLineArgs(args); + app = new ManagedDoom.DoomApplication(commandLineArgs, configLines); + } + + public override void UpdateApp() + { + app.renderer.X = x; + app.renderer.Y = y + 23; + + if (app == null) + { + return; + } + + uint[] upKeys = { }; + uint[] downKeys = { }; + + app.Run(upKeys, downKeys); + + string[] lines = debugger.Text.Split('\n'); + int maxLinesToShow = 17; + int startIndex = Math.Max(0, lines.Length - maxLinesToShow); // Ensure you don't go below 0 + int dy = 0; + + for (int i = startIndex; i < lines.Length; i++) + { + string line = lines[i]; + Kernel.canvas.DrawString(line, Kernel.font, Kernel.BlackColor, x + 2, y + 23 + 200 + 4 + dy); + dy += 12; + } + + TopPanel.X = x + 1; + TopPanel.Y = y + 1; + TopPanel.Update(); + Screenshot.X = x + 3; + Screenshot.Y = y + 3; + Screenshot.Update(); + } + + public override void HandleLeftClick() + { + base.HandleLeftClick(); + + if (Screenshot.IsInside((int)MouseManager.X, (int)MouseManager.Y)) + { + Screenshot.Action(); + return; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/ApplicationInfo.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/ApplicationInfo.cs new file mode 100644 index 00000000..e415d20e --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/ApplicationInfo.cs @@ -0,0 +1,26 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static class ApplicationInfo + { + public static readonly string Title = "Managed Doom v1.1b"; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/ApplicationState.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/ApplicationState.cs new file mode 100644 index 00000000..c76e9abd --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/ApplicationState.cs @@ -0,0 +1,29 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum ApplicationState + { + None, + Opening, + DemoPlayback, + Game + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/Bgm.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/Bgm.cs new file mode 100644 index 00000000..e31e0a3a --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/Bgm.cs @@ -0,0 +1,93 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + 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 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/IMusic.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/IMusic.cs new file mode 100644 index 00000000..b0ed5e55 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/IMusic.cs @@ -0,0 +1,29 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.Audio +{ + public interface IMusic + { + void StartMusic(Bgm bgm, bool loop); + + public int MaxVolume { get; } + public int Volume { get; set; } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/ISound.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/ISound.cs new file mode 100644 index 00000000..9ffda617 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/ISound.cs @@ -0,0 +1,37 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.Audio +{ + public interface ISound + { + public void SetListener(Mobj listener); + public void Update(); + public void StartSound(Sfx sfx); + public void StartSound(Mobj mobj, Sfx sfx, SfxType type); + public void StartSound(Mobj mobj, Sfx sfx, SfxType type, int volume); + public void StopSound(Mobj mobj); + public void Reset(); + public void Pause(); + public void Resume(); + + public int MaxVolume { get; } + public int Volume { get; set; } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/NullMusic.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/NullMusic.cs new file mode 100644 index 00000000..43393139 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/NullMusic.cs @@ -0,0 +1,60 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.Audio +{ + public sealed class NullMusic : IMusic + { + private static NullMusic instance; + + public static NullMusic GetInstance() + { + if (instance == null) + { + instance = new NullMusic(); + } + + return instance; + } + + public void StartMusic(Bgm bgm, bool loop) + { + } + + public int MaxVolume + { + get + { + return 15; + } + } + + public int Volume + { + get + { + return 0; + } + + set + { + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/NullSound.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/NullSound.cs new file mode 100644 index 00000000..9805e856 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/NullSound.cs @@ -0,0 +1,92 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.Audio +{ + public sealed class NullSound : ISound + { + private static NullSound instance; + + public static NullSound GetInstance() + { + if (instance == null) + { + instance = new NullSound(); + } + + return instance; + } + + public void SetListener(Mobj listerner) + { + } + + public void Update() + { + } + + public void StartSound(Sfx sfx) + { + } + + public void StartSound(Mobj mobj, Sfx sfx, SfxType type) + { + } + + public void StartSound(Mobj mobj, Sfx sfx, SfxType type, int volume) + { + } + + public void StopSound(Mobj mobj) + { + } + + public void Reset() + { + } + + public void Pause() + { + } + + public void Resume() + { + } + + public int MaxVolume + { + get + { + return 15; + } + } + + public int Volume + { + get + { + return 0; + } + + set + { + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/Sfx.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/Sfx.cs new file mode 100644 index 00000000..1d57cfe5 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/Sfx.cs @@ -0,0 +1,134 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + 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 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/SfxType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/SfxType.cs new file mode 100644 index 00000000..59dc98c8 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Audio/SfxType.cs @@ -0,0 +1,30 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum SfxType + { + Diffuse, + Weapon, + Voice, + Footstep, + Misc + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/CommandLineArgs.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/CommandLineArgs.cs new file mode 100644 index 00000000..0e3e7123 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/CommandLineArgs.cs @@ -0,0 +1,237 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace ManagedDoom +{ + public sealed class CommandLineArgs + { + public readonly Arg iwad; + public readonly Arg file; + public readonly Arg deh; + + public readonly Arg> warp; + public readonly Arg skill; + + public readonly Arg deathmatch; + public readonly Arg altdeath; + public readonly Arg fast; + public readonly Arg respawn; + public readonly Arg nomonsters; + + public readonly Arg playdemo; + public readonly Arg timedemo; + + public readonly Arg loadgame; + + public readonly Arg nomouse; + public readonly Arg nosound; + public readonly Arg nosfx; + public readonly Arg nomusic; + + public CommandLineArgs(string[] args) + { + iwad = GetString(args, "-iwad"); + file = Check_file(args); + deh = Check_deh(args); + + warp = Check_warp(args); + 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")); + + 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")); + } + + private static Arg Check_file(string[] args) + { + // PWAD file paths can be specified without "-file" option for drag & drop support. + if (args.Length > 0 && args.All(arg => arg.FirstOrDefault() != '-')) + { + var values = args.Where(arg => Path.GetExtension(arg).ToLower() == ".wad").ToArray(); + if (values.Length >= 1) + { + return new Arg(values); + } + } + else + { + var values = GetValues(args, "-file"); + if (values.Length >= 1) + { + return new Arg(values); + } + } + + return new Arg(); + } + + private static Arg Check_deh(string[] args) + { + // DEH file paths can be specified without "-deh" option for drag & drop support. + if (args.Length > 0 && args.All(arg => arg.FirstOrDefault() != '-')) + { + var values = args.Where(arg => Path.GetExtension(arg).ToLower() == ".deh").ToArray(); + if (values.Length >= 1) + { + return new Arg(values); + } + } + else + { + var values = GetValues(args, "-deh"); + if (values.Length >= 1) + { + return new Arg(values); + } + } + + return new Arg(); + } + + private static Arg> Check_warp(string[] args) + { + var values = GetValues(args, "-warp"); + if (values.Length == 1) + { + int map; + if (int.TryParse(values[0], out map)) + { + return new Arg>(Tuple.Create(1, map)); + } + } + else if (values.Length == 2) + { + int episode; + int map; + if (int.TryParse(values[0], out episode) && int.TryParse(values[1], out map)) + { + return new Arg>(Tuple.Create(episode, map)); + } + } + + return new Arg>(); + } + + private static Arg GetString(string[] args, string name) + { + var values = GetValues(args, name); + if (values.Length == 1) + { + return new Arg(values[0]); + } + + return new Arg(); + } + + private static Arg GetInt(string[] args, string name) + { + var values = GetValues(args, name); + if (values.Length == 1) + { + int result; + if (int.TryParse(values[0], out result)) + { + return new Arg(result); + } + } + + return new Arg(); + } + + private static string[] GetValues(string[] args, string name) + { + List result = new List(); + bool nameFound = false; + + foreach (string arg in args) + { + if (!nameFound) + { + if (arg == name) + { + nameFound = true; + } + } + else + { + if (arg.StartsWith("-")) + { + break; + } + result.Add(arg); + } + } + + return result.ToArray(); + } + + public class Arg + { + private bool present; + + public Arg() + { + this.present = false; + } + + public Arg(bool present) + { + this.present = present; + } + + public bool Present => present; + } + + public class Arg + { + private bool present; + private T value; + + public Arg() + { + this.present = false; + this.value = default; + } + + public Arg(T value) + { + this.present = true; + this.value = value; + } + + public bool Present => present; + public T Value => value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Config.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Config.cs new file mode 100644 index 00000000..b972492c --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Config.cs @@ -0,0 +1,284 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace ManagedDoom +{ + 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 audio_soundvolume; + public int audio_musicvolume; + public bool audio_randompitch; + + // Default settings. + public Config() + { + key_forward = new KeyBinding( + new DoomKey[] + { + DoomKey.Up, + DoomKey.W + }); + key_backward = new KeyBinding( + new DoomKey[] + { + DoomKey.Down, + DoomKey.S + }); + key_strafeleft = new KeyBinding( + new DoomKey[] + { + DoomKey.A + }); + key_straferight = new KeyBinding( + new DoomKey[] + { + DoomKey.D + }); + key_turnleft = new KeyBinding( + new DoomKey[] + { + DoomKey.Left + }); + key_turnright = new KeyBinding( + new DoomKey[] + { + DoomKey.Right + }); + key_fire = new KeyBinding( + new DoomKey[] + { + DoomKey.ControlKey, + DoomKey.LControl, + DoomKey.RControl + }, + new DoomMouseButton[] + { + DoomMouseButton.Mouse1 + }); + key_use = new KeyBinding( + new DoomKey[] + { + DoomKey.Space + }, + new DoomMouseButton[] + { + DoomMouseButton.Mouse2 + }); + key_run = new KeyBinding( + new DoomKey[] + { + DoomKey.LShift, + DoomKey.RShift, + DoomKey.ShiftKey + }); + key_strafe = new KeyBinding( + new DoomKey[] + { + DoomKey.LAlt, + DoomKey.RAlt, + DoomKey.Alt + }); + + mouse_sensitivity = 3; + mouse_disableyaxis = false; + + game_alwaysrun = true; + + // TODO: do not use const values + video_screenwidth = 320; + video_screenheight = 200; + video_fullscreen = false; + video_highresolution = false; + video_gamescreensize = 7; + video_displaymessage = true; + video_gammacorrection = 0; + + audio_soundvolume = 8; + audio_musicvolume = 8; + audio_randompitch = true; + } + + public Config(string[] configLines) : this() + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Restore settings: "); + + var dic = new Dictionary(); + foreach (var line in configLines) + { + var split = line.Split('=', StringSplitOptions.RemoveEmptyEntries); + if (split.Length == 2) + { + dic[split[0].Trim()] = split[1].Trim(); + } + } + + key_forward = GetKeyBinding(dic, nameof(key_forward), key_forward); + key_backward = GetKeyBinding(dic, nameof(key_backward), key_backward); + key_strafeleft = GetKeyBinding(dic, nameof(key_strafeleft), key_strafeleft); + key_straferight = GetKeyBinding(dic, nameof(key_straferight), key_straferight); + key_turnleft = GetKeyBinding(dic, nameof(key_turnleft), key_turnleft); + key_turnright = GetKeyBinding(dic, nameof(key_turnright), key_turnright); + key_fire = GetKeyBinding(dic, nameof(key_fire), key_fire); + key_use = GetKeyBinding(dic, nameof(key_use), key_use); + key_run = GetKeyBinding(dic, nameof(key_run), key_run); + key_strafe = GetKeyBinding(dic, nameof(key_strafe), key_strafe); + + mouse_sensitivity = GetInt(dic, nameof(mouse_sensitivity), mouse_sensitivity); + mouse_disableyaxis = GetBool(dic, nameof(mouse_disableyaxis), mouse_disableyaxis); + + game_alwaysrun = GetBool(dic, nameof(game_alwaysrun), game_alwaysrun); + + video_screenwidth = GetInt(dic, nameof(video_screenwidth), video_screenwidth); + video_screenheight = GetInt(dic, nameof(video_screenheight), video_screenheight); + video_fullscreen = GetBool(dic, nameof(video_fullscreen), video_fullscreen); + video_highresolution = GetBool(dic, nameof(video_highresolution), video_highresolution); + video_displaymessage = GetBool(dic, nameof(video_displaymessage), video_displaymessage); + video_gamescreensize = GetInt(dic, nameof(video_gamescreensize), video_gamescreensize); + video_gammacorrection = GetInt(dic, nameof(video_gammacorrection), video_gammacorrection); + + audio_soundvolume = GetInt(dic, nameof(audio_soundvolume), audio_soundvolume); + audio_musicvolume = GetInt(dic, nameof(audio_musicvolume), audio_musicvolume); + audio_randompitch = GetBool(dic, nameof(audio_randompitch), audio_randompitch); + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK"); + } + catch + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + } + } + + public void Save(string path) + { + try + { + using (var writer = new StreamWriter(path)) + { + writer.WriteLine(nameof(key_forward) + " = " + key_forward); + writer.WriteLine(nameof(key_strafeleft) + " = " + key_strafeleft); + writer.WriteLine(nameof(key_straferight) + " = " + key_straferight); + writer.WriteLine(nameof(key_turnleft) + " = " + key_turnleft); + writer.WriteLine(nameof(key_turnright) + " = " + key_turnright); + writer.WriteLine(nameof(key_fire) + " = " + key_fire); + writer.WriteLine(nameof(key_use) + " = " + key_use); + writer.WriteLine(nameof(key_run) + " = " + key_run); + writer.WriteLine(nameof(key_strafe) + " = " + key_strafe); + + writer.WriteLine(nameof(mouse_sensitivity) + " = " + mouse_sensitivity); + writer.WriteLine(nameof(mouse_disableyaxis) + " = " + BoolToString(mouse_disableyaxis)); + + writer.WriteLine(nameof(game_alwaysrun) + " = " + BoolToString(game_alwaysrun)); + + writer.WriteLine(nameof(video_screenwidth) + " = " + video_screenwidth); + writer.WriteLine(nameof(video_screenheight) + " = " + video_screenheight); + writer.WriteLine(nameof(video_fullscreen) + " = " + BoolToString(video_fullscreen)); + writer.WriteLine(nameof(video_highresolution) + " = " + BoolToString(video_highresolution)); + writer.WriteLine(nameof(video_displaymessage) + " = " + BoolToString(video_displaymessage)); + writer.WriteLine(nameof(video_gamescreensize) + " = " + video_gamescreensize); + writer.WriteLine(nameof(video_gammacorrection) + " = " + video_gammacorrection); + + writer.WriteLine(nameof(audio_soundvolume) + " = " + audio_soundvolume); + writer.WriteLine(nameof(audio_musicvolume) + " = " + audio_musicvolume); + writer.WriteLine(nameof(audio_randompitch) + " = " + BoolToString(audio_randompitch)); + } + } + catch + { + } + } + + private static int GetInt(Dictionary dic, string name, int defaultValue) + { + string stringValue; + if (dic.TryGetValue(name, out stringValue)) + { + int value; + if (int.TryParse(stringValue, out value)) + { + return value; + } + } + + return defaultValue; + } + + private static bool GetBool(Dictionary dic, string name, bool defaultValue) + { + string stringValue; + if (dic.TryGetValue(name, out stringValue)) + { + if (stringValue == "true") + { + return true; + } + else if (stringValue == "false") + { + return false; + } + } + + return defaultValue; + } + + private static KeyBinding GetKeyBinding(Dictionary dic, string name, KeyBinding defaultValue) + { + string stringValue; + if (dic.TryGetValue(name, out stringValue)) + { + return KeyBinding.Parse(stringValue); + } + + return defaultValue; + } + + private static string BoolToString(bool value) + { + return value ? "true" : "false"; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/ConfigUtilities.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/ConfigUtilities.cs new file mode 100644 index 00000000..678cbd27 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/ConfigUtilities.cs @@ -0,0 +1,37 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.IO; +using ManagedDoom.Audio; +using SFML.Window; + +namespace ManagedDoom +{ + public static class ConfigUtilities + { + + static string[] names = new string[] + { + "DOOM2.WAD", + "PLUTONIA.WAD", + "TNT.WAD", + "DOOM.WAD", + "DOOM1.WAD" + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/DeHackEd.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/DeHackEd.cs new file mode 100644 index 00000000..f38733cb --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/DeHackEd.cs @@ -0,0 +1,608 @@ +// +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.ExceptionServices; +using System.Text; + +namespace ManagedDoom +{ + public static class DeHackEd + { + private static Tuple, Action>[] sourcePointerTable; + + public static void ReadFiles(params string[] fileNames) + { + try + { + // Ensure the static members are initialized. + DoomInfo.Strings.PRESSKEY.GetHashCode(); + + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Apply DeHackEd patches: "); + + foreach (var fileName in fileNames) + { + ReadFile(fileName); + } + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK (" + string.Join(", ", fileNames.Select(x => Path.GetFileName(x))) + ")"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + ExceptionDispatchInfo.Throw(e); + } + } + + private static void ReadFile(string fileName) + { + if (sourcePointerTable == null) + { + sourcePointerTable = new Tuple, Action>[DoomInfo.States.Length]; + for (var i = 0; i < sourcePointerTable.Length; i++) + { + var playerAction = DoomInfo.States[i].PlayerAction; + var mobjAction = DoomInfo.States[i].MobjAction; + sourcePointerTable[i] = Tuple.Create(playerAction, mobjAction); + } + } + + var data = new List(); + var last = Block.None; + foreach (var line in File.ReadLines(fileName)) + { + var split = line.Split(' '); + var blockType = GetBlockType(split); + if (blockType == Block.None) + { + data.Add(line); + } + else + { + ProcessBlock(last, data); + data.Clear(); + data.Add(line); + last = blockType; + } + } + ProcessBlock(last, data); + } + + private static void ProcessBlock(Block type, List data) + { + 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; + } + } + + private static void ProcessThingBlock(List data) + { + var thingNumber = int.Parse(data[0].Split(' ')[1]) - 1; + var info = DoomInfo.MobjInfos[thingNumber]; + var dic = GetKeyValuePairs(data); + + info.DoomEdNum = GetInt(dic, "ID #", info.DoomEdNum); + info.SpawnState = (MobjState)GetInt(dic, "Initial frame", (int)info.SpawnState); + info.SpawnHealth = GetInt(dic, "Hit points", info.SpawnHealth); + info.SeeState = (MobjState)GetInt(dic, "First moving frame", (int)info.SeeState); + info.SeeSound = (Sfx)GetInt(dic, "Alert sound", (int)info.SeeSound); + info.ReactionTime = GetInt(dic, "Reaction time", info.ReactionTime); + info.AttackSound = (Sfx)GetInt(dic, "Attack sound", (int)info.AttackSound); + info.PainState = (MobjState)GetInt(dic, "Injury frame", (int)info.PainState); + info.PainChance = GetInt(dic, "Pain chance", info.PainChance); + info.PainSound = (Sfx)GetInt(dic, "Pain sound", (int)info.PainSound); + info.MeleeState = (MobjState)GetInt(dic, "Close attack frame", (int)info.MeleeState); + info.MissileState = (MobjState)GetInt(dic, "Far attack frame", (int)info.MissileState); + info.DeathState = (MobjState)GetInt(dic, "Death frame", (int)info.DeathState); + info.XdeathState = (MobjState)GetInt(dic, "Exploding frame", (int)info.XdeathState); + info.DeathSound = (Sfx)GetInt(dic, "Death sound", (int)info.DeathSound); + info.Speed = GetInt(dic, "Speed", info.Speed); + info.Radius = new Fixed(GetInt(dic, "Width", info.Radius.Data)); + info.Height = new Fixed(GetInt(dic, "Height", info.Height.Data)); + info.Mass = GetInt(dic, "Mass", info.Mass); + info.Damage = GetInt(dic, "Missile damage", info.Damage); + info.ActiveSound = (Sfx)GetInt(dic, "Action sound", (int)info.ActiveSound); + info.Flags = (MobjFlags)GetInt(dic, "Bits", (int)info.Flags); + info.Raisestate = (MobjState)GetInt(dic, "Respawn frame", (int)info.Raisestate); + } + + private static void ProcessFrameBlock(List data) + { + var frameNumber = int.Parse(data[0].Split(' ')[1]); + var info = DoomInfo.States[frameNumber]; + var dic = GetKeyValuePairs(data); + + info.Sprite = (Sprite)GetInt(dic, "Sprite number", (int)info.Sprite); + info.Frame = GetInt(dic, "Sprite subnumber", info.Frame); + info.Tics = GetInt(dic, "Duration", info.Tics); + info.Next = (MobjState)GetInt(dic, "Next frame", (int)info.Next); + info.Misc1 = GetInt(dic, "Unknown 1", info.Misc1); + info.Misc2 = GetInt(dic, "Unknown 2", info.Misc2); + } + + private static void ProcessPointerBlock(List data) + { + var dic = GetKeyValuePairs(data); + var start = data[0].IndexOf('(') + 1; + var end = data[0].IndexOf(')'); + var length = end - start; + var targetFrameNumber = int.Parse(data[0].Substring(start, length).Split(' ')[1]); + var sourceFrameNumber = GetInt(dic, "Codep Frame", -1); + if (sourceFrameNumber == -1) + { + return; + } + var info = DoomInfo.States[targetFrameNumber]; + + info.PlayerAction = sourcePointerTable[sourceFrameNumber].Item1; + info.MobjAction = sourcePointerTable[sourceFrameNumber].Item2; + } + + private static void ProcessSoundBlock(List data) + { + } + + private static void ProcessAmmoBlock(List data) + { + var ammoNumber = int.Parse(data[0].Split(' ')[1]); + var dic = GetKeyValuePairs(data); + var max = DoomInfo.AmmoInfos.Max; + var clip = DoomInfo.AmmoInfos.Clip; + + max[ammoNumber] = GetInt(dic, "Max ammo", max[ammoNumber]); + clip[ammoNumber] = GetInt(dic, "Per ammo", clip[ammoNumber]); + } + + private static void ProcessWeaponBlock(List data) + { + var weaponNumber = int.Parse(data[0].Split(' ')[1]); + var info = DoomInfo.WeaponInfos[weaponNumber]; + var dic = GetKeyValuePairs(data); + + info.Ammo = (AmmoType)GetInt(dic, "Ammo type", (int)info.Ammo); + info.UpState = (MobjState)GetInt(dic, "Deselect frame", (int)info.UpState); + info.DownState = (MobjState)GetInt(dic, "Select frame", (int)info.DownState); + info.ReadyState = (MobjState)GetInt(dic, "Bobbing frame", (int)info.ReadyState); + info.AttackState = (MobjState)GetInt(dic, "Shooting frame", (int)info.AttackState); + info.FlashState = (MobjState)GetInt(dic, "Firing frame", (int)info.FlashState); + } + + private static void ProcessCheatBlock(List data) + { + } + + private static void ProcessMiscBlock(List data) + { + var dic = GetKeyValuePairs(data); + + DoomInfo.DeHackEdConst.InitialHealth = GetInt(dic, "Initial Health", DoomInfo.DeHackEdConst.InitialHealth); + DoomInfo.DeHackEdConst.InitialBullets = GetInt(dic, "Initial Bullets", DoomInfo.DeHackEdConst.InitialBullets); + DoomInfo.DeHackEdConst.MaxHealth = GetInt(dic, "Max Health", DoomInfo.DeHackEdConst.MaxHealth); + DoomInfo.DeHackEdConst.MaxArmor = GetInt(dic, "Max Armor", DoomInfo.DeHackEdConst.MaxArmor); + DoomInfo.DeHackEdConst.GreenArmorClass = GetInt(dic, "Green Armor Class", DoomInfo.DeHackEdConst.GreenArmorClass); + DoomInfo.DeHackEdConst.BlueArmorClass = GetInt(dic, "Blue Armor Class", DoomInfo.DeHackEdConst.BlueArmorClass); + DoomInfo.DeHackEdConst.MaxSoulsphere = GetInt(dic, "Max Soulsphere", DoomInfo.DeHackEdConst.MaxSoulsphere); + DoomInfo.DeHackEdConst.SoulsphereHealth = GetInt(dic, "Soulsphere Health", DoomInfo.DeHackEdConst.SoulsphereHealth); + DoomInfo.DeHackEdConst.MegasphereHealth = GetInt(dic, "Megasphere Health", DoomInfo.DeHackEdConst.MegasphereHealth); + DoomInfo.DeHackEdConst.GodModeHealth = GetInt(dic, "God Mode Health", DoomInfo.DeHackEdConst.GodModeHealth); + DoomInfo.DeHackEdConst.IdfaArmor = GetInt(dic, "IDFA Armor", DoomInfo.DeHackEdConst.IdfaArmor); + DoomInfo.DeHackEdConst.IdfaArmorClass = GetInt(dic, "IDFA Armor Class", DoomInfo.DeHackEdConst.IdfaArmorClass); + DoomInfo.DeHackEdConst.IdkfaArmor = GetInt(dic, "IDKFA Armor", DoomInfo.DeHackEdConst.IdkfaArmor); + DoomInfo.DeHackEdConst.IdkfaArmorClass = GetInt(dic, "IDKFA Armor Class", DoomInfo.DeHackEdConst.IdkfaArmorClass); + DoomInfo.DeHackEdConst.BfgCellsPerShot = GetInt(dic, "BFG Cells/Shot", DoomInfo.DeHackEdConst.BfgCellsPerShot); + DoomInfo.DeHackEdConst.MonstersInfight = GetInt(dic, "Monsters Infight", 0) == 221; + } + + private static void ProcessTextBlock(List data) + { + var split = data[0].Split(' '); + var length1 = int.Parse(split[1]); + var length2 = int.Parse(split[2]); + + var line = 1; + var pos = 0; + + var sb1 = new StringBuilder(); + for (var i = 0; i < length1; i++) + { + if (pos == data[line].Length) + { + sb1.Append('\n'); + line++; + pos = 0; + } + else + { + sb1.Append(data[line][pos]); + pos++; + } + } + + var sb2 = new StringBuilder(); + for (var i = 0; i < length2; i++) + { + if (pos == data[line].Length) + { + sb2.Append('\n'); + line++; + pos = 0; + } + else + { + sb2.Append(data[line][pos]); + pos++; + } + } + + DoomString.Replace(sb1.ToString(), sb2.ToString()); + } + + private static void ProcessSpriteBlock(List data) + { + } + + private static Block GetBlockType(string[] split) + { + if (IsThingBlockStart(split)) + { + return Block.Thing; + } + else if (IsFrameBlockStart(split)) + { + return Block.Frame; + } + else if (IsPointerBlockStart(split)) + { + return Block.Pointer; + } + else if (IsSoundBlockStart(split)) + { + return Block.Sound; + } + else if (IsAmmoBlockStart(split)) + { + return Block.Ammo; + } + else if (IsWeaponBlockStart(split)) + { + return Block.Weapon; + } + else if (IsCheatBlockStart(split)) + { + return Block.Cheat; + } + else if (IsMiscBlockStart(split)) + { + return Block.Misc; + } + else if (IsTextBlockStart(split)) + { + return Block.Text; + } + else if (IsSpriteBlockStart(split)) + { + return Block.Sprite; + } + else + { + 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 IsNumber(string value) + { + foreach (var ch in value) + { + if (!('0' <= ch && ch <= '9')) + { + return false; + } + } + + return true; + } + + private static Dictionary GetKeyValuePairs(List data) + { + var dic = new Dictionary(); + foreach (var line in data) + { + var split = line.Split('='); + if (split.Length == 2) + { + dic[split[0].Trim()] = split[1].Trim(); + } + } + return dic; + } + + private static string GetString(Dictionary dic, string key, string defaultValue) + { + string value; + if (dic.TryGetValue(key, out value)) + { + return value; + } + + return defaultValue; + } + + private static int GetInt(Dictionary dic, string key, int defaultValue) + { + string value; + if (dic.TryGetValue(key, out value)) + { + int intValue; + if (int.TryParse(value, out intValue)) + { + return intValue; + } + } + + return defaultValue; + } + + + + private enum Block + { + None, + Thing, + Frame, + Pointer, + Sound, + Ammo, + Weapon, + Cheat, + Misc, + Text, + Sprite + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/CommonResource.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/CommonResource.cs new file mode 100644 index 00000000..9400bf27 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/CommonResource.cs @@ -0,0 +1,85 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Runtime.ExceptionServices; + +namespace ManagedDoom +{ + public sealed class CommonResource : IDisposable + { + private Wad wad; + private Palette palette; + private ColorMap colorMap; + private TextureLookup textures; + private FlatLookup flats; + private SpriteLookup sprites; + private TextureAnimation animation; + + private CommonResource() + { + } + + public CommonResource(string[] wadPaths) + { + try + { + wad = new Wad(wadPaths); + 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); + } + catch (Exception e) + { + ExceptionDispatchInfo.Throw(e); + } + } + + public static CommonResource CreateDummy(params string[] wadPaths) + { + var resource = new CommonResource(); + resource.wad = new Wad(wadPaths); + resource.palette = new Palette(resource.wad); + resource.colorMap = new ColorMap(resource.wad); + resource.textures = new TextureLookup(resource.wad, true); + resource.flats = new FlatLookup(resource.wad, true); + resource.sprites = new SpriteLookup(resource.wad, true); + resource.animation = new TextureAnimation(resource.textures, resource.flats); + return resource; + } + + public void Dispose() + { + if (wad != null) + { + wad.Dispose(); + wad = null; + } + } + + public Wad Wad => wad; + public Palette Palette => palette; + public ColorMap ColorMap => colorMap; + public TextureLookup Textures => textures; + public FlatLookup Flats => flats; + public SpriteLookup Sprites => sprites; + public TextureAnimation Animation => animation; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomDebug.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomDebug.cs new file mode 100644 index 00000000..52c0ecec --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomDebug.cs @@ -0,0 +1,150 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Text; + +namespace ManagedDoom +{ + public static class DoomDebug + { + public static int CombineHash(int a, int b) + { + return (3 * a) ^ b; + } + + public static int GetMobjHash(Mobj mobj) + { + var hash = 0; + + hash = CombineHash(hash, mobj.X.Data); + hash = CombineHash(hash, mobj.Y.Data); + hash = CombineHash(hash, mobj.Z.Data); + + hash = CombineHash(hash, (int)mobj.Angle.Data); + hash = CombineHash(hash, (int)mobj.Sprite); + hash = CombineHash(hash, mobj.Frame); + + hash = CombineHash(hash, mobj.FloorZ.Data); + hash = CombineHash(hash, mobj.CeilingZ.Data); + + hash = CombineHash(hash, mobj.Radius.Data); + hash = CombineHash(hash, mobj.Height.Data); + + hash = CombineHash(hash, mobj.MomX.Data); + hash = CombineHash(hash, mobj.MomY.Data); + hash = CombineHash(hash, mobj.MomZ.Data); + + hash = CombineHash(hash, mobj.Tics); + hash = CombineHash(hash, (int)mobj.Flags); + hash = CombineHash(hash, mobj.Health); + + hash = CombineHash(hash, (int)mobj.MoveDir); + hash = CombineHash(hash, mobj.MoveCount); + + hash = CombineHash(hash, mobj.ReactionTime); + hash = CombineHash(hash, mobj.Threshold); + + return hash; + } + + public static int GetMobjHash(World world) + { + var hash = 0; + foreach (var thinker in world.Thinkers) + { + var mobj = thinker as Mobj; + if (mobj != null) + { + hash = CombineHash(hash, GetMobjHash(mobj)); + } + } + return hash; + } + + private static string GetMobjCsv(Mobj mobj) + { + var sb = new StringBuilder(); + + sb.Append(mobj.X.Data).Append(","); + sb.Append(mobj.Y.Data).Append(","); + sb.Append(mobj.Z.Data).Append(","); + + sb.Append((int)mobj.Angle.Data).Append(","); + sb.Append((int)mobj.Sprite).Append(","); + sb.Append(mobj.Frame).Append(","); + + sb.Append(mobj.FloorZ.Data).Append(","); + sb.Append(mobj.CeilingZ.Data).Append(","); + + sb.Append(mobj.Radius.Data).Append(","); + sb.Append(mobj.Height.Data).Append(","); + + sb.Append(mobj.MomX.Data).Append(","); + sb.Append(mobj.MomY.Data).Append(","); + sb.Append(mobj.MomZ.Data).Append(","); + + sb.Append((int)mobj.Tics).Append(","); + sb.Append((int)mobj.Flags).Append(","); + sb.Append(mobj.Health).Append(","); + + sb.Append((int)mobj.MoveDir).Append(","); + sb.Append(mobj.MoveCount).Append(","); + + sb.Append(mobj.ReactionTime).Append(","); + sb.Append(mobj.Threshold); + + return sb.ToString(); + } + + public static void DumpMobjCsv(string path, World world) + { + using (var writer = new System.IO.StreamWriter(path)) + { + foreach (var thinker in world.Thinkers) + { + var mobj = thinker as Mobj; + if (mobj != null) + { + writer.WriteLine(GetMobjCsv(mobj)); + } + } + } + } + + public static int GetSectorHash(Sector sector) + { + var hash = 0; + + hash = CombineHash(hash, sector.FloorHeight.Data); + hash = CombineHash(hash, sector.CeilingHeight.Data); + hash = CombineHash(hash, sector.LightLevel); + + return hash; + } + + public static int GetSectorHash(World world) + { + var hash = 0; + foreach (var sector in world.Map.Sectors) + { + hash = CombineHash(hash, GetSectorHash(sector)); + } + return hash; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomInterop.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomInterop.cs new file mode 100644 index 00000000..b0a152a6 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomInterop.cs @@ -0,0 +1,48 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static class DoomInterop + { + public static string ToString(byte[] data, int offset, int maxLength) + { + var length = 0; + for (var i = 0; i < maxLength; i++) + { + if (data[offset + i] == 0) + { + break; + } + length++; + } + var chars = new char[length]; + for (var i = 0; i < chars.Length; i++) + { + var c = data[offset + i]; + if ('a' <= c && c <= 'z') + { + c -= 0x20; + } + chars[i] = (char)c; + } + return new string(chars); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomRandom.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomRandom.cs new file mode 100644 index 00000000..7bf70f4b --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomRandom.cs @@ -0,0 +1,70 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class DoomRandom + { + private static readonly int[] table = new int[] + { + 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; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomString.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomString.cs new file mode 100644 index 00000000..d78e6f04 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Common/DoomString.cs @@ -0,0 +1,68 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class DoomString + { + private static Dictionary table = new Dictionary(); + + private string original; + private string replaced; + + public DoomString(string original) + { + this.original = original; + replaced = original; + + if (!table.ContainsKey(original)) + { + table.Add(original, this); + } + } + + public override string ToString() + { + return replaced; + } + + public char this[int index] + { + get + { + return replaced[index]; + } + } + + public static implicit operator string(DoomString ds) + { + return ds.replaced; + } + + public static void Replace(string original, string replaced) + { + DoomString ds; + if (table.TryGetValue(original, out ds)) + { + ds.replaced = replaced; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/DemoPlayback.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/DemoPlayback.cs new file mode 100644 index 00000000..257ba9e4 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/DemoPlayback.cs @@ -0,0 +1,99 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Diagnostics; +using System.IO; + +namespace ManagedDoom +{ + public sealed class DemoPlayback + { + private Demo demo; + private TicCmd[] cmds; + private DoomGame game; + + private Stopwatch stopwatch; + private int frameCount; + + public DemoPlayback(CommonResource resource, GameOptions options, string demoName) + { + if (File.Exists(demoName)) + { + demo = new Demo(demoName); + } + else if (File.Exists(demoName + ".lmp")) + { + demo = new Demo(demoName + ".lmp"); + } + else + { + var lumpName = demoName.ToUpper(); + if (resource.Wad.GetLumpNumber(lumpName) == -1) + { + throw new Exception("Demo '" + demoName + "' was not found!"); + } + demo = new Demo(resource.Wad.ReadLump(lumpName)); + } + + demo.Options.GameVersion = options.GameVersion; + demo.Options.GameMode = options.GameMode; + demo.Options.MissionPack = options.MissionPack; + demo.Options.Renderer = options.Renderer; + demo.Options.Sound = options.Sound; + demo.Options.Music = options.Music; + + cmds = new TicCmd[Player.MaxPlayerCount]; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + cmds[i] = new TicCmd(); + } + + game = new DoomGame(resource, demo.Options); + game.DeferedInitNew(); + + stopwatch = new Stopwatch(); + } + + public UpdateResult Update() + { + if (!stopwatch.IsRunning) + { + stopwatch.Start(); + } + + if (!demo.ReadCmd(cmds)) + { + stopwatch.Stop(); + return UpdateResult.Completed; + } + else + { + frameCount++; + return game.Update(cmds); + } + } + + public void DoEvent(DoomEvent e) + { + game.DoEvent(e); + } + + public DoomGame Game => game; + public double Fps => frameCount / stopwatch.Elapsed.TotalSeconds; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Event/DoomEvent.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Event/DoomEvent.cs new file mode 100644 index 00000000..12288275 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Event/DoomEvent.cs @@ -0,0 +1,36 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class DoomEvent + { + private EventType type; + private DoomKey key; + + public DoomEvent(EventType type, DoomKey key) + { + this.type = type; + this.key = key; + } + + public EventType Type => type; + public DoomKey Key => key; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Event/EventType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Event/EventType.cs new file mode 100644 index 00000000..1dbaf511 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Event/EventType.cs @@ -0,0 +1,29 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum EventType + { + KeyDown, + KeyUp, + Mouse, + Joystick + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/Demo.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/Demo.cs new file mode 100644 index 00000000..03540fdc --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/Demo.cs @@ -0,0 +1,113 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.IO; + +namespace ManagedDoom +{ + public sealed class Demo + { + private int p; + private byte[] data; + + private GameOptions options; + + private int playerCount; + + 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 (var 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] == 0x80) + { + return false; + } + + if (p + 4 * playerCount > data.Length) + { + return false; + } + + var players = options.Players; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (players[i].InGame) + { + var cmd = cmds[i]; + cmd.ForwardMove = (sbyte)data[p++]; + cmd.SideMove = (sbyte)data[p++]; + cmd.AngleTurn = (short)(data[p++] << 8); + cmd.Buttons = data[p++]; + } + } + + return true; + } + + public GameOptions Options => options; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/DoomGame.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/DoomGame.cs new file mode 100644 index 00000000..b28dbd4a --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/DoomGame.cs @@ -0,0 +1,626 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.IO; + +namespace ManagedDoom +{ + public sealed class DoomGame + { + private CommonResource resource; + private GameOptions options; + + private GameAction gameAction; + private GameState gameState; + + private int gameTic; + private DoomRandom random; + + private World world; + private Intermission intermission; + private Finale finale; + + private bool paused; + + private int loadGameSlotNumber; + private int saveGameSlotNumber; + private string saveGameDescription; + + public DoomGame(CommonResource resource, GameOptions options) + { + this.resource = resource; + this.options = options; + + gameAction = GameAction.Nothing; + + gameTic = 0; + random = new DoomRandom(); + } + + + //////////////////////////////////////////////////////////// + // Public methods to control the game state + //////////////////////////////////////////////////////////// + + /// + /// Start a new game. + /// Can be called by the startup code or the menu task. + /// + public void DeferedInitNew() + { + gameAction = GameAction.NewGame; + } + + /// + /// Start a new game. + /// Can be called by the startup code or the menu task. + /// + public void DeferedInitNew(GameSkill skill, int episode, int map) + { + options.Skill = skill; + options.Episode = episode; + options.Map = map; + gameAction = GameAction.NewGame; + } + + /// + /// Load the saved game at the given slot number. + /// Can be called by the startup code or the menu task. + /// + public void LoadGame(int slotNumber) + { + loadGameSlotNumber = slotNumber; + gameAction = GameAction.LoadGame; + } + + /// + /// Save the game at the given slot number. + /// Can be called by the startup code or the menu task. + /// + public void SaveGame(int slotNumber, string description) + { + saveGameSlotNumber = slotNumber; + saveGameDescription = description; + gameAction = GameAction.SaveGame; + } + + /// + /// Advance the game one frame. + /// + public UpdateResult Update(TicCmd[] cmds) + { + // Do player reborns if needed. + var players = options.Players; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (players[i].InGame && players[i].PlayerState == PlayerState.Reborn) + { + DoReborn(i); + } + } + + // Do things to change the game state. + while (gameAction != GameAction.Nothing) + { + 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; + case GameAction.Nothing: + break; + } + } + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (players[i].InGame) + { + var cmd = players[i].Cmd; + cmd.CopyFrom(cmds[i]); + + /* + if (demorecording) + { + G_WriteDemoTiccmd(cmd); + } + */ + + // Check for turbo cheats. + if (cmd.ForwardMove > GameConst.TurboThreshold && + (world.LevelTime & 31) == 0 && + ((world.LevelTime >> 5) & 3) == i) + { + var player = players[options.ConsolePlayer]; + player.SendMessage(players[i].Name + " is turbo!"); + } + } + } + + // Check for special buttons. + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (players[i].InGame) + { + if ((players[i].Cmd.Buttons & TicCmdButtons.Special) != 0) + { + if ((players[i].Cmd.Buttons & TicCmdButtons.SpecialMask) == TicCmdButtons.Pause) + { + paused = !paused; + if (paused) + { + options.Sound.Pause(); + } + else + { + options.Sound.Resume(); + } + } + } + } + } + + // Do main actions. + var result = UpdateResult.None; + switch (gameState) + { + case GameState.Level: + if (!paused || world.FirstTicIsNotYetDone) + { + result = world.Update(); + if (result == UpdateResult.Completed) + { + gameAction = GameAction.Completed; + } + } + break; + + case GameState.Intermission: + result = intermission.Update(); + if (result == UpdateResult.Completed) + { + gameAction = GameAction.WorldDone; + + if (world.SecretExit) + { + players[options.ConsolePlayer].DidSecret = true; + } + + if (options.GameMode == GameMode.Commercial) + { + switch (options.Map) + { + case 6: + case 11: + case 20: + case 30: + DoFinale(); + result = UpdateResult.NeedWipe; + break; + + case 15: + case 31: + if (world.SecretExit) + { + DoFinale(); + result = UpdateResult.NeedWipe; + } + break; + } + } + } + break; + + case GameState.Finale: + result = finale.Update(); + if (result == UpdateResult.Completed) + { + gameAction = GameAction.WorldDone; + } + break; + } + + gameTic++; + + if (result == UpdateResult.NeedWipe) + { + return UpdateResult.NeedWipe; + } + else + { + return UpdateResult.None; + } + } + + + //////////////////////////////////////////////////////////// + // Actual game actions + //////////////////////////////////////////////////////////// + + // It seems that these methods should not be called directly + // from outside for some reason. + // So if you want to start a new game or do load / save, use + // the following public methods. + // + // - DeferedInitNew + // - LoadGame + // - SaveGame + + private void DoLoadLevel() + { + gameAction = GameAction.Nothing; + + gameState = GameState.Level; + + var players = options.Players; + for (var 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(resource, options, this); + + options.UserInput.Reset(); + } + + private void DoNewGame() + { + gameAction = GameAction.Nothing; + + InitNew(options.Skill, options.Episode, options.Map); + } + + private void DoLoadGame() + { + /*gameAction = GameAction.Nothing; + + var directory = ConfigUtilities.GetExeDirectory(); + var path = Path.Combine(directory, "doomsav" + loadGameSlotNumber + ".dsg"); + SaveAndLoad.Load(this, path);*/ + // TODO: implement load game + } + + private void DoSaveGame() + { + /*gameAction = GameAction.Nothing; + + var directory = ConfigUtilities.GetExeDirectory(); + var path = Path.Combine(directory, "doomsav" + saveGameSlotNumber + ".dsg"); + SaveAndLoad.Save(this, saveGameDescription, path); + world.ConsolePlayer.SendMessage(DoomInfo.Strings.GGSAVED);*/ + // TODO: implement save game + } + + private void DoCompleted() + { + gameAction = GameAction.Nothing; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (options.Players[i].InGame) + { + // Take away cards and stuff. + options.Players[i].FinishLevel(); + } + } + + if (options.GameMode != GameMode.Commercial) + { + switch (options.Map) + { + case 8: + gameAction = GameAction.Victory; + return; + case 9: + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + options.Players[i].DidSecret = true; + } + break; + } + } + + if ((options.Map == 8) && (options.GameMode != GameMode.Commercial)) + { + // Victory. + gameAction = GameAction.Victory; + return; + } + + if ((options.Map == 9) && (options.GameMode != GameMode.Commercial)) + { + // Exit secret level. + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + options.Players[i].DidSecret = true; + } + } + + var imInfo = options.IntermissionInfo; + + imInfo.DidSecret = options.Players[options.ConsolePlayer].DidSecret; + imInfo.Episode = options.Episode - 1; + imInfo.LastLevel = options.Map - 1; + + // IntermissionInfo.Next is 0 biased, unlike GameOptions.Map. + if (options.GameMode == GameMode.Commercial) + { + if (world.SecretExit) + { + switch (options.Map) + { + case 15: + imInfo.NextLevel = 30; + break; + case 31: + imInfo.NextLevel = 31; + break; + } + } + else + { + switch (options.Map) + { + case 31: + case 32: + imInfo.NextLevel = 15; + break; + default: + imInfo.NextLevel = options.Map; + break; + } + } + } + else + { + if (world.SecretExit) + { + // Go to secret level. + imInfo.NextLevel = 8; + } + else if (options.Map == 9) + { + // Returning from secret level. + switch (options.Episode) + { + case 1: + imInfo.NextLevel = 3; + break; + case 2: + imInfo.NextLevel = 5; + break; + case 3: + imInfo.NextLevel = 6; + break; + case 4: + imInfo.NextLevel = 2; + break; + } + } + else + { + // Go to next level. + imInfo.NextLevel = options.Map; + } + } + + imInfo.MaxKillCount = world.TotalKills; + imInfo.MaxItemCount = world.TotalItems; + imInfo.MaxSecretCount = world.TotalSecrets; + imInfo.TotalFrags = 0; + if (options.GameMode == GameMode.Commercial) + { + imInfo.ParTime = 35 * DoomInfo.ParTimes.Doom2[options.Map - 1]; + } + else + { + imInfo.ParTime = 35 * DoomInfo.ParTimes.Doom1[options.Episode - 1][options.Map - 1]; + } + + var players = options.Players; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + imInfo.Players[i].InGame = players[i].InGame; + imInfo.Players[i].KillCount = players[i].KillCount; + imInfo.Players[i].ItemCount = players[i].ItemCount; + imInfo.Players[i].SecretCount = players[i].SecretCount; + imInfo.Players[i].Time = world.LevelTime; + Array.Copy(players[i].Frags, imInfo.Players[i].Frags, Player.MaxPlayerCount); + } + + gameState = GameState.Intermission; + intermission = new Intermission(options, imInfo); + } + + 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); + } + + + //////////////////////////////////////////////////////////// + // Miscellaneous things + //////////////////////////////////////////////////////////// + + public void InitNew(GameSkill skill, int episode, int map) + { + skill = (GameSkill)Math.Clamp((int)skill, (int)GameSkill.Baby, (int)GameSkill.Nightmare); + + if (options.GameMode == GameMode.Retail) + { + episode = Math.Clamp(episode, 1, 4); + } + else if (options.GameMode == GameMode.Shareware) + { + episode = 1; + } + else + { + episode = Math.Clamp(episode, 1, 3); + } + + if (options.GameMode == GameMode.Commercial) + { + map = Math.Clamp(map, 1, 32); + } + else + { + map = Math.Clamp(map, 1, 9); + } + + random.Clear(); + + // Force players to be initialized upon first level load. + for (var 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); + } + else if (gameState == GameState.Finale) + { + return finale.DoEvent(e); + } + + return false; + } + + private void DoReborn(int playerNumber) + { + if (!options.NetGame) + { + // Reload the level from scratch. + gameAction = GameAction.LoadLevel; + } + else + { + // Respawn at the start. + + // First dissasociate the corpse. + options.Players[playerNumber].Mobj.Player = null; + + var ta = world.ThingAllocation; + + // Spawn at random spot if in death match. + if (options.Deathmatch != 0) + { + ta.DeathMatchSpawnPlayer(playerNumber); + return; + } + + if (ta.CheckSpot(playerNumber, ta.PlayerStarts[playerNumber])) + { + ta.SpawnPlayer(ta.PlayerStarts[playerNumber]); + return; + } + + // Try to spawn at one of the other players spots. + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (ta.CheckSpot(playerNumber, ta.PlayerStarts[i])) + { + // Fake as other player. + ta.PlayerStarts[i].Type = playerNumber + 1; + + world.ThingAllocation.SpawnPlayer(ta.PlayerStarts[i]); + + // Restore. + ta.PlayerStarts[i].Type = i + 1; + + return; + } + } + + // He's going to be inside something. + // Too bad. + world.ThingAllocation.SpawnPlayer(ta.PlayerStarts[playerNumber]); + } + } + + + public GameOptions Options => options; + public Player[] Players => options.Players; + public GameState State => gameState; + public int GameTic => gameTic; + public DoomRandom Random => random; + public World World => world; + public Intermission Intermission => intermission; + public Finale Finale => finale; + public bool Paused => paused; + + + + private enum GameAction + { + Nothing, + LoadLevel, + NewGame, + LoadGame, + SaveGame, + Completed, + Victory, + WorldDone + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameConst.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameConst.cs new file mode 100644 index 00000000..9d37c06b --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameConst.cs @@ -0,0 +1,30 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static class GameConst + { + public static readonly int TicRate = 35; + + public static readonly Fixed MaxThingRadius = Fixed.FromInt(32); + + public static readonly int TurboThreshold = 0x32; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameMode.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameMode.cs new file mode 100644 index 00000000..4a3d7135 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameMode.cs @@ -0,0 +1,31 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum GameMode + { + Shareware, // DOOM 1 shareware, E1, M9 + Registered, // DOOM 1 registered, E3, M27 + Commercial, // DOOM 2 retail, E1 M34 + // DOOM 2 german edition not handled + Retail, // DOOM 1 retail, E4, M36 + Indetermined // Well, no IWAD found. + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameOptions.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameOptions.cs new file mode 100644 index 00000000..5a41ff36 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameOptions.cs @@ -0,0 +1,232 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using ManagedDoom.SoftwareRendering; +using ManagedDoom.Audio; +using ManagedDoom.UserInput; + +namespace ManagedDoom +{ + 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 IRenderer renderer; + private ISound sound; + private IMusic music; + private IUserInput userInput; + + public GameOptions() + { + gameVersion = GameVersion.Version109; + gameMode = GameMode.Commercial; + missionPack = MissionPack.Doom2; + + players = new Player[Player.MaxPlayerCount]; + for (var 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(); + + renderer = null; + sound = NullSound.GetInstance(); + music = NullMusic.GetInstance(); + userInput = NullUserInput.GetInstance(); + } + + public GameVersion GameVersion + { + get => gameVersion; + set => gameVersion = value; + } + + public GameMode GameMode + { + get => gameMode; + set => gameMode = value; + } + + public MissionPack MissionPack + { + get => missionPack; + set => missionPack = value; + } + + public Player[] Players + { + get => players; + } + + public int ConsolePlayer + { + get => consolePlayer; + set => consolePlayer = value; + } + + public int Episode + { + get => episode; + set => episode = value; + } + + public int Map + { + get => map; + set => map = value; + } + + public GameSkill Skill + { + get => skill; + set => skill = value; + } + + public bool DemoPlayback + { + get => demoPlayback; + set => demoPlayback = value; + } + + public bool NetGame + { + get => netGame; + set => netGame = value; + } + + public int Deathmatch + { + get => deathmatch; + set => deathmatch = value; + } + + public bool FastMonsters + { + get => fastMonsters; + set => fastMonsters = value; + } + + public bool RespawnMonsters + { + get => respawnMonsters; + set => respawnMonsters = value; + } + + public bool NoMonsters + { + get => noMonsters; + set => noMonsters = value; + } + + public IntermissionInfo IntermissionInfo + { + get => intermissionInfo; + } + + public IRenderer Renderer + { + get => renderer; + set => renderer = value; + } + + public ISound Sound + { + get => sound; + + set + { + if (value != null) + { + sound = value; + } + else + { + sound = NullSound.GetInstance(); + } + } + } + + public IMusic Music + { + get => music; + + set + { + if (value != null) + { + music = value; + } + else + { + music = NullMusic.GetInstance(); + } + } + } + + public IUserInput UserInput + { + get => userInput; + + set + { + if (value != null) + { + userInput = value; + } + else + { + userInput = NullUserInput.GetInstance(); + } + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameSkill.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameSkill.cs new file mode 100644 index 00000000..bff20952 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameSkill.cs @@ -0,0 +1,30 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum GameSkill + { + Baby, + Easy, + Medium, + Hard, + Nightmare + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameState.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameState.cs new file mode 100644 index 00000000..07463b37 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameState.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum GameState + { + Level, + Intermission, + Finale + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameVersion.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameVersion.cs new file mode 100644 index 00000000..e3fab599 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/GameVersion.cs @@ -0,0 +1,29 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum GameVersion + { + Version109, + Ultimate, + Final, + Final2 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/MissionPack.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/MissionPack.cs new file mode 100644 index 00000000..dc375874 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/MissionPack.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum MissionPack + { + Doom2, + Plutonia, + Tnt + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/Player.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/Player.cs new file mode 100644 index 00000000..05a3b4d4 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/Player.cs @@ -0,0 +1,523 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + 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[] + { + "Green", + "Indigo", + "Brown", + "Red" + }; + + private int number; + private string name; + private bool inGame; + + private Mobj mobj; + private PlayerState playerState; + private TicCmd cmd; + + // Determine POV, including viewpoint bobbing during movement. + // Focal origin above mobj.Z. + private Fixed viewZ; + + // Base height above floor for viewz. + private Fixed viewHeight; + + // Bob / squat speed. + private Fixed deltaViewHeight; + + // Bounded / scaled total momentum. + private Fixed bob; + + // This is only used between levels, + // mobj.Health is used during levels. + private int health; + private int armorPoints; + + // Armor type is 0-2. + private int armorType; + + // Power ups. invinc and invis are tic counters. + private int[] powers; + private bool[] cards; + private bool backpack; + + // Frags, kills of other players. + private int[] frags; + + private WeaponType readyWeapon; + + // Is WeanponType.NoChange if not changing. + private WeaponType pendingWeapon; + + private bool[] weaponOwned; + private int[] ammo; + private int[] maxAmmo; + + // True if button down last tic. + private bool attackDown; + private bool useDown; + + // Bit flags, for cheats and debug. + private CheatFlags cheats; + + // Refired shots are less accurate. + private int refire; + + // For intermission stats. + private int killCount; + private int itemCount; + private int secretCount; + + // Hint messages. + private string message; + private int messageTime; + + // For screen flashing (red or bright). + private int damageCount; + private int bonusCount; + + // Who did damage (null for floors / ceilings). + private Mobj attacker; + + // So gun flashes light up areas. + private int extraLight; + + // Current PLAYPAL, ??? + // can be set to REDCOLORMAP for pain, etc. + private int fixedColorMap; + + // Player skin colorshift, + // 0-3 for which color to draw player. + private int colorMap; + + // Overlay view sprites (gun, etc). + private PlayerSpriteDef[] playerSprites; + + // True if secret level has been done. + private bool didSecret; + + public Player(int number) + { + this.number = number; + + name = defaultPlayerNames[number]; + + cmd = new TicCmd(); + + powers = new int[(int)PowerType.Count]; + cards = new bool[(int)CardType.Count]; + + frags = new int[MaxPlayerCount]; + + weaponOwned = new bool[(int)WeaponType.Count]; + ammo = new int[(int)AmmoType.Count]; + maxAmmo = new int[(int)AmmoType.Count]; + + playerSprites = new PlayerSpriteDef[(int)PlayerSprite.Count]; + for (var i = 0; i < playerSprites.Length; i++) + { + playerSprites[i] = new PlayerSpriteDef(); + } + } + + public void Clear() + { + mobj = null; + playerState = 0; + 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 = 0; + pendingWeapon = 0; + + Array.Clear(weaponOwned, 0, weaponOwned.Length); + Array.Clear(ammo, 0, ammo.Length); + Array.Clear(maxAmmo, 0, maxAmmo.Length); + + useDown = false; + attackDown = false; + + cheats = 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; + + foreach (var psp in playerSprites) + { + psp.Clear(); + } + + didSecret = false; + } + + 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[(int)WeaponType.Fist] = true; + weaponOwned[(int)WeaponType.Pistol] = true; + ammo[(int)AmmoType.Clip] = DoomInfo.DeHackEdConst.InitialBullets; + for (var i = 0; i < (int)AmmoType.Count; i++) + { + maxAmmo[i] = DoomInfo.AmmoInfos.Max[i]; + } + + // Don't do anything immediately. + useDown = true; + attackDown = true; + + cheats = 0; + + refire = 0; + + message = null; + messageTime = 0; + + damageCount = 0; + bonusCount = 0; + + attacker = null; + + extraLight = 0; + + fixedColorMap = 0; + + colorMap = 0; + + foreach (var psp in playerSprites) + { + psp.Clear(); + } + + didSecret = false; + } + + public void FinishLevel() + { + Array.Clear(powers, 0, powers.Length); + Array.Clear(cards, 0, cards.Length); + + // Cancel invisibility. + mobj.Flags &= ~MobjFlags.Shadow; + + // Cancel gun flashes. + extraLight = 0; + + // Cancel ir gogles. + fixedColorMap = 0; + + // No palette changes. + damageCount = 0; + bonusCount = 0; + } + + public void SendMessage(string message) + { + if (ReferenceEquals(this.message, (string)DoomInfo.Strings.MSGOFF) && + !ReferenceEquals(message, (string)DoomInfo.Strings.MSGON)) + { + return; + } + + this.message = message; + messageTime = 4 * GameConst.TicRate; + } + + public int Number => number; + + public string Name => name; + + public bool InGame + { + get => inGame; + set => inGame = value; + } + + public Mobj Mobj + { + get => mobj; + set => mobj = value; + } + + public PlayerState PlayerState + { + get => playerState; + set => playerState = value; + } + + public TicCmd Cmd + { + get => cmd; + } + + public Fixed ViewZ + { + get => viewZ; + set => viewZ = value; + } + + public Fixed ViewHeight + { + get => viewHeight; + set => viewHeight = value; + } + + public Fixed DeltaViewHeight + { + get => deltaViewHeight; + set => deltaViewHeight = value; + } + + public Fixed Bob + { + get => bob; + set => bob = value; + } + + public int Health + { + get => health; + set => health = value; + } + + public int ArmorPoints + { + get => armorPoints; + set => armorPoints = value; + } + + public int ArmorType + { + get => armorType; + set => armorType = value; + } + + public int[] Powers + { + get => powers; + } + + public bool[] Cards + { + get => cards; + } + + public bool Backpack + { + get => backpack; + set => backpack = value; + } + + public int[] Frags + { + get => frags; + } + + public WeaponType ReadyWeapon + { + get => readyWeapon; + set => readyWeapon = value; + } + + public WeaponType PendingWeapon + { + get => pendingWeapon; + set => pendingWeapon = value; + } + + public bool[] WeaponOwned + { + get => weaponOwned; + } + + public int[] Ammo + { + get => ammo; + } + + public int[] MaxAmmo + { + get => maxAmmo; + } + + public bool AttackDown + { + get => attackDown; + set => attackDown = value; + } + + public bool UseDown + { + get => useDown; + set => useDown = value; + } + + public CheatFlags Cheats + { + get => cheats; + set => cheats = value; + } + + public int Refire + { + get => refire; + set => refire = value; + } + + public int KillCount + { + get => killCount; + set => killCount = value; + } + + public int ItemCount + { + get => itemCount; + set => itemCount = value; + } + + public int SecretCount + { + get => secretCount; + set => secretCount = value; + } + + public string Message + { + get => message; + set => message = value; + } + + public int MessageTime + { + get => messageTime; + set => messageTime = value; + } + + public int DamageCount + { + get => damageCount; + set => damageCount = value; + } + + public int BonusCount + { + get => bonusCount; + set => bonusCount = value; + } + + public Mobj Attacker + { + get => attacker; + set => attacker = value; + } + + public int ExtraLight + { + get => extraLight; + set => extraLight = value; + } + + public int FixedColorMap + { + get => fixedColorMap; + set => fixedColorMap = value; + } + + public int ColorMap + { + get => colorMap; + set => colorMap = value; + } + + public PlayerSpriteDef[] PlayerSprites + { + get => playerSprites; + } + + public bool DidSecret + { + get => didSecret; + set => didSecret = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/PlayerState.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/PlayerState.cs new file mode 100644 index 00000000..f633fe4c --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/PlayerState.cs @@ -0,0 +1,33 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum PlayerState + { + // Playing or camping. + Live, + + // Dead on the ground, view follows killer. + Dead, + + // Ready to restart / respawn??? + Reborn + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/SaveAndLoad.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/SaveAndLoad.cs new file mode 100644 index 00000000..46bd262d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/SaveAndLoad.cs @@ -0,0 +1,1012 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.IO; + +namespace ManagedDoom +{ + /// + /// Vanilla-compatible save and load, full of messy binary handling code. + /// + public static class SaveAndLoad + { + public static readonly int DescriptionSize = 24; + + private static readonly int versionSize = 16; + private static readonly int saveBufferSize = 360 * 1024; + + private enum ThinkerClass + { + End, + Mobj + } + + private enum SpecialClass + { + Ceiling, + Door, + Floor, + Plat, + Flash, + Strobe, + Glow, + EndSpecials + } + + public static void Save(DoomGame game, string description, string path) + { + var sg = new SaveGame(description); + sg.Save(game, path); + } + + public static void Load(DoomGame game, string path) + { + var options = game.Options; + game.InitNew(options.Skill, options.Episode, options.Map); + + var lg = new LoadGame(File.ReadAllBytes(path)); + lg.Load(game); + } + + + + //////////////////////////////////////////////////////////// + // Save game + //////////////////////////////////////////////////////////// + + 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 (var i = 0; i < description.Length; i++) + { + data[i] = (byte)description[i]; + } + ptr += DescriptionSize; + } + + private void WriteVersion() + { + var version = "version 109"; + for (var i = 0; i < version.Length; i++) + { + data[ptr + i] = (byte)version[i]; + } + ptr += versionSize; + } + + public void Save(DoomGame game, string path) + { + var options = game.World.Options; + data[ptr++] = (byte)options.Skill; + data[ptr++] = (byte)options.Episode; + data[ptr++] = (byte)options.Map; + for (var 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++] = 0x1d; + + using (var writer = new FileStream(path, FileMode.Create, FileAccess.Write)) + { + writer.Write(data, 0, ptr); + } + } + + private void PadPointer() + { + ptr += (4 - (ptr & 3)) & 3; + } + + private void ArchivePlayers(World world) + { + var players = world.Options.Players; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (!players[i].InGame) + { + continue; + } + + PadPointer(); + + ptr = ArchivePlayer(players[i], data, ptr); + } + } + + private void ArchiveWorld(World world) + { + // Do sectors. + var sectors = world.Map.Sectors; + for (var i = 0; i < sectors.Length; i++) + { + ptr = ArchiveSector(sectors[i], data, ptr); + } + + // Do lines. + var lines = world.Map.Lines; + for (var i = 0; i < lines.Length; i++) + { + ptr = ArchiveLine(lines[i], data, ptr); + } + } + + private void ArchiveThinkers(World world) + { + var thinkers = world.Thinkers; + + // Read in saved thinkers. + foreach (var thinker in thinkers) + { + var mobj = thinker as Mobj; + if (mobj != null) + { + data[ptr++] = (byte)ThinkerClass.Mobj; + 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++] = (byte)ThinkerClass.End; + } + + private void ArchiveSpecials(World world) + { + var thinkers = world.Thinkers; + var sa = world.SectorAction; + + // Read in saved thinkers. + foreach (var thinker in thinkers) + { + if (thinker.ThinkerState == ThinkerState.InStasis) + { + var ceiling = thinker as CeilingMove; + if (sa.CheckActiveCeiling(ceiling)) + { + data[ptr++] = (byte)SpecialClass.Ceiling; + PadPointer(); + WriteThinkerState(data, ptr + 8, ceiling.ThinkerState); + Write(data, ptr + 12, (int)ceiling.Type); + Write(data, ptr + 16, ceiling.Sector.Number); + Write(data, ptr + 20, ceiling.BottomHeight.Data); + Write(data, ptr + 24, ceiling.TopHeight.Data); + Write(data, ptr + 28, ceiling.Speed.Data); + Write(data, ptr + 32, ceiling.Crush ? 1 : 0); + Write(data, ptr + 36, ceiling.Direction); + Write(data, ptr + 40, ceiling.Tag); + Write(data, ptr + 44, ceiling.OldDirection); + ptr += 48; + } + continue; + } + + { + var ceiling = thinker as CeilingMove; + if (ceiling != null) + { + data[ptr++] = (byte)SpecialClass.Ceiling; + PadPointer(); + WriteThinkerState(data, ptr + 8, ceiling.ThinkerState); + Write(data, ptr + 12, (int)ceiling.Type); + Write(data, ptr + 16, ceiling.Sector.Number); + Write(data, ptr + 20, ceiling.BottomHeight.Data); + Write(data, ptr + 24, ceiling.TopHeight.Data); + Write(data, ptr + 28, ceiling.Speed.Data); + Write(data, ptr + 32, ceiling.Crush ? 1 : 0); + Write(data, ptr + 36, ceiling.Direction); + Write(data, ptr + 40, ceiling.Tag); + Write(data, ptr + 44, ceiling.OldDirection); + ptr += 48; + continue; + } + } + + { + var door = thinker as VerticalDoor; + if (door != null) + { + data[ptr++] = (byte)SpecialClass.Door; + PadPointer(); + WriteThinkerState(data, ptr + 8, door.ThinkerState); + Write(data, ptr + 12, (int)door.Type); + Write(data, ptr + 16, door.Sector.Number); + Write(data, ptr + 20, door.TopHeight.Data); + Write(data, ptr + 24, door.Speed.Data); + Write(data, ptr + 28, door.Direction); + Write(data, ptr + 32, door.TopWait); + Write(data, ptr + 36, door.TopCountDown); + ptr += 40; + continue; + } + } + + { + var floor = thinker as FloorMove; + if (floor != null) + { + data[ptr++] = (byte)SpecialClass.Floor; + PadPointer(); + WriteThinkerState(data, ptr + 8, floor.ThinkerState); + Write(data, ptr + 12, (int)floor.Type); + Write(data, ptr + 16, floor.Crush ? 1 : 0); + Write(data, ptr + 20, floor.Sector.Number); + Write(data, ptr + 24, floor.Direction); + Write(data, ptr + 28, (int)floor.NewSpecial); + Write(data, ptr + 32, floor.Texture); + Write(data, ptr + 36, floor.FloorDestHeight.Data); + Write(data, ptr + 40, floor.Speed.Data); + ptr += 44; + continue; + } + } + + { + var plat = thinker as Platform; + if (plat != null) + { + data[ptr++] = (byte)SpecialClass.Plat; + PadPointer(); + WriteThinkerState(data, ptr + 8, plat.ThinkerState); + Write(data, ptr + 12, plat.Sector.Number); + Write(data, ptr + 16, plat.Speed.Data); + Write(data, ptr + 20, plat.Low.Data); + Write(data, ptr + 24, plat.High.Data); + Write(data, ptr + 28, plat.Wait); + Write(data, ptr + 32, plat.Count); + Write(data, ptr + 36, (int)plat.Status); + Write(data, ptr + 40, (int)plat.OldStatus); + Write(data, ptr + 44, plat.Crush ? 1 : 0); + Write(data, ptr + 48, plat.Tag); + Write(data, ptr + 52, (int)plat.Type); + ptr += 56; + continue; + } + } + + { + var flash = thinker as LightFlash; + if (flash != null) + { + data[ptr++] = (byte)SpecialClass.Flash; + PadPointer(); + WriteThinkerState(data, ptr + 8, flash.ThinkerState); + Write(data, ptr + 12, flash.Sector.Number); + Write(data, ptr + 16, flash.Count); + Write(data, ptr + 20, flash.MaxLight); + Write(data, ptr + 24, flash.MinLight); + Write(data, ptr + 28, flash.MaxTime); + Write(data, ptr + 32, flash.MinTime); + ptr += 36; + continue; + } + } + + { + var strobe = thinker as StrobeFlash; + if (strobe != null) + { + data[ptr++] = (byte)SpecialClass.Strobe; + PadPointer(); + WriteThinkerState(data, ptr + 8, strobe.ThinkerState); + Write(data, ptr + 12, strobe.Sector.Number); + Write(data, ptr + 16, strobe.Count); + Write(data, ptr + 20, strobe.MinLight); + Write(data, ptr + 24, strobe.MaxLight); + Write(data, ptr + 28, strobe.DarkTime); + Write(data, ptr + 32, strobe.BrightTime); + ptr += 36; + continue; + } + } + + { + var glow = thinker as GlowingLight; + if (glow != null) + { + data[ptr++] = (byte)SpecialClass.Glow; + PadPointer(); + WriteThinkerState(data, ptr + 8, glow.ThinkerState); + Write(data, ptr + 12, glow.Sector.Number); + Write(data, ptr + 16, glow.MinLight); + Write(data, ptr + 20, glow.MaxLight); + Write(data, ptr + 24, glow.Direction); + ptr += 28; + continue; + } + } + } + + data[ptr++] = (byte)SpecialClass.EndSpecials; + } + + 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 (var i = 0; i < (int)PowerType.Count; i++) + { + Write(data, p + 44 + 4 * i, player.Powers[i]); + } + for (var i = 0; i < (int)PowerType.Count; i++) + { + Write(data, p + 68 + 4 * i, player.Cards[i] ? 1 : 0); + } + Write(data, p + 92, player.Backpack ? 1 : 0); + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + Write(data, p + 96 + 4 * i, player.Frags[i]); + } + Write(data, p + 112, (int)player.ReadyWeapon); + Write(data, p + 116, (int)player.PendingWeapon); + for (var i = 0; i < (int)WeaponType.Count; i++) + { + Write(data, p + 120 + 4 * i, player.WeaponOwned[i] ? 1 : 0); + } + for (var i = 0; i < (int)AmmoType.Count; i++) + { + Write(data, p + 156 + 4 * i, player.Ammo[i]); + } + for (var i = 0; i < (int)AmmoType.Count; i++) + { + Write(data, p + 172 + 4 * i, player.MaxAmmo[i]); + } + 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 (var i = 0; i < (int)PlayerSprite.Count; i++) + { + if (player.PlayerSprites[i].State == null) + { + Write(data, p + 244 + 16 * i, 0); + } + else + { + Write(data, p + 244 + 16 * i, player.PlayerSprites[i].State.Number); + } + Write(data, p + 244 + 16 * i + 4, player.PlayerSprites[i].Tics); + Write(data, p + 244 + 16 * i + 8, player.PlayerSprites[i].Sx.Data); + Write(data, p + 244 + 16 * i + 12, player.PlayerSprites[i].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, (short)line.Tag); + p += 6; + + if (line.FrontSide != null) + { + var side = line.FrontSide; + Write(data, p, (short)side.TextureOffset.ToIntFloor()); + Write(data, p + 2, (short)side.RowOffset.ToIntFloor()); + Write(data, p + 4, (short)side.TopTexture); + Write(data, p + 6, (short)side.BottomTexture); + Write(data, p + 8, (short)side.MiddleTexture); + p += 10; + } + + if (line.BackSide != null) + { + var side = line.BackSide; + Write(data, p, (short)side.TextureOffset.ToIntFloor()); + Write(data, p + 2, (short)side.RowOffset.ToIntFloor()); + Write(data, p + 4, (short)side.TopTexture); + Write(data, p + 6, (short)side.BottomTexture); + Write(data, p + 8, (short)side.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) + { + switch (state) + { + case ThinkerState.InStasis: + Write(data, p, 0); + break; + default: + Write(data, p, 1); + break; + } + } + } + + + + //////////////////////////////////////////////////////////// + // Load game + //////////////////////////////////////////////////////////// + + private class LoadGame + { + private byte[] data; + private int ptr; + + public LoadGame(byte[] data) + { + this.data = data; + ptr = 0; + + ReadDescription(); + + var version = ReadVersion(); + if (version != "VERSION 109") + { + throw new Exception("Unsupported version!"); + } + } + + public void Load(DoomGame game) + { + var options = game.World.Options; + options.Skill = (GameSkill)data[ptr++]; + options.Episode = data[ptr++]; + options.Map = data[ptr++]; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + options.Players[i].InGame = data[ptr++] != 0; + } + + game.InitNew(options.Skill, options.Episode, options.Map); + + var a = data[ptr++]; + var b = data[ptr++]; + var c = data[ptr++]; + var levelTime = (a << 16) + (b << 8) + c; + + UnArchivePlayers(game.World); + UnArchiveWorld(game.World); + UnArchiveThinkers(game.World); + UnArchiveSpecials(game.World); + + if (data[ptr] != 0x1d) + { + 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() + { + var value = DoomInterop.ToString(data, ptr, DescriptionSize); + ptr += DescriptionSize; + return value; + } + + private string ReadVersion() + { + var value = DoomInterop.ToString(data, ptr, versionSize); + ptr += versionSize; + return value; + } + + private void UnArchivePlayers(World world) + { + var players = world.Options.Players; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (!players[i].InGame) + { + continue; + } + + PadPointer(); + + ptr = UnArchivePlayer(players[i], data, ptr); + } + } + + private void UnArchiveWorld(World world) + { + // Do sectors. + var sectors = world.Map.Sectors; + for (var i = 0; i < sectors.Length; i++) + { + ptr = UnArchiveSector(sectors[i], data, ptr); + } + + // Do lines. + var lines = world.Map.Lines; + for (var i = 0; i < lines.Length; i++) + { + ptr = UnArchiveLine(lines[i], data, ptr); + } + } + + private void UnArchiveThinkers(World world) + { + var thinkers = world.Thinkers; + var ta = world.ThingAllocation; + + // Remove all the current thinkers. + foreach (var thinker in thinkers) + { + var mobj = thinker as Mobj; + if (mobj != null) + { + ta.RemoveMobj(mobj); + } + } + thinkers.Reset(); + + // Read in saved thinkers. + while (true) + { + var tclass = (ThinkerClass)data[ptr++]; + switch (tclass) + { + case ThinkerClass.End: + // End of list. + return; + + case ThinkerClass.Mobj: + PadPointer(); + var mobj = new Mobj(world); + mobj.ThinkerState = ReadThinkerState(data, ptr + 8); + mobj.X = new Fixed(BitConverter.ToInt32(data, ptr + 12)); + mobj.Y = new Fixed(BitConverter.ToInt32(data, ptr + 16)); + mobj.Z = new Fixed(BitConverter.ToInt32(data, ptr + 20)); + mobj.Angle = new Angle(BitConverter.ToInt32(data, ptr + 32)); + mobj.Sprite = (Sprite)BitConverter.ToInt32(data, ptr + 36); + mobj.Frame = BitConverter.ToInt32(data, ptr + 40); + mobj.FloorZ = new Fixed(BitConverter.ToInt32(data, ptr + 56)); + mobj.CeilingZ = new Fixed(BitConverter.ToInt32(data, ptr + 60)); + mobj.Radius = new Fixed(BitConverter.ToInt32(data, ptr + 64)); + mobj.Height = new Fixed(BitConverter.ToInt32(data, ptr + 68)); + mobj.MomX = new Fixed(BitConverter.ToInt32(data, ptr + 72)); + mobj.MomY = new Fixed(BitConverter.ToInt32(data, ptr + 76)); + mobj.MomZ = new Fixed(BitConverter.ToInt32(data, ptr + 80)); + mobj.Type = (MobjType)BitConverter.ToInt32(data, ptr + 88); + mobj.Info = DoomInfo.MobjInfos[(int)mobj.Type]; + mobj.Tics = BitConverter.ToInt32(data, ptr + 96); + mobj.State = DoomInfo.States[BitConverter.ToInt32(data, ptr + 100)]; + mobj.Flags = (MobjFlags)BitConverter.ToInt32(data, ptr + 104); + mobj.Health = BitConverter.ToInt32(data, ptr + 108); + mobj.MoveDir = (Direction)BitConverter.ToInt32(data, ptr + 112); + mobj.MoveCount = BitConverter.ToInt32(data, ptr + 116); + mobj.ReactionTime = BitConverter.ToInt32(data, ptr + 124); + mobj.Threshold = BitConverter.ToInt32(data, ptr + 128); + var playerNumber = BitConverter.ToInt32(data, ptr + 132); + if (playerNumber != 0) + { + mobj.Player = world.Options.Players[playerNumber - 1]; + mobj.Player.Mobj = mobj; + } + mobj.LastLook = BitConverter.ToInt32(data, ptr + 136); + mobj.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(mobj); + // mobj.FloorZ = mobj.Subsector.Sector.FloorHeight; + // mobj.CeilingZ = mobj.Subsector.Sector.CeilingHeight; + thinkers.Add(mobj); + break; + + default: + throw new Exception("Unknown thinker class in savegame!"); + } + } + } + + private void UnArchiveSpecials(World world) + { + var thinkers = world.Thinkers; + var sa = world.SectorAction; + + // Read in saved thinkers. + while (true) + { + var tclass = (SpecialClass)data[ptr++]; + switch (tclass) + { + case SpecialClass.EndSpecials: + // End of list. + return; + + case SpecialClass.Ceiling: + PadPointer(); + var ceiling = new CeilingMove(world); + ceiling.ThinkerState = ReadThinkerState(data, ptr + 8); + ceiling.Type = (CeilingMoveType)BitConverter.ToInt32(data, ptr + 12); + ceiling.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 16)]; + ceiling.Sector.SpecialData = ceiling; + ceiling.BottomHeight = new Fixed(BitConverter.ToInt32(data, ptr + 20)); + ceiling.TopHeight = new Fixed(BitConverter.ToInt32(data, ptr + 24)); + ceiling.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 28)); + ceiling.Crush = BitConverter.ToInt32(data, ptr + 32) != 0; + ceiling.Direction = BitConverter.ToInt32(data, ptr + 36); + ceiling.Tag = BitConverter.ToInt32(data, ptr + 40); + ceiling.OldDirection = BitConverter.ToInt32(data, ptr + 44); + ptr += 48; + + thinkers.Add(ceiling); + sa.AddActiveCeiling(ceiling); + break; + + case SpecialClass.Door: + PadPointer(); + var door = new VerticalDoor(world); + door.ThinkerState = ReadThinkerState(data, ptr + 8); + door.Type = (VerticalDoorType)BitConverter.ToInt32(data, ptr + 12); + door.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 16)]; + door.Sector.SpecialData = door; + door.TopHeight = new Fixed(BitConverter.ToInt32(data, ptr + 20)); + door.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 24)); + door.Direction = BitConverter.ToInt32(data, ptr + 28); + door.TopWait = BitConverter.ToInt32(data, ptr + 32); + door.TopCountDown = BitConverter.ToInt32(data, ptr + 36); + ptr += 40; + + thinkers.Add(door); + break; + + case SpecialClass.Floor: + PadPointer(); + var floor = new FloorMove(world); + floor.ThinkerState = ReadThinkerState(data, ptr + 8); + floor.Type = (FloorMoveType)BitConverter.ToInt32(data, ptr + 12); + floor.Crush = BitConverter.ToInt32(data, ptr + 16) != 0; + floor.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 20)]; + floor.Sector.SpecialData = floor; + floor.Direction = BitConverter.ToInt32(data, ptr + 24); + floor.NewSpecial = (SectorSpecial)BitConverter.ToInt32(data, ptr + 28); + floor.Texture = BitConverter.ToInt32(data, ptr + 32); + floor.FloorDestHeight = new Fixed(BitConverter.ToInt32(data, ptr + 36)); + floor.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 40)); + ptr += 44; + + thinkers.Add(floor); + break; + + case SpecialClass.Plat: + PadPointer(); + var plat = new Platform(world); + plat.ThinkerState = ReadThinkerState(data, ptr + 8); + plat.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 12)]; + plat.Sector.SpecialData = plat; + plat.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 16)); + plat.Low = new Fixed(BitConverter.ToInt32(data, ptr + 20)); + plat.High = new Fixed(BitConverter.ToInt32(data, ptr + 24)); + plat.Wait = BitConverter.ToInt32(data, ptr + 28); + plat.Count = BitConverter.ToInt32(data, ptr + 32); + plat.Status = (PlatformState)BitConverter.ToInt32(data, ptr + 36); + plat.OldStatus = (PlatformState)BitConverter.ToInt32(data, ptr + 40); + plat.Crush = BitConverter.ToInt32(data, ptr + 44) != 0; + plat.Tag = BitConverter.ToInt32(data, ptr + 48); + plat.Type = (PlatformType)BitConverter.ToInt32(data, ptr + 52); + ptr += 56; + + thinkers.Add(plat); + sa.AddActivePlatform(plat); + break; + + case SpecialClass.Flash: + PadPointer(); + var flash = new LightFlash(world); + flash.ThinkerState = ReadThinkerState(data, ptr + 8); + flash.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 12)]; + flash.Count = BitConverter.ToInt32(data, ptr + 16); + flash.MaxLight = BitConverter.ToInt32(data, ptr + 20); + flash.MinLight = BitConverter.ToInt32(data, ptr + 24); + flash.MaxTime = BitConverter.ToInt32(data, ptr + 28); + flash.MinTime = BitConverter.ToInt32(data, ptr + 32); + ptr += 36; + + thinkers.Add(flash); + break; + + case SpecialClass.Strobe: + PadPointer(); + var strobe = new StrobeFlash(world); + strobe.ThinkerState = ReadThinkerState(data, ptr + 8); + strobe.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 12)]; + strobe.Count = BitConverter.ToInt32(data, ptr + 16); + strobe.MinLight = BitConverter.ToInt32(data, ptr + 20); + strobe.MaxLight = BitConverter.ToInt32(data, ptr + 24); + strobe.DarkTime = BitConverter.ToInt32(data, ptr + 28); + strobe.BrightTime = BitConverter.ToInt32(data, ptr + 32); + ptr += 36; + + thinkers.Add(strobe); + break; + + case SpecialClass.Glow: + PadPointer(); + var glow = new GlowingLight(world); + glow.ThinkerState = ReadThinkerState(data, ptr + 8); + glow.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 12)]; + glow.MinLight = BitConverter.ToInt32(data, ptr + 16); + glow.MaxLight = BitConverter.ToInt32(data, ptr + 20); + glow.Direction = BitConverter.ToInt32(data, ptr + 24); + ptr += 28; + + thinkers.Add(glow); + break; + + default: + throw new Exception("Unknown special in savegame!"); + } + } + } + + private static ThinkerState ReadThinkerState(byte[] data, int p) + { + switch (BitConverter.ToInt32(data, p)) + { + case 0: + return ThinkerState.InStasis; + default: + return ThinkerState.Active; + } + } + + private static int UnArchivePlayer(Player player, byte[] data, int p) + { + player.Clear(); + + player.PlayerState = (PlayerState)BitConverter.ToInt32(data, p + 4); + player.ViewZ = new Fixed(BitConverter.ToInt32(data, p + 16)); + player.ViewHeight = new Fixed(BitConverter.ToInt32(data, p + 20)); + player.DeltaViewHeight = new Fixed(BitConverter.ToInt32(data, p + 24)); + player.Bob = new Fixed(BitConverter.ToInt32(data, p + 28)); + player.Health = BitConverter.ToInt32(data, p + 32); + player.ArmorPoints = BitConverter.ToInt32(data, p + 36); + player.ArmorType = BitConverter.ToInt32(data, p + 40); + for (var i = 0; i < (int)PowerType.Count; i++) + { + player.Powers[i] = BitConverter.ToInt32(data, p + 44 + 4 * i); + } + for (var i = 0; i < (int)PowerType.Count; i++) + { + player.Cards[i] = BitConverter.ToInt32(data, p + 68 + 4 * i) != 0; + } + player.Backpack = BitConverter.ToInt32(data, p + 92) != 0; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + player.Frags[i] = BitConverter.ToInt32(data, p + 96 + 4 * i); + } + player.ReadyWeapon = (WeaponType)BitConverter.ToInt32(data, p + 112); + player.PendingWeapon = (WeaponType)BitConverter.ToInt32(data, p + 116); + for (var i = 0; i < (int)WeaponType.Count; i++) + { + player.WeaponOwned[i] = BitConverter.ToInt32(data, p + 120 + 4 * i) != 0; + } + for (var i = 0; i < (int)AmmoType.Count; i++) + { + player.Ammo[i] = BitConverter.ToInt32(data, p + 156 + 4 * i); + } + for (var i = 0; i < (int)AmmoType.Count; i++) + { + player.MaxAmmo[i] = BitConverter.ToInt32(data, p + 172 + 4 * i); + } + player.AttackDown = BitConverter.ToInt32(data, p + 188) != 0; + player.UseDown = BitConverter.ToInt32(data, p + 192) != 0; + player.Cheats = (CheatFlags)BitConverter.ToInt32(data, p + 196); + player.Refire = BitConverter.ToInt32(data, p + 200); + player.KillCount = BitConverter.ToInt32(data, p + 204); + player.ItemCount = BitConverter.ToInt32(data, p + 208); + player.SecretCount = BitConverter.ToInt32(data, p + 212); + player.DamageCount = BitConverter.ToInt32(data, p + 220); + player.BonusCount = BitConverter.ToInt32(data, p + 224); + player.ExtraLight = BitConverter.ToInt32(data, p + 232); + player.FixedColorMap = BitConverter.ToInt32(data, p + 236); + player.ColorMap = BitConverter.ToInt32(data, p + 240); + for (var i = 0; i < (int)PlayerSprite.Count; i++) + { + player.PlayerSprites[i].State = DoomInfo.States[BitConverter.ToInt32(data, p + 244 + 16 * i)]; + if (player.PlayerSprites[i].State.Number == (int)MobjState.Null) + { + player.PlayerSprites[i].State = null; + } + player.PlayerSprites[i].Tics = BitConverter.ToInt32(data, p + 244 + 16 * i + 4); + player.PlayerSprites[i].Sx = new Fixed(BitConverter.ToInt32(data, p + 244 + 16 * i + 8)); + player.PlayerSprites[i].Sy = new Fixed(BitConverter.ToInt32(data, p + 244 + 16 * i + 12)); + } + player.DidSecret = BitConverter.ToInt32(data, p + 276) != 0; + + return p + 280; + } + + private static int UnArchiveSector(Sector sector, byte[] data, int p) + { + sector.FloorHeight = Fixed.FromInt(BitConverter.ToInt16(data, p)); + sector.CeilingHeight = Fixed.FromInt(BitConverter.ToInt16(data, p + 2)); + sector.FloorFlat = BitConverter.ToInt16(data, p + 4); + sector.CeilingFlat = BitConverter.ToInt16(data, p + 6); + sector.LightLevel = BitConverter.ToInt16(data, p + 8); + sector.Special = (SectorSpecial)BitConverter.ToInt16(data, p + 10); + sector.Tag = BitConverter.ToInt16(data, p + 12); + sector.SpecialData = null; + sector.SoundTarget = null; + return p + 14; + } + + private static int UnArchiveLine(LineDef line, byte[] data, int p) + { + line.Flags = (LineFlags)BitConverter.ToInt16(data, p); + line.Special = (LineSpecial)BitConverter.ToInt16(data, p + 2); + line.Tag = BitConverter.ToInt16(data, p + 4); + p += 6; + + if (line.FrontSide != null) + { + var side = line.FrontSide; + side.TextureOffset = Fixed.FromInt(BitConverter.ToInt16(data, p)); + side.RowOffset = Fixed.FromInt(BitConverter.ToInt16(data, p + 2)); + side.TopTexture = BitConverter.ToInt16(data, p + 4); + side.BottomTexture = BitConverter.ToInt16(data, p + 6); + side.MiddleTexture = BitConverter.ToInt16(data, p + 8); + p += 10; + } + + if (line.BackSide != null) + { + var side = line.BackSide; + side.TextureOffset = Fixed.FromInt(BitConverter.ToInt16(data, p)); + side.RowOffset = Fixed.FromInt(BitConverter.ToInt16(data, p + 2)); + side.TopTexture = BitConverter.ToInt16(data, p + 4); + side.BottomTexture = BitConverter.ToInt16(data, p + 6); + side.MiddleTexture = BitConverter.ToInt16(data, p + 8); + p += 10; + } + + return p; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/TicCmd.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/TicCmd.cs new file mode 100644 index 00000000..04d54ec8 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/TicCmd.cs @@ -0,0 +1,69 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class TicCmd + { + private sbyte forwardMove; + private sbyte sideMove; + private short angleTurn; + private byte buttons; + + public void Clear() + { + forwardMove = 0; + sideMove = 0; + angleTurn = 0; + buttons = 0; + } + + public void CopyFrom(TicCmd cmd) + { + forwardMove = cmd.forwardMove; + sideMove = cmd.sideMove; + angleTurn = cmd.angleTurn; + buttons = cmd.buttons; + } + + public sbyte ForwardMove + { + get => forwardMove; + set => forwardMove = value; + } + + public sbyte SideMove + { + get => sideMove; + set => sideMove = value; + } + + public short AngleTurn + { + get => angleTurn; + set => angleTurn = value; + } + + public byte Buttons + { + get => buttons; + set => buttons = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/TicCmdButtons.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/TicCmdButtons.cs new file mode 100644 index 00000000..f25ef05a --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/TicCmdButtons.cs @@ -0,0 +1,44 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static class TicCmdButtons + { + public static readonly byte Attack = 1; + + // Use button, to open doors, activate switches. + public static readonly byte Use = 2; + + // Flag: game events, not really buttons. + public static readonly byte Special = 128; + public static readonly byte SpecialMask = 3; + + // Flag, weapon change pending. + // If true, the next 3 bits hold weapon num. + public static readonly byte Change = 4; + + // The 3bit weapon mask and shift, convenience. + public static readonly byte WeaponMask = 8 + 16 + 32; + public static readonly byte WeaponShift = 3; + + // Pause the game. + public static readonly byte Pause = 1; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/UpdateResult.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/UpdateResult.cs new file mode 100644 index 00000000..08b3bfe3 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Game/UpdateResult.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum UpdateResult + { + None, + Completed, + NeedWipe + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/AnimationDef.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/AnimationDef.cs new file mode 100644 index 00000000..2d537862 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/AnimationDef.cs @@ -0,0 +1,42 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class AnimationDef + { + private bool isTexture; + private string endName; + private string startName; + private int speed; + + public AnimationDef(bool isTexture, string endName, string startName, int speed) + { + this.isTexture = isTexture; + this.endName = endName; + this.startName = startName; + this.speed = speed; + } + + public bool IsTexture => isTexture; + public string EndName => endName; + public string StartName => startName; + public int Speed => speed; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/ColorMap.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/ColorMap.cs new file mode 100644 index 00000000..1fd83a78 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/ColorMap.cs @@ -0,0 +1,73 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Runtime.ExceptionServices; + +namespace ManagedDoom +{ + public sealed class ColorMap + { + public static readonly int Inverse = 32; + + private byte[][] data; + + public ColorMap(Wad wad) + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Load color map: "); + + var raw = wad.ReadLump("COLORMAP"); + var num = raw.Length / 256; + data = new byte[num][]; + for (var i = 0; i < num; i++) + { + data[i] = new byte[256]; + var offset = 256 * i; + for (var c = 0; c < 256; c++) + { + data[i][c] = raw[offset + c]; + } + } + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + ExceptionDispatchInfo.Throw(e); + } + } + + public byte[] this[int index] + { + get + { + return data[index]; + } + } + + public byte[] FullBright + { + get + { + return data[0]; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Column.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Column.cs new file mode 100644 index 00000000..ea0ff2f8 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Column.cs @@ -0,0 +1,44 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Column + { + public const int Last = 0xFF; + + private int topDelta; + private byte[] data; + private int offset; + private int length; + + public Column(int topDelta, byte[] data, int offset, int length) + { + this.topDelta = topDelta; + this.data = data; + this.offset = offset; + this.length = length; + } + + public int TopDelta => topDelta; + public byte[] Data => data; + public int Offset => offset; + public int Length => length; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Dummy.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Dummy.cs new file mode 100644 index 00000000..a7980f16 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Dummy.cs @@ -0,0 +1,125 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public static class Dummy + { + private static Patch dummyPatch; + + public static Patch GetPatch() + { + if (dummyPatch != null) + { + return dummyPatch; + } + else + { + var width = 64; + var height = 128; + + var data = new byte[height + 32]; + for (var y = 0; y < data.Length; y++) + { + data[y] = y / 32 % 2 == 0 ? (byte)80 : (byte)96; + } + + var columns = new Column[width][]; + var c1 = new Column[] { new Column(0, data, 0, height) }; + var c2 = new Column[] { new Column(0, data, 32, height) }; + for (var x = 0; x < width; x++) + { + columns[x] = x / 32 % 2 == 0 ? c1 : c2; + } + + dummyPatch = new Patch("DUMMY", width, height, 32, 128, columns); + + return dummyPatch; + } + } + + + + private static Dictionary dummyTextures = new Dictionary(); + + public static Texture GetTexture(int height) + { + if (dummyTextures.ContainsKey(height)) + { + return dummyTextures[height]; + } + else + { + var patch = new TexturePatch[] { new TexturePatch(0, 0, GetPatch()) }; + + dummyTextures.Add(height, new Texture("DUMMY", false, 64, height, patch)); + + return dummyTextures[height]; + } + } + + + + private static Flat dummyFlat; + + public static Flat GetFlat() + { + if (dummyFlat != null) + { + return dummyFlat; + } + else + { + var data = new byte[64 * 64]; + var spot = 0; + for (var y = 0; y < 64; y++) + { + for (var x = 0; x < 64; x++) + { + data[spot] = ((x / 32) ^ (y / 32)) == 0 ? (byte)80 : (byte)96; + spot++; + } + } + + dummyFlat = new Flat("DUMMY", data); + + return dummyFlat; + } + } + + + + private static Flat dummySkyFlat; + + public static Flat GetSkyFlat() + { + if (dummySkyFlat != null) + { + return dummySkyFlat; + } + else + { + dummySkyFlat = new Flat("DUMMY", GetFlat().Data); + + return dummySkyFlat; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Flat.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Flat.cs new file mode 100644 index 00000000..8e56f2e7 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Flat.cs @@ -0,0 +1,46 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Flat + { + private string name; + private byte[] data; + + public Flat(string name, byte[] data) + { + this.name = name; + this.data = data; + } + + public static Flat FromData(string name, byte[] data) + { + return new Flat(name, data); + } + + public override string ToString() + { + return name; + } + + public string Name => name; + public byte[] Data => data; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/FlatLookup.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/FlatLookup.cs new file mode 100644 index 00000000..45b7efa4 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/FlatLookup.cs @@ -0,0 +1,278 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.ExceptionServices; + +namespace ManagedDoom +{ + public sealed class FlatLookup : IReadOnlyList + { + private Flat[] flats; + + private Dictionary nameToFlat; + private Dictionary nameToNumber; + + private int skyFlatNumber; + private Flat skyFlat; + + public FlatLookup(Wad wad) : this(wad, false) + { + } + + public FlatLookup(Wad wad, bool useDummy) + { + if (!useDummy) + { + var fStartCount = CountLump(wad, "F_START"); + var fEndCount = CountLump(wad, "F_END"); + var ffStartCount = CountLump(wad, "FF_START"); + var ffEndCount = CountLump(wad, "FF_END"); + + // Usual case. + var standard = + fStartCount == 1 && + fEndCount == 1 && + ffStartCount == 0 && + ffEndCount == 0; + + // A trick to add custom flats is used. + // https://www.doomworld.com/tutorials/fx2.php + var customFlatTrick = + fStartCount == 1 && + fEndCount >= 2; + + // Need deutex to add flats. + var deutexMerge = + fStartCount + ffStartCount >= 2 && + fEndCount + ffEndCount >= 2; + + if (standard || customFlatTrick) + { + InitStandard(wad); + } + else if (deutexMerge) + { + InitDeuTexMerge(wad); + } + else + { + throw new Exception("Faild to read flats."); + } + } + else + { + InitDummy(wad); + } + } + + private void InitStandard(Wad wad) + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Load flats: "); + + var firstFlat = wad.GetLumpNumber("F_START") + 1; + var lastFlat = wad.GetLumpNumber("F_END") - 1; + var count = lastFlat - firstFlat + 1; + + flats = new Flat[count]; + + nameToFlat = new Dictionary(); + nameToNumber = new Dictionary(); + + for (var lump = firstFlat; lump <= lastFlat; lump++) + { + if (wad.GetLumpSize(lump) != 4096) + { + continue; + } + + var number = lump - firstFlat; + var name = wad.LumpInfos[lump].Name; + var flat = new Flat(name, wad.ReadLump(lump)); + + flats[number] = flat; + nameToFlat[name] = flat; + nameToNumber[name] = number; + } + + skyFlatNumber = nameToNumber["F_SKY1"]; + skyFlat = nameToFlat["F_SKY1"]; + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK (" + nameToFlat.Count + " flats)"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + ExceptionDispatchInfo.Throw(e); + } + } + + private void InitDeuTexMerge(Wad wad) + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Load flats: "); + + var allFlats = new List(); + var flatZone = false; + for (var lump = 0; lump < wad.LumpInfos.Count; lump++) + { + var name = wad.LumpInfos[lump].Name; + if (flatZone) + { + if (name == "F_END" || name == "FF_END") + { + flatZone = false; + } + else + { + allFlats.Add(lump); + } + } + else + { + if (name == "F_START" || name == "FF_START") + { + flatZone = true; + } + } + } + allFlats.Reverse(); + + var dupCheck = new HashSet(); + var distinctFlats = new List(); + foreach (var lump in allFlats) + { + if (!dupCheck.Contains(wad.LumpInfos[lump].Name)) + { + distinctFlats.Add(lump); + dupCheck.Add(wad.LumpInfos[lump].Name); + } + } + distinctFlats.Reverse(); + + flats = new Flat[distinctFlats.Count]; + + nameToFlat = new Dictionary(); + nameToNumber = new Dictionary(); + + for (var number = 0; number < flats.Length; number++) + { + var lump = distinctFlats[number]; + + if (wad.GetLumpSize(lump) != 4096) + { + continue; + } + + var name = wad.LumpInfos[lump].Name; + var flat = new Flat(name, wad.ReadLump(lump)); + + flats[number] = flat; + nameToFlat[name] = flat; + nameToNumber[name] = number; + } + + skyFlatNumber = nameToNumber["F_SKY1"]; + skyFlat = nameToFlat["F_SKY1"]; + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK (" + nameToFlat.Count + " flats)"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + ExceptionDispatchInfo.Throw(e); + } + } + + private void InitDummy(Wad wad) + { + var firstFlat = wad.GetLumpNumber("F_START") + 1; + var lastFlat = wad.GetLumpNumber("F_END") - 1; + var count = lastFlat - firstFlat + 1; + + flats = new Flat[count]; + + nameToFlat = new Dictionary(); + nameToNumber = new Dictionary(); + + for (var lump = firstFlat; lump <= lastFlat; lump++) + { + if (wad.GetLumpSize(lump) != 4096) + { + continue; + } + + var number = lump - firstFlat; + var name = wad.LumpInfos[lump].Name; + var flat = name != "F_SKY1" ? Dummy.GetFlat() : Dummy.GetSkyFlat(); + + flats[number] = flat; + nameToFlat[name] = flat; + nameToNumber[name] = number; + } + + skyFlatNumber = nameToNumber["F_SKY1"]; + skyFlat = nameToFlat["F_SKY1"]; + } + + public int GetNumber(string name) + { + if (nameToNumber.ContainsKey(name)) + { + return nameToNumber[name]; + } + else + { + return -1; + } + } + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)flats).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return flats.GetEnumerator(); + } + + private static int CountLump(Wad wad, string name) + { + var count = 0; + foreach (var lump in wad.LumpInfos) + { + if (lump.Name == name) + { + count++; + } + } + return count; + } + + public int Count => flats.Length; + public Flat this[int num] => flats[num]; + public Flat this[string name] => nameToFlat[name]; + public int SkyFlatNumber => skyFlatNumber; + public Flat SkyFlat => skyFlat; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Palette.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Palette.cs new file mode 100644 index 00000000..399131b3 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Palette.cs @@ -0,0 +1,96 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Runtime.ExceptionServices; + +namespace ManagedDoom +{ + public sealed class Palette + { + public static readonly int DamageStart = 1; + public static readonly int DamageCount = 8; + + public static readonly int BonusStart = 9; + public static readonly int BonusCount = 4; + + public static readonly int IronFeet = 13; + + private byte[] data; + + private uint[][] palettes; + + public Palette(Wad wad) + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Load palette: "); + + data = wad.ReadLump("PLAYPAL"); + + var count = data.Length / (3 * 256); + palettes = new uint[count][]; + for (var i = 0; i < palettes.Length; i++) + { + palettes[i] = new uint[256]; + } + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + ExceptionDispatchInfo.Throw(e); + } + } + + public void ResetColors(double p) + { + for (var i = 0; i < palettes.Length; i++) + { + var paletteOffset = (3 * 256) * i; + for (var j = 0; j < 256; j++) + { + var colorOffset = paletteOffset + 3 * j; + + var r = data[colorOffset]; + var g = data[colorOffset + 1]; + var b = data[colorOffset + 2]; + + r = (byte)Math.Round(255 * CorrectionCurve(r / 255.0, p)); + g = (byte)Math.Round(255 * CorrectionCurve(g / 255.0, p)); + b = (byte)Math.Round(255 * CorrectionCurve(b / 255.0, p)); + + palettes[i][j] = (uint)((r << 0) | (g << 8) | (b << 16) | (255 << 24)); + } + } + } + + private static double CorrectionCurve(double x, double p) + { + return Math.Pow(x, p); + } + + public uint[] this[int paletteNumber] + { + get + { + return palettes[paletteNumber]; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Patch.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Patch.cs new file mode 100644 index 00000000..fbe9f942 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Patch.cs @@ -0,0 +1,129 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class Patch + { + private string name; + private int width; + private int height; + private int leftOffset; + private int topOffset; + private Column[][] columns; + + public Patch( + string name, + int width, + int height, + int leftOffset, + int topOffset, + Column[][] columns) + { + this.name = name; + this.width = width; + this.height = height; + this.leftOffset = leftOffset; + this.topOffset = topOffset; + this.columns = columns; + } + + public static Patch FromData(string name, byte[] data) + { + var width = BitConverter.ToInt16(data, 0); + var height = BitConverter.ToInt16(data, 2); + var leftOffset = BitConverter.ToInt16(data, 4); + var topOffset = BitConverter.ToInt16(data, 6); + + PadData(ref data, width); + + var columns = new Column[width][]; + for (var x = 0; x < width; x++) + { + var cs = new List(); + var p = BitConverter.ToInt32(data, 8 + 4 * x); + while (true) + { + var topDelta = data[p]; + if (topDelta == Column.Last) + { + break; + } + var length = data[p + 1]; + var offset = p + 3; + cs.Add(new Column(topDelta, data, offset, length)); + p += length + 4; + } + columns[x] = cs.ToArray(); + } + + return new Patch( + name, + width, + height, + leftOffset, + topOffset, + columns); + } + + public static Patch FromWad(Wad wad, string name) + { + return FromData(name, wad.ReadLump(name)); + } + + private static void PadData(ref byte[] data, int width) + { + var need = 0; + for (var x = 0; x < width; x++) + { + var p = BitConverter.ToInt32(data, 8 + 4 * x); + while (true) + { + var topDelta = data[p]; + if (topDelta == Column.Last) + { + break; + } + var length = data[p + 1]; + var offset = p + 3; + need = Math.Max(offset + 128, need); + p += length + 4; + } + } + + if (data.Length < need) + { + Array.Resize(ref data, need); + } + } + + public override string ToString() + { + return name; + } + + public string Name => name; + public int Width => width; + public int Height => height; + public int LeftOffset => leftOffset; + public int TopOffset => topOffset; + public Column[][] Columns => columns; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/PatchCache.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/PatchCache.cs new file mode 100644 index 00000000..76695bea --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/PatchCache.cs @@ -0,0 +1,59 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class PatchCache + { + private Wad wad; + private Dictionary cache; + + public PatchCache(Wad wad) + { + this.wad = wad; + + cache = new Dictionary(); + } + + public Patch this[string name] + { + get + { + Patch patch; + if (!cache.TryGetValue(name, out patch)) + { + patch = Patch.FromWad(wad, name); + cache.Add(name, patch); + } + return patch; + } + } + + public int GetWidth(string name) + { + return this[name].Width; + } + + public int GetHeight(string name) + { + return this[name].Height; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Sprite.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Sprite.cs new file mode 100644 index 00000000..e0ccb3f0 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Sprite.cs @@ -0,0 +1,164 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum Sprite + { + TROO, + SHTG, + PUNG, + PISG, + PISF, + SHTF, + SHT2, + CHGG, + CHGF, + MISG, + MISF, + SAWG, + PLSG, + PLSF, + BFGG, + BFGF, + BLUD, + PUFF, + BAL1, + BAL2, + PLSS, + PLSE, + MISL, + BFS1, + BFE1, + BFE2, + TFOG, + IFOG, + PLAY, + POSS, + SPOS, + VILE, + FIRE, + FATB, + FBXP, + SKEL, + MANF, + FATT, + CPOS, + SARG, + HEAD, + BAL7, + BOSS, + BOS2, + SKUL, + SPID, + BSPI, + APLS, + APBX, + CYBR, + PAIN, + SSWV, + KEEN, + BBRN, + BOSF, + ARM1, + ARM2, + BAR1, + BEXP, + FCAN, + BON1, + BON2, + BKEY, + RKEY, + YKEY, + BSKU, + RSKU, + YSKU, + STIM, + MEDI, + SOUL, + PINV, + PSTR, + PINS, + MEGA, + SUIT, + PMAP, + PVIS, + CLIP, + AMMO, + ROCK, + BROK, + CELL, + CELP, + SHEL, + SBOX, + BPAK, + BFUG, + MGUN, + CSAW, + LAUN, + PLAS, + SHOT, + SGN2, + COLU, + SMT2, + GOR1, + POL2, + POL5, + POL4, + POL3, + POL1, + POL6, + GOR2, + GOR3, + GOR4, + GOR5, + SMIT, + COL1, + COL2, + COL3, + COL4, + CAND, + CBRA, + COL6, + TRE1, + TRE2, + ELEC, + CEYE, + FSKU, + COL5, + TBLU, + TGRN, + TRED, + SMBT, + SMGT, + SMRT, + HDB1, + HDB2, + HDB3, + HDB4, + HDB5, + HDB6, + POB1, + POB2, + BRS1, + TLMP, + TLP2, + Count + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/SpriteDef.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/SpriteDef.cs new file mode 100644 index 00000000..c32c66af --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/SpriteDef.cs @@ -0,0 +1,33 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class SpriteDef + { + private SpriteFrame[] frames; + + public SpriteDef(SpriteFrame[] frames) + { + this.frames = frames; + } + + public SpriteFrame[] Frames => frames; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/SpriteFrame.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/SpriteFrame.cs new file mode 100644 index 00000000..18e93624 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/SpriteFrame.cs @@ -0,0 +1,39 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class SpriteFrame + { + private bool rotate; + private Patch[] patches; + private bool[] flip; + + public SpriteFrame(bool rotate, Patch[] patches, bool[] flip) + { + this.rotate = rotate; + this.patches = patches; + this.flip = flip; + } + + public bool Rotate => rotate; + public Patch[] Patches => patches; + public bool[] Flip => flip; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/SpriteLookup.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/SpriteLookup.cs new file mode 100644 index 00000000..dc3ef91b --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/SpriteLookup.cs @@ -0,0 +1,238 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.Runtime.ExceptionServices; + +namespace ManagedDoom +{ + public sealed class SpriteLookup + { + private SpriteDef[] spriteDefs; + + public SpriteLookup(Wad wad) : this(wad, false) + { + } + + public SpriteLookup(Wad wad, bool useDummy) + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Load sprites: "); + + var temp = new Dictionary>(); + for (var i = 0; i < (int)Sprite.Count; i++) + { + temp.Add(DoomInfo.SpriteNames[i], new List()); + } + + var cache = new Dictionary(); + + foreach (var lump in EnumerateSprites(wad)) + { + var name = wad.LumpInfos[lump].Name.Substring(0, 4); + + if (!temp.ContainsKey(name)) + { + continue; + } + + var list = temp[name]; + + { + var frame = wad.LumpInfos[lump].Name[4] - 'A'; + var rotation = wad.LumpInfos[lump].Name[5] - '0'; + + while (list.Count < frame + 1) + { + list.Add(new SpriteInfo()); + } + + if (rotation == 0) + { + for (var i = 0; i < 8; i++) + { + if (list[frame].Patches[i] == null) + { + list[frame].Patches[i] = CachedRead(lump, wad, cache, useDummy); + list[frame].Flip[i] = false; + } + } + } + else + { + if (list[frame].Patches[rotation - 1] == null) + { + list[frame].Patches[rotation - 1] = CachedRead(lump, wad, cache, useDummy); + list[frame].Flip[rotation - 1] = false; + } + } + } + + if (wad.LumpInfos[lump].Name.Length == 8) + { + var frame = wad.LumpInfos[lump].Name[6] - 'A'; + var rotation = wad.LumpInfos[lump].Name[7] - '0'; + + while (list.Count < frame + 1) + { + list.Add(new SpriteInfo()); + } + + if (rotation == 0) + { + for (var i = 0; i < 8; i++) + { + if (list[frame].Patches[i] == null) + { + list[frame].Patches[i] = CachedRead(lump, wad, cache, useDummy); + list[frame].Flip[i] = true; + } + } + } + else + { + if (list[frame].Patches[rotation - 1] == null) + { + list[frame].Patches[rotation - 1] = CachedRead(lump, wad, cache, useDummy); + list[frame].Flip[rotation - 1] = true; + } + } + } + } + + spriteDefs = new SpriteDef[(int)Sprite.Count]; + for (var i = 0; i < spriteDefs.Length; i++) + { + var list = temp[DoomInfo.SpriteNames[i]]; + + var frames = new SpriteFrame[list.Count]; + for (var j = 0; j < frames.Length; j++) + { + list[j].CheckCompletion(); + + var frame = new SpriteFrame(list[j].HasRotation(), list[j].Patches, list[j].Flip); + frames[j] = frame; + } + + spriteDefs[i] = new SpriteDef(frames); + } + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK (" + cache.Count + " sprites)"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + ExceptionDispatchInfo.Throw(e); + } + } + + private static IEnumerable EnumerateSprites(Wad wad) + { + var spriteSection = false; + + for (var lump = wad.LumpInfos.Count - 1; lump >= 0; lump--) + { + var name = wad.LumpInfos[lump].Name; + + if (name.StartsWith("S")) + { + if (name.EndsWith("_END")) + { + spriteSection = true; + continue; + } + else if (name.EndsWith("_START")) + { + spriteSection = false; + continue; + } + } + + if (spriteSection) + { + if (wad.LumpInfos[lump].Size > 0) + { + yield return lump; + } + } + } + } + + private static Patch CachedRead(int lump, Wad wad, Dictionary cache, bool useDummy) + { + if (useDummy) + { + return Dummy.GetPatch(); + } + + if (!cache.ContainsKey(lump)) + { + var name = wad.LumpInfos[lump].Name; + cache.Add(lump, Patch.FromData(name, wad.ReadLump(lump))); + } + + return cache[lump]; + } + + + private class SpriteInfo + { + public Patch[] Patches; + public bool[] Flip; + + public SpriteInfo() + { + Patches = new Patch[8]; + Flip = new bool[8]; + } + + public void CheckCompletion() + { + for (var i = 0; i < Patches.Length; i++) + { + if (Patches[i] == null) + { + throw new Exception("Missing sprite!"); + } + } + } + + public bool HasRotation() + { + for (var i = 1; i < Patches.Length; i++) + { + if (Patches[i] != Patches[0]) + { + return true; + } + } + + return false; + } + } + + public SpriteDef this[Sprite sprite] + { + get + { + return spriteDefs[(int)sprite]; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Texture.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Texture.cs new file mode 100644 index 00000000..33af45e2 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/Texture.cs @@ -0,0 +1,194 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class Texture + { + private string name; + private bool masked; + private int width; + private int height; + private TexturePatch[] patches; + private Patch composite; + + public Texture( + string name, + bool masked, + int width, + int height, + TexturePatch[] patches) + { + this.name = name; + this.masked = masked; + this.width = width; + this.height = height; + this.patches = patches; + composite = GenerateComposite(name, width, height, patches); + } + + public static Texture FromData(byte[] data, int offset, Patch[] patchLookup) + { + var name = DoomInterop.ToString(data, offset, 8); + var masked = BitConverter.ToInt32(data, offset + 8); + var width = BitConverter.ToInt16(data, offset + 12); + var height = BitConverter.ToInt16(data, offset + 14); + var patchCount = BitConverter.ToInt16(data, offset + 20); + var patches = new TexturePatch[patchCount]; + for (var i = 0; i < patchCount; i++) + { + var patchOffset = offset + 22 + TexturePatch.DataSize * i; + patches[i] = TexturePatch.FromData(data, patchOffset, patchLookup); + } + + return new Texture( + name, + masked != 0, + width, + height, + patches); + } + + public static string GetName(byte[] data, int offset) + { + return DoomInterop.ToString(data, offset, 8); + } + + public static int GetHeight(byte[] data, int offset) + { + return BitConverter.ToInt16(data, offset + 14); + } + + private static Patch GenerateComposite(string name, int width, int height, TexturePatch[] patches) + { + var patchCount = new int[width]; + var columns = new Column[width][]; + var compositeColumnCount = 0; + + foreach (var patch in patches) + { + var left = patch.OriginX; + var right = left + patch.Width; + + var start = Math.Max(left, 0); + var end = Math.Min(right, width); + + for (var x = start; x < end; x++) + { + patchCount[x]++; + if (patchCount[x] == 2) + { + compositeColumnCount++; + } + columns[x] = patch.Columns[x - patch.OriginX]; + } + } + + var padding = Math.Max(128 - height, 0); + var data = new byte[height * compositeColumnCount + padding]; + var i = 0; + for (var x = 0; x < width; x++) + { + if (patchCount[x] == 0) + { + throw new Exception(); + } + + if (patchCount[x] >= 2) + { + var column = new Column(0, data, height * i, height); + + foreach (var patch in patches) + { + var px = x - patch.OriginX; + if (px < 0 || px >= patch.Width) + { + continue; + } + var patchColumn = patch.Columns[px]; + DrawColumnInCache( + patchColumn, + column.Data, + column.Offset, + patch.OriginY, + height); + } + + columns[x] = new[] { column }; + + i++; + } + } + + return new Patch(name, width, height, 0, 0, columns); + } + + private static void DrawColumnInCache( + Column[] source, + byte[] destination, + int destinationOffset, + int destinationY, + int destinationHeight) + { + foreach (var column in source) + { + var sourceIndex = column.Offset; + var destinationIndex = destinationOffset + destinationY + column.TopDelta; + var length = column.Length; + + var topExceedance = -(destinationY + column.TopDelta); + if (topExceedance > 0) + { + sourceIndex += topExceedance; + destinationIndex += topExceedance; + length -= topExceedance; + } + + var bottomExceedance = destinationY + column.TopDelta + column.Length - destinationHeight; + if (bottomExceedance > 0) + { + length -= bottomExceedance; + } + + if (length > 0) + { + Array.Copy( + column.Data, + sourceIndex, + destination, + destinationIndex, + length); + } + } + } + + public override string ToString() + { + return name; + } + + public string Name => name; + public bool Masked => masked; + public int Width => width; + public int Height => height; + public IReadOnlyList Patches => patches; + public Patch Composite => composite; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TextureAnimation.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TextureAnimation.cs new file mode 100644 index 00000000..258e2abf --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TextureAnimation.cs @@ -0,0 +1,89 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.Runtime.ExceptionServices; + +namespace ManagedDoom +{ + public sealed class TextureAnimation + { + private TextureAnimationInfo[] animations; + + public TextureAnimation(TextureLookup textures, FlatLookup flats) + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Load texture animation info: "); + + var list = new List(); + + foreach (var animDef in DoomInfo.TextureAnimation) + { + int picNum; + int basePic; + if (animDef.IsTexture) + { + if (textures.GetNumber(animDef.StartName) == -1) + { + continue; + } + + picNum = textures.GetNumber(animDef.EndName); + basePic = textures.GetNumber(animDef.StartName); + } + else + { + if (flats.GetNumber(animDef.StartName) == -1) + { + continue; + } + + picNum = flats.GetNumber(animDef.EndName); + basePic = flats.GetNumber(animDef.StartName); + } + + var anim = new TextureAnimationInfo( + animDef.IsTexture, + picNum, + basePic, + picNum - basePic + 1, + animDef.Speed); + + if (anim.NumPics < 2) + { + throw new Exception("Bad animation cycle from " + animDef.StartName + " to " + animDef.EndName + "!"); + } + + list.Add(anim); + } + + animations = list.ToArray(); + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + ExceptionDispatchInfo.Throw(e); + } + } + + public TextureAnimationInfo[] Animations => animations; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TextureAnimationInfo.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TextureAnimationInfo.cs new file mode 100644 index 00000000..dc35939a --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TextureAnimationInfo.cs @@ -0,0 +1,45 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class TextureAnimationInfo + { + private bool isTexture; + private int picNum; + private int basePic; + private int numPics; + private int speed; + + public TextureAnimationInfo(bool isTexture, int picNum, int basePic, int numPics, int speed) + { + this.isTexture = isTexture; + this.picNum = picNum; + this.basePic = basePic; + this.numPics = numPics; + this.speed = speed; + } + + public bool IsTexture => isTexture; + public int PicNum => picNum; + public int BasePic => basePic; + public int NumPics => numPics; + public int Speed => speed; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TextureLookup.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TextureLookup.cs new file mode 100644 index 00000000..efb18be2 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TextureLookup.cs @@ -0,0 +1,202 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.ExceptionServices; + +namespace ManagedDoom +{ + public sealed class TextureLookup : IReadOnlyList + { + private List textures; + private Dictionary nameToTexture; + private Dictionary nameToNumber; + + private int[] switchList; + + public TextureLookup(Wad wad) : this(wad, false) + { + } + + public TextureLookup(Wad wad, bool useDummy) + { + if (!useDummy) + { + Init(wad); + } + else + { + InitDummy(wad); + } + + InitSwitchList(); + } + + private void Init(Wad wad) + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Load textures: "); + + textures = new List(); + nameToTexture = new Dictionary(); + nameToNumber = new Dictionary(); + + var patches = LoadPatches(wad); + + for (var n = 1; n <= 2; n++) + { + var lumpNumber = wad.GetLumpNumber("TEXTURE" + n); + if (lumpNumber == -1) + { + break; + } + + var data = wad.ReadLump(lumpNumber); + var count = BitConverter.ToInt32(data, 0); + for (var i = 0; i < count; i++) + { + var offset = BitConverter.ToInt32(data, 4 + 4 * i); + var texture = Texture.FromData(data, offset, patches); + nameToNumber.Add(texture.Name, textures.Count); + textures.Add(texture); + nameToTexture.Add(texture.Name, texture); + } + } + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK (" + nameToTexture.Count + " textures)"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + ExceptionDispatchInfo.Throw(e); + } + } + + private void InitDummy(Wad wad) + { + textures = new List(); + nameToTexture = new Dictionary(); + nameToNumber = new Dictionary(); + + for (var n = 1; n <= 2; n++) + { + var lumpNumber = wad.GetLumpNumber("TEXTURE" + n); + if (lumpNumber == -1) + { + break; + } + + var data = wad.ReadLump(lumpNumber); + var count = BitConverter.ToInt32(data, 0); + for (var i = 0; i < count; i++) + { + var offset = BitConverter.ToInt32(data, 4 + 4 * i); + var name = Texture.GetName(data, offset); + var height = Texture.GetHeight(data, offset); + var texture = Dummy.GetTexture(height); + nameToNumber.Add(name, textures.Count); + textures.Add(texture); + nameToTexture.Add(name, texture); + } + } + } + + private void InitSwitchList() + { + var list = new List(); + foreach (var tuple in DoomInfo.SwitchNames) + { + var texNum1 = GetNumber(tuple.Item1); + var texNum2 = GetNumber(tuple.Item2); + if (texNum1 != -1 && texNum2 != -1) + { + list.Add(texNum1); + list.Add(texNum2); + } + } + switchList = list.ToArray(); + } + + public int GetNumber(string name) + { + if (name[0] == '-') + { + return 0; + } + + int number; + if (nameToNumber.TryGetValue(name, out number)) + { + return number; + } + else + { + return -1; + } + } + + private static Patch[] LoadPatches(Wad wad) + { + var patchNames = LoadPatchNames(wad); + var patches = new Patch[patchNames.Length]; + for (var i = 0; i < patches.Length; i++) + { + var name = patchNames[i]; + + // This check is necessary to avoid crash in DOOM1.WAD. + if (wad.GetLumpNumber(name) == -1) + { + continue; + } + + var data = wad.ReadLump(name); + patches[i] = Patch.FromData(name, data); + } + return patches; + } + + private static string[] LoadPatchNames(Wad wad) + { + var data = wad.ReadLump("PNAMES"); + var count = BitConverter.ToInt32(data, 0); + var names = new string[count]; + for (var i = 0; i < names.Length; i++) + { + names[i] = DoomInterop.ToString(data, 4 + 8 * i, 8); + } + return names; + } + + public IEnumerator GetEnumerator() + { + return textures.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return textures.GetEnumerator(); + } + + public int Count => textures.Count; + public Texture this[int num] => textures[num]; + public Texture this[string name] => nameToTexture[name]; + public int[] SwitchList => switchList; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TexturePatch.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TexturePatch.cs new file mode 100644 index 00000000..31749341 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Graphics/TexturePatch.cs @@ -0,0 +1,59 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class TexturePatch + { + public const int DataSize = 10; + + private int originX; + private int originY; + private Patch patch; + + public TexturePatch( + int originX, + int originY, + Patch patch) + { + this.originX = originX; + this.originY = originY; + this.patch = patch; + } + + public static TexturePatch FromData(byte[] data, int offset, Patch[] patches) + { + var originX = BitConverter.ToInt16(data, offset); + var originY = BitConverter.ToInt16(data, offset + 2); + var patchNum = BitConverter.ToInt16(data, offset + 4); + + return new TexturePatch( + originX, + originY, + patches[patchNum]); + } + + public string Name => patch.Name; + public int OriginX => originX; + public int OriginY => originY; + public int Width => patch.Width; + public int Height => patch.Height; + public Column[][] Columns => patch.Columns; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.AmmoInfos.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.AmmoInfos.cs new file mode 100644 index 00000000..7ba8f696 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.AmmoInfos.cs @@ -0,0 +1,43 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static class AmmoInfos + { + public static readonly int[] Max = new int[] + { + 200, + 50, + 300, + 50 + }; + + public static readonly int[] Clip = new int[] + { + 10, + 4, + 20, + 1 + }; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.BgmNames.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.BgmNames.cs new file mode 100644 index 00000000..1bc92d96 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.BgmNames.cs @@ -0,0 +1,96 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static readonly DoomString[] BgmNames = new DoomString[] + { + new DoomString("NONE"), + new DoomString("E1M1"), + new DoomString("E1M2"), + new DoomString("E1M3"), + new DoomString("E1M4"), + new DoomString("E1M5"), + new DoomString("E1M6"), + new DoomString("E1M7"), + new DoomString("E1M8"), + new DoomString("E1M9"), + new DoomString("E2M1"), + new DoomString("E2M2"), + new DoomString("E2M3"), + new DoomString("E2M4"), + new DoomString("E2M5"), + new DoomString("E2M6"), + new DoomString("E2M7"), + new DoomString("E2M8"), + new DoomString("E2M9"), + new DoomString("E3M1"), + new DoomString("E3M2"), + new DoomString("E3M3"), + new DoomString("E3M4"), + new DoomString("E3M5"), + new DoomString("E3M6"), + new DoomString("E3M7"), + new DoomString("E3M8"), + new DoomString("E3M9"), + new DoomString("INTER"), + new DoomString("INTRO"), + new DoomString("BUNNY"), + new DoomString("VICTOR"), + new DoomString("INTROA"), + new DoomString("RUNNIN"), + new DoomString("STALKS"), + new DoomString("COUNTD"), + new DoomString("BETWEE"), + new DoomString("DOOM"), + new DoomString("THE_DA"), + new DoomString("SHAWN"), + new DoomString("DDTBLU"), + new DoomString("IN_CIT"), + new DoomString("DEAD"), + new DoomString("STLKS2"), + new DoomString("THEDA2"), + new DoomString("DOOM2"), + new DoomString("DDTBL2"), + new DoomString("RUNNI2"), + new DoomString("DEAD2"), + new DoomString("STLKS3"), + new DoomString("ROMERO"), + new DoomString("SHAWN2"), + new DoomString("MESSAG"), + new DoomString("COUNT2"), + new DoomString("DDTBL3"), + new DoomString("AMPIE"), + new DoomString("THEDA3"), + new DoomString("ADRIAN"), + new DoomString("MESSG2"), + new DoomString("ROMER2"), + new DoomString("TENSE"), + new DoomString("SHAWN3"), + new DoomString("OPENIN"), + new DoomString("EVIL"), + new DoomString("ULTIMA"), + new DoomString("READ_M"), + new DoomString("DM2TTL"), + new DoomString("DM2INT") + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.DeHackEdConst.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.DeHackEdConst.cs new file mode 100644 index 00000000..e02923b2 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.DeHackEdConst.cs @@ -0,0 +1,44 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static class DeHackEdConst + { + public static int InitialHealth { get; set; } = 100; + public static int InitialBullets { get; set; } = 50; + public static int MaxHealth { get; set; } = 200; + public static int MaxArmor { get; set; } = 200; + public static int GreenArmorClass { get; set; } = 1; + public static int BlueArmorClass { get; set; } = 2; + public static int MaxSoulsphere { get; set; } = 200; + public static int SoulsphereHealth { get; set; } = 100; + public static int MegasphereHealth { get; set; } = 200; + public static int GodModeHealth { get; set; } = 100; + public static int IdfaArmor { get; set; } = 200; + public static int IdfaArmorClass { get; set; } = 2; + public static int IdkfaArmor { get; set; } = 200; + public static int IdkfaArmorClass { get; set; } = 2; + public static int BfgCellsPerShot { get; set; } = 40; + public static bool MonstersInfight { get; set; } = false; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.MapTitles.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.MapTitles.cs new file mode 100644 index 00000000..8b4236c7 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.MapTitles.cs @@ -0,0 +1,191 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static class MapTitles + { + public static IReadOnlyList> Doom = new DoomString[][] + { + new DoomString[] + { + Strings.HUSTR_E1M1, + Strings.HUSTR_E1M2, + Strings.HUSTR_E1M3, + Strings.HUSTR_E1M4, + Strings.HUSTR_E1M5, + Strings.HUSTR_E1M6, + Strings.HUSTR_E1M7, + Strings.HUSTR_E1M8, + Strings.HUSTR_E1M9 + }, + + new DoomString[] + { + Strings.HUSTR_E2M1, + Strings.HUSTR_E2M2, + Strings.HUSTR_E2M3, + Strings.HUSTR_E2M4, + Strings.HUSTR_E2M5, + Strings.HUSTR_E2M6, + Strings.HUSTR_E2M7, + Strings.HUSTR_E2M8, + Strings.HUSTR_E2M9 + }, + + new DoomString[] + { + Strings.HUSTR_E3M1, + Strings.HUSTR_E3M2, + Strings.HUSTR_E3M3, + Strings.HUSTR_E3M4, + Strings.HUSTR_E3M5, + Strings.HUSTR_E3M6, + Strings.HUSTR_E3M7, + Strings.HUSTR_E3M8, + Strings.HUSTR_E3M9 + }, + + new DoomString[] + { + Strings.HUSTR_E4M1, + Strings.HUSTR_E4M2, + Strings.HUSTR_E4M3, + Strings.HUSTR_E4M4, + Strings.HUSTR_E4M5, + Strings.HUSTR_E4M6, + Strings.HUSTR_E4M7, + Strings.HUSTR_E4M8, + Strings.HUSTR_E4M9 + } + }; + + public static IReadOnlyList Doom2 = new DoomString[] + { + Strings.HUSTR_1, + Strings.HUSTR_2, + Strings.HUSTR_3, + Strings.HUSTR_4, + Strings.HUSTR_5, + Strings.HUSTR_6, + Strings.HUSTR_7, + Strings.HUSTR_8, + Strings.HUSTR_9, + Strings.HUSTR_10, + Strings.HUSTR_11, + Strings.HUSTR_12, + Strings.HUSTR_13, + Strings.HUSTR_14, + Strings.HUSTR_15, + Strings.HUSTR_16, + Strings.HUSTR_17, + Strings.HUSTR_18, + Strings.HUSTR_19, + Strings.HUSTR_20, + Strings.HUSTR_21, + Strings.HUSTR_22, + Strings.HUSTR_23, + Strings.HUSTR_24, + Strings.HUSTR_25, + Strings.HUSTR_26, + Strings.HUSTR_27, + Strings.HUSTR_28, + Strings.HUSTR_29, + Strings.HUSTR_30, + Strings.HUSTR_31, + Strings.HUSTR_32 + }; + + public static IReadOnlyList Plutonia = new DoomString[] + { + Strings.PHUSTR_1, + Strings.PHUSTR_2, + Strings.PHUSTR_3, + Strings.PHUSTR_4, + Strings.PHUSTR_5, + Strings.PHUSTR_6, + Strings.PHUSTR_7, + Strings.PHUSTR_8, + Strings.PHUSTR_9, + Strings.PHUSTR_10, + Strings.PHUSTR_11, + Strings.PHUSTR_12, + Strings.PHUSTR_13, + Strings.PHUSTR_14, + Strings.PHUSTR_15, + Strings.PHUSTR_16, + Strings.PHUSTR_17, + Strings.PHUSTR_18, + Strings.PHUSTR_19, + Strings.PHUSTR_20, + Strings.PHUSTR_21, + Strings.PHUSTR_22, + Strings.PHUSTR_23, + Strings.PHUSTR_24, + Strings.PHUSTR_25, + Strings.PHUSTR_26, + Strings.PHUSTR_27, + Strings.PHUSTR_28, + Strings.PHUSTR_29, + Strings.PHUSTR_30, + Strings.PHUSTR_31, + Strings.PHUSTR_32 + }; + + public static IReadOnlyList Tnt = new DoomString[] + { + Strings.THUSTR_1, + Strings.THUSTR_2, + Strings.THUSTR_3, + Strings.THUSTR_4, + Strings.THUSTR_5, + Strings.THUSTR_6, + Strings.THUSTR_7, + Strings.THUSTR_8, + Strings.THUSTR_9, + Strings.THUSTR_10, + Strings.THUSTR_11, + Strings.THUSTR_12, + Strings.THUSTR_13, + Strings.THUSTR_14, + Strings.THUSTR_15, + Strings.THUSTR_16, + Strings.THUSTR_17, + Strings.THUSTR_18, + Strings.THUSTR_19, + Strings.THUSTR_20, + Strings.THUSTR_21, + Strings.THUSTR_22, + Strings.THUSTR_23, + Strings.THUSTR_24, + Strings.THUSTR_25, + Strings.THUSTR_26, + Strings.THUSTR_27, + Strings.THUSTR_28, + Strings.THUSTR_29, + Strings.THUSTR_30, + Strings.THUSTR_31, + Strings.THUSTR_32 + }; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.MobjActions.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.MobjActions.cs new file mode 100644 index 00000000..e4027ef6 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.MobjActions.cs @@ -0,0 +1,287 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + private class MobjActions + { + public void BFGSpray(World world, Mobj actor) + { + world.WeaponBehavior.BFGSpray(actor); + } + + public void Explode(World world, Mobj actor) + { + world.MonsterBehavior.Explode(actor); + } + + public void Pain(World world, Mobj actor) + { + world.MonsterBehavior.Pain(actor); + } + + public void PlayerScream(World world, Mobj actor) + { + world.PlayerBehavior.PlayerScream(actor); + } + + public void Fall(World world, Mobj actor) + { + world.MonsterBehavior.Fall(actor); + } + + public void XScream(World world, Mobj actor) + { + world.MonsterBehavior.XScream(actor); + } + + public void Look(World world, Mobj actor) + { + world.MonsterBehavior.Look(actor); + } + + public void Chase(World world, Mobj actor) + { + world.MonsterBehavior.Chase(actor); + } + + public void FaceTarget(World world, Mobj actor) + { + world.MonsterBehavior.FaceTarget(actor); + } + + public void PosAttack(World world, Mobj actor) + { + world.MonsterBehavior.PosAttack(actor); + } + + public void Scream(World world, Mobj actor) + { + world.MonsterBehavior.Scream(actor); + } + + public void SPosAttack(World world, Mobj actor) + { + world.MonsterBehavior.SPosAttack(actor); + } + + public void VileChase(World world, Mobj actor) + { + world.MonsterBehavior.VileChase(actor); + } + + public void VileStart(World world, Mobj actor) + { + world.MonsterBehavior.VileStart(actor); + } + + public void VileTarget(World world, Mobj actor) + { + world.MonsterBehavior.VileTarget(actor); + } + + public void VileAttack(World world, Mobj actor) + { + world.MonsterBehavior.VileAttack(actor); + } + + public void StartFire(World world, Mobj actor) + { + world.MonsterBehavior.StartFire(actor); + } + + public void Fire(World world, Mobj actor) + { + world.MonsterBehavior.Fire(actor); + } + + public void FireCrackle(World world, Mobj actor) + { + world.MonsterBehavior.FireCrackle(actor); + } + + public void Tracer(World world, Mobj actor) + { + world.MonsterBehavior.Tracer(actor); + } + + public void SkelWhoosh(World world, Mobj actor) + { + world.MonsterBehavior.SkelWhoosh(actor); + } + + public void SkelFist(World world, Mobj actor) + { + world.MonsterBehavior.SkelFist(actor); + } + + public void SkelMissile(World world, Mobj actor) + { + world.MonsterBehavior.SkelMissile(actor); + } + + public void FatRaise(World world, Mobj actor) + { + world.MonsterBehavior.FatRaise(actor); + } + + public void FatAttack1(World world, Mobj actor) + { + world.MonsterBehavior.FatAttack1(actor); + } + + public void FatAttack2(World world, Mobj actor) + { + world.MonsterBehavior.FatAttack2(actor); + } + + public void FatAttack3(World world, Mobj actor) + { + world.MonsterBehavior.FatAttack3(actor); + } + + public void BossDeath(World world, Mobj actor) + { + world.MonsterBehavior.BossDeath(actor); + } + + public void CPosAttack(World world, Mobj actor) + { + world.MonsterBehavior.CPosAttack(actor); + } + + public void CPosRefire(World world, Mobj actor) + { + world.MonsterBehavior.CPosRefire(actor); + } + + public void TroopAttack(World world, Mobj actor) + { + world.MonsterBehavior.TroopAttack(actor); + } + + public void SargAttack(World world, Mobj actor) + { + world.MonsterBehavior.SargAttack(actor); + } + + public void HeadAttack(World world, Mobj actor) + { + world.MonsterBehavior.HeadAttack(actor); + } + + public void BruisAttack(World world, Mobj actor) + { + world.MonsterBehavior.BruisAttack(actor); + } + + public void SkullAttack(World world, Mobj actor) + { + world.MonsterBehavior.SkullAttack(actor); + } + + public void Metal(World world, Mobj actor) + { + world.MonsterBehavior.Metal(actor); + } + + public void SpidRefire(World world, Mobj actor) + { + world.MonsterBehavior.SpidRefire(actor); + } + + public void BabyMetal(World world, Mobj actor) + { + world.MonsterBehavior.BabyMetal(actor); + } + + public void BspiAttack(World world, Mobj actor) + { + world.MonsterBehavior.BspiAttack(actor); + } + + public void Hoof(World world, Mobj actor) + { + world.MonsterBehavior.Hoof(actor); + } + + public void CyberAttack(World world, Mobj actor) + { + world.MonsterBehavior.CyberAttack(actor); + } + + public void PainAttack(World world, Mobj actor) + { + world.MonsterBehavior.PainAttack(actor); + } + + public void PainDie(World world, Mobj actor) + { + world.MonsterBehavior.PainDie(actor); + } + + public void KeenDie(World world, Mobj actor) + { + world.MonsterBehavior.KeenDie(actor); + } + + public void BrainPain(World world, Mobj actor) + { + world.MonsterBehavior.BrainPain(actor); + } + + public void BrainScream(World world, Mobj actor) + { + world.MonsterBehavior.BrainScream(actor); + } + + public void BrainDie(World world, Mobj actor) + { + world.MonsterBehavior.BrainDie(actor); + } + + public void BrainAwake(World world, Mobj actor) + { + world.MonsterBehavior.BrainAwake(actor); + } + + public void BrainSpit(World world, Mobj actor) + { + world.MonsterBehavior.BrainSpit(actor); + } + + public void SpawnSound(World world, Mobj actor) + { + world.MonsterBehavior.SpawnSound(actor); + } + + public void SpawnFly(World world, Mobj actor) + { + world.MonsterBehavior.SpawnFly(actor); + } + + public void BrainExplode(World world, Mobj actor) + { + world.MonsterBehavior.BrainExplode(actor); + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.MobjInfos.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.MobjInfos.cs new file mode 100644 index 00000000..afca030f --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.MobjInfos.cs @@ -0,0 +1,3590 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static readonly MobjInfo[] MobjInfos = new MobjInfo[] + { + new MobjInfo( // MobjType.Player + -1, // doomEdNum + MobjState.Play, // spawnState + 100, // spawnHealth + MobjState.PlayRun1, // seeState + Sfx.NONE, // seeSound + 0, // reactionTime + Sfx.NONE, // attackSound + MobjState.PlayPain, // painState + 255, // painChance + Sfx.PLPAIN, // painSound + MobjState.Null, // meleeState + MobjState.PlayAtk1, // missileState + MobjState.PlayDie1, // deathState + MobjState.PlayXdie1, // xdeathState + Sfx.PLDETH, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(56), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.DropOff | MobjFlags.PickUp | MobjFlags.NotDeathmatch, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Possessed + 3004, // doomEdNum + MobjState.PossStnd, // spawnState + 20, // spawnHealth + MobjState.PossRun1, // seeState + Sfx.POSIT1, // seeSound + 8, // reactionTime + Sfx.PISTOL, // attackSound + MobjState.PossPain, // painState + 200, // painChance + Sfx.POPAIN, // painSound + MobjState.Null, // meleeState + MobjState.PossAtk1, // missileState + MobjState.PossDie1, // deathState + MobjState.PossXdie1, // xdeathState + Sfx.PODTH1, // deathSound + 8, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(56), // height + 100, // mass + 0, // damage + Sfx.POSACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.PossRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Shotguy + 9, // doomEdNum + MobjState.SposStnd, // spawnState + 30, // spawnHealth + MobjState.SposRun1, // seeState + Sfx.POSIT2, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.SposPain, // painState + 170, // painChance + Sfx.POPAIN, // painSound + MobjState.Null, // meleeState + MobjState.SposAtk1, // missileState + MobjState.SposDie1, // deathState + MobjState.SposXdie1, // xdeathState + Sfx.PODTH2, // deathSound + 8, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(56), // height + 100, // mass + 0, // damage + Sfx.POSACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.SposRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Vile + 64, // doomEdNum + MobjState.VileStnd, // spawnState + 700, // spawnHealth + MobjState.VileRun1, // seeState + Sfx.VILSIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.VilePain, // painState + 10, // painChance + Sfx.VIPAIN, // painSound + MobjState.Null, // meleeState + MobjState.VileAtk1, // missileState + MobjState.VileDie1, // deathState + MobjState.Null, // xdeathState + Sfx.VILDTH, // deathSound + 15, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(56), // height + 500, // mass + 0, // damage + Sfx.VILACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Fire + -1, // doomEdNum + MobjState.Fire1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Undead + 66, // doomEdNum + MobjState.SkelStnd, // spawnState + 300, // spawnHealth + MobjState.SkelRun1, // seeState + Sfx.SKESIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.SkelPain, // painState + 100, // painChance + Sfx.POPAIN, // painSound + MobjState.SkelFist1, // meleeState + MobjState.SkelMiss1, // missileState + MobjState.SkelDie1, // deathState + MobjState.Null, // xdeathState + Sfx.SKEDTH, // deathSound + 10, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(56), // height + 500, // mass + 0, // damage + Sfx.SKEACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.SkelRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Tracer + -1, // doomEdNum + MobjState.Tracer, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.SKEATK, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Traceexp1, // deathState + MobjState.Null, // xdeathState + Sfx.BAREXP, // deathSound + 10 * Fixed.FracUnit, // speed + Fixed.FromInt(11), // radius + Fixed.FromInt(8), // height + 100, // mass + 10, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.Missile | MobjFlags.DropOff | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Smoke + -1, // doomEdNum + MobjState.Smoke1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Fatso + 67, // doomEdNum + MobjState.FattStnd, // spawnState + 600, // spawnHealth + MobjState.FattRun1, // seeState + Sfx.MANSIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.FattPain, // painState + 80, // painChance + Sfx.MNPAIN, // painSound + MobjState.Null, // meleeState + MobjState.FattAtk1, // missileState + MobjState.FattDie1, // deathState + MobjState.Null, // xdeathState + Sfx.MANDTH, // deathSound + 8, // speed + Fixed.FromInt(48), // radius + Fixed.FromInt(64), // height + 1000, // mass + 0, // damage + Sfx.POSACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.FattRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Fatshot + -1, // doomEdNum + MobjState.Fatshot1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.FIRSHT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Fatshotx1, // deathState + MobjState.Null, // xdeathState + Sfx.FIRXPL, // deathSound + 20 * Fixed.FracUnit, // speed + Fixed.FromInt(6), // radius + Fixed.FromInt(8), // height + 100, // mass + 8, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.Missile | MobjFlags.DropOff | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Chainguy + 65, // doomEdNum + MobjState.CposStnd, // spawnState + 70, // spawnHealth + MobjState.CposRun1, // seeState + Sfx.POSIT2, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.CposPain, // painState + 170, // painChance + Sfx.POPAIN, // painSound + MobjState.Null, // meleeState + MobjState.CposAtk1, // missileState + MobjState.CposDie1, // deathState + MobjState.CposXdie1, // xdeathState + Sfx.PODTH2, // deathSound + 8, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(56), // height + 100, // mass + 0, // damage + Sfx.POSACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.CposRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Troop + 3001, // doomEdNum + MobjState.TrooStnd, // spawnState + 60, // spawnHealth + MobjState.TrooRun1, // seeState + Sfx.BGSIT1, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.TrooPain, // painState + 200, // painChance + Sfx.POPAIN, // painSound + MobjState.TrooAtk1, // meleeState + MobjState.TrooAtk1, // missileState + MobjState.TrooDie1, // deathState + MobjState.TrooXdie1, // xdeathState + Sfx.BGDTH1, // deathSound + 8, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(56), // height + 100, // mass + 0, // damage + Sfx.BGACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.TrooRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Sergeant + 3002, // doomEdNum + MobjState.SargStnd, // spawnState + 150, // spawnHealth + MobjState.SargRun1, // seeState + Sfx.SGTSIT, // seeSound + 8, // reactionTime + Sfx.SGTATK, // attackSound + MobjState.SargPain, // painState + 180, // painChance + Sfx.DMPAIN, // painSound + MobjState.SargAtk1, // meleeState + MobjState.Null, // missileState + MobjState.SargDie1, // deathState + MobjState.Null, // xdeathState + Sfx.SGTDTH, // deathSound + 10, // speed + Fixed.FromInt(30), // radius + Fixed.FromInt(56), // height + 400, // mass + 0, // damage + Sfx.DMACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.SargRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Shadows + 58, // doomEdNum + MobjState.SargStnd, // spawnState + 150, // spawnHealth + MobjState.SargRun1, // seeState + Sfx.SGTSIT, // seeSound + 8, // reactionTime + Sfx.SGTATK, // attackSound + MobjState.SargPain, // painState + 180, // painChance + Sfx.DMPAIN, // painSound + MobjState.SargAtk1, // meleeState + MobjState.Null, // missileState + MobjState.SargDie1, // deathState + MobjState.Null, // xdeathState + Sfx.SGTDTH, // deathSound + 10, // speed + Fixed.FromInt(30), // radius + Fixed.FromInt(56), // height + 400, // mass + 0, // damage + Sfx.DMACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.Shadow | MobjFlags.CountKill, // flags + MobjState.SargRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Head + 3005, // doomEdNum + MobjState.HeadStnd, // spawnState + 400, // spawnHealth + MobjState.HeadRun1, // seeState + Sfx.CACSIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.HeadPain, // painState + 128, // painChance + Sfx.DMPAIN, // painSound + MobjState.Null, // meleeState + MobjState.HeadAtk1, // missileState + MobjState.HeadDie1, // deathState + MobjState.Null, // xdeathState + Sfx.CACDTH, // deathSound + 8, // speed + Fixed.FromInt(31), // radius + Fixed.FromInt(56), // height + 400, // mass + 0, // damage + Sfx.DMACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.Float | MobjFlags.NoGravity | MobjFlags.CountKill, // flags + MobjState.HeadRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Bruiser + 3003, // doomEdNum + MobjState.BossStnd, // spawnState + 1000, // spawnHealth + MobjState.BossRun1, // seeState + Sfx.BRSSIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.BossPain, // painState + 50, // painChance + Sfx.DMPAIN, // painSound + MobjState.BossAtk1, // meleeState + MobjState.BossAtk1, // missileState + MobjState.BossDie1, // deathState + MobjState.Null, // xdeathState + Sfx.BRSDTH, // deathSound + 8, // speed + Fixed.FromInt(24), // radius + Fixed.FromInt(64), // height + 1000, // mass + 0, // damage + Sfx.DMACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.BossRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Bruisershot + -1, // doomEdNum + MobjState.Brball1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.FIRSHT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Brballx1, // deathState + MobjState.Null, // xdeathState + Sfx.FIRXPL, // deathSound + 15 * Fixed.FracUnit, // speed + Fixed.FromInt(6), // radius + Fixed.FromInt(8), // height + 100, // mass + 8, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.Missile | MobjFlags.DropOff | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Knight + 69, // doomEdNum + MobjState.Bos2Stnd, // spawnState + 500, // spawnHealth + MobjState.Bos2Run1, // seeState + Sfx.KNTSIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Bos2Pain, // painState + 50, // painChance + Sfx.DMPAIN, // painSound + MobjState.Bos2Atk1, // meleeState + MobjState.Bos2Atk1, // missileState + MobjState.Bos2Die1, // deathState + MobjState.Null, // xdeathState + Sfx.KNTDTH, // deathSound + 8, // speed + Fixed.FromInt(24), // radius + Fixed.FromInt(64), // height + 1000, // mass + 0, // damage + Sfx.DMACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.Bos2Raise1 // raiseState + ), + + new MobjInfo( // MobjType.Skull + 3006, // doomEdNum + MobjState.SkullStnd, // spawnState + 100, // spawnHealth + MobjState.SkullRun1, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.SKLATK, // attackSound + MobjState.SkullPain, // painState + 256, // painChance + Sfx.DMPAIN, // painSound + MobjState.Null, // meleeState + MobjState.SkullAtk1, // missileState + MobjState.SkullDie1, // deathState + MobjState.Null, // xdeathState + Sfx.FIRXPL, // deathSound + 8, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(56), // height + 50, // mass + 3, // damage + Sfx.DMACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.Float | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Spider + 7, // doomEdNum + MobjState.SpidStnd, // spawnState + 3000, // spawnHealth + MobjState.SpidRun1, // seeState + Sfx.SPISIT, // seeSound + 8, // reactionTime + Sfx.SHOTGN, // attackSound + MobjState.SpidPain, // painState + 40, // painChance + Sfx.DMPAIN, // painSound + MobjState.Null, // meleeState + MobjState.SpidAtk1, // missileState + MobjState.SpidDie1, // deathState + MobjState.Null, // xdeathState + Sfx.SPIDTH, // deathSound + 12, // speed + Fixed.FromInt(128), // radius + Fixed.FromInt(100), // height + 1000, // mass + 0, // damage + Sfx.DMACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Baby + 68, // doomEdNum + MobjState.BspiStnd, // spawnState + 500, // spawnHealth + MobjState.BspiSight, // seeState + Sfx.BSPSIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.BspiPain, // painState + 128, // painChance + Sfx.DMPAIN, // painSound + MobjState.Null, // meleeState + MobjState.BspiAtk1, // missileState + MobjState.BspiDie1, // deathState + MobjState.Null, // xdeathState + Sfx.BSPDTH, // deathSound + 12, // speed + Fixed.FromInt(64), // radius + Fixed.FromInt(64), // height + 600, // mass + 0, // damage + Sfx.BSPACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.BspiRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Cyborg + 16, // doomEdNum + MobjState.CyberStnd, // spawnState + 4000, // spawnHealth + MobjState.CyberRun1, // seeState + Sfx.CYBSIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.CyberPain, // painState + 20, // painChance + Sfx.DMPAIN, // painSound + MobjState.Null, // meleeState + MobjState.CyberAtk1, // missileState + MobjState.CyberDie1, // deathState + MobjState.Null, // xdeathState + Sfx.CYBDTH, // deathSound + 16, // speed + Fixed.FromInt(40), // radius + Fixed.FromInt(110), // height + 1000, // mass + 0, // damage + Sfx.DMACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Pain + 71, // doomEdNum + MobjState.PainStnd, // spawnState + 400, // spawnHealth + MobjState.PainRun1, // seeState + Sfx.PESIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.PainPain, // painState + 128, // painChance + Sfx.PEPAIN, // painSound + MobjState.Null, // meleeState + MobjState.PainAtk1, // missileState + MobjState.PainDie1, // deathState + MobjState.Null, // xdeathState + Sfx.PEDTH, // deathSound + 8, // speed + Fixed.FromInt(31), // radius + Fixed.FromInt(56), // height + 400, // mass + 0, // damage + Sfx.DMACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.Float | MobjFlags.NoGravity | MobjFlags.CountKill, // flags + MobjState.PainRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Wolfss + 84, // doomEdNum + MobjState.SswvStnd, // spawnState + 50, // spawnHealth + MobjState.SswvRun1, // seeState + Sfx.SSSIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.SswvPain, // painState + 170, // painChance + Sfx.POPAIN, // painSound + MobjState.Null, // meleeState + MobjState.SswvAtk1, // missileState + MobjState.SswvDie1, // deathState + MobjState.SswvXdie1, // xdeathState + Sfx.SSDTH, // deathSound + 8, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(56), // height + 100, // mass + 0, // damage + Sfx.POSACT, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.SswvRaise1 // raiseState + ), + + new MobjInfo( // MobjType.Keen + 72, // doomEdNum + MobjState.Keenstnd, // spawnState + 100, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Keenpain, // painState + 256, // painChance + Sfx.KEENPN, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Commkeen, // deathState + MobjState.Null, // xdeathState + Sfx.KEENDT, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(72), // height + 10000000, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity | MobjFlags.Shootable | MobjFlags.CountKill, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Bossbrain + 88, // doomEdNum + MobjState.Brain, // spawnState + 250, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.BrainPain, // painState + 255, // painChance + Sfx.BOSPN, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.BrainDie1, // deathState + MobjState.Null, // xdeathState + Sfx.BOSDTH, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 10000000, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.Shootable, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Bossspit + 89, // doomEdNum + MobjState.Braineye, // spawnState + 1000, // spawnHealth + MobjState.Braineyesee, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(32), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.NoSector, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Bosstarget + 87, // doomEdNum + MobjState.Null, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(32), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.NoSector, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Spawnshot + -1, // doomEdNum + MobjState.Spawn1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.BOSPIT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.FIRXPL, // deathSound + 10 * Fixed.FracUnit, // speed + Fixed.FromInt(6), // radius + Fixed.FromInt(32), // height + 100, // mass + 3, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.Missile | MobjFlags.DropOff | MobjFlags.NoGravity | MobjFlags.NoClip, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Spawnfire + -1, // doomEdNum + MobjState.Spawnfire1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Barrel + 2035, // doomEdNum + MobjState.Bar1, // spawnState + 20, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Bexp, // deathState + MobjState.Null, // xdeathState + Sfx.BAREXP, // deathSound + 0, // speed + Fixed.FromInt(10), // radius + Fixed.FromInt(42), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.Shootable | MobjFlags.NoBlood, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Troopshot + -1, // doomEdNum + MobjState.Tball1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.FIRSHT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Tballx1, // deathState + MobjState.Null, // xdeathState + Sfx.FIRXPL, // deathSound + 10 * Fixed.FracUnit, // speed + Fixed.FromInt(6), // radius + Fixed.FromInt(8), // height + 100, // mass + 3, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.Missile | MobjFlags.DropOff | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Headshot + -1, // doomEdNum + MobjState.Rball1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.FIRSHT, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Rballx1, // deathState + MobjState.Null, // xdeathState + Sfx.FIRXPL, // deathSound + 10 * Fixed.FracUnit, // speed + Fixed.FromInt(6), // radius + Fixed.FromInt(8), // height + 100, // mass + 5, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.Missile | MobjFlags.DropOff | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Rocket + -1, // doomEdNum + MobjState.Rocket, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.RLAUNC, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Explode1, // deathState + MobjState.Null, // xdeathState + Sfx.BAREXP, // deathSound + 20 * Fixed.FracUnit, // speed + Fixed.FromInt(11), // radius + Fixed.FromInt(8), // height + 100, // mass + 20, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.Missile | MobjFlags.DropOff | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Plasma + -1, // doomEdNum + MobjState.Plasball, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.PLASMA, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Plasexp, // deathState + MobjState.Null, // xdeathState + Sfx.FIRXPL, // deathSound + 25 * Fixed.FracUnit, // speed + Fixed.FromInt(13), // radius + Fixed.FromInt(8), // height + 100, // mass + 5, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.Missile | MobjFlags.DropOff | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Bfg + -1, // doomEdNum + MobjState.Bfgshot, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Bfgland, // deathState + MobjState.Null, // xdeathState + Sfx.RXPLOD, // deathSound + 25 * Fixed.FracUnit, // speed + Fixed.FromInt(13), // radius + Fixed.FromInt(8), // height + 100, // mass + 100, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.Missile | MobjFlags.DropOff | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Arachplaz + -1, // doomEdNum + MobjState.ArachPlaz, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.PLASMA, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.ArachPlex, // deathState + MobjState.Null, // xdeathState + Sfx.FIRXPL, // deathSound + 25 * Fixed.FracUnit, // speed + Fixed.FromInt(13), // radius + Fixed.FromInt(8), // height + 100, // mass + 5, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.Missile | MobjFlags.DropOff | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Puff + -1, // doomEdNum + MobjState.Puff1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Blood + -1, // doomEdNum + MobjState.Blood1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Tfog + -1, // doomEdNum + MobjState.Tfog, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Ifog + -1, // doomEdNum + MobjState.Ifog, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Teleportman + 14, // doomEdNum + MobjState.Null, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.NoSector, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Extrabfg + -1, // doomEdNum + MobjState.Bfgexp, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc0 + 2018, // doomEdNum + MobjState.Arm1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc1 + 2019, // doomEdNum + MobjState.Arm2, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc2 + 2014, // doomEdNum + MobjState.Bon1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.CountItem, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc3 + 2015, // doomEdNum + MobjState.Bon2, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.CountItem, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc4 + 5, // doomEdNum + MobjState.Bkey, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.NotDeathmatch, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc5 + 13, // doomEdNum + MobjState.Rkey, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.NotDeathmatch, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc6 + 6, // doomEdNum + MobjState.Ykey, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.NotDeathmatch, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc7 + 39, // doomEdNum + MobjState.Yskull, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.NotDeathmatch, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc8 + 38, // doomEdNum + MobjState.Rskull, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.NotDeathmatch, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc9 + 40, // doomEdNum + MobjState.Bskull, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.NotDeathmatch, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc10 + 2011, // doomEdNum + MobjState.Stim, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc11 + 2012, // doomEdNum + MobjState.Medi, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc12 + 2013, // doomEdNum + MobjState.Soul, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.CountItem, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Inv + 2022, // doomEdNum + MobjState.Pinv, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.CountItem, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc13 + 2023, // doomEdNum + MobjState.Pstr, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.CountItem, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Ins + 2024, // doomEdNum + MobjState.Pins, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.CountItem, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc14 + 2025, // doomEdNum + MobjState.Suit, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc15 + 2026, // doomEdNum + MobjState.Pmap, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.CountItem, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc16 + 2045, // doomEdNum + MobjState.Pvis, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.CountItem, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Mega + 83, // doomEdNum + MobjState.Mega, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special | MobjFlags.CountItem, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Clip + 2007, // doomEdNum + MobjState.Clip, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc17 + 2048, // doomEdNum + MobjState.Ammo, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc18 + 2010, // doomEdNum + MobjState.Rock, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc19 + 2046, // doomEdNum + MobjState.Brok, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc20 + 2047, // doomEdNum + MobjState.Cell, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc21 + 17, // doomEdNum + MobjState.Celp, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc22 + 2008, // doomEdNum + MobjState.Shel, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc23 + 2049, // doomEdNum + MobjState.Sbox, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc24 + 8, // doomEdNum + MobjState.Bpak, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc25 + 2006, // doomEdNum + MobjState.Bfug, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Chaingun + 2002, // doomEdNum + MobjState.Mgun, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc26 + 2005, // doomEdNum + MobjState.Csaw, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc27 + 2003, // doomEdNum + MobjState.Laun, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc28 + 2004, // doomEdNum + MobjState.Plas, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Shotgun + 2001, // doomEdNum + MobjState.Shot, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Supershotgun + 82, // doomEdNum + MobjState.Shot2, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Special, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc29 + 85, // doomEdNum + MobjState.Techlamp, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc30 + 86, // doomEdNum + MobjState.Tech2Lamp, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc31 + 2028, // doomEdNum + MobjState.Colu, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc32 + 30, // doomEdNum + MobjState.Tallgrncol, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc33 + 31, // doomEdNum + MobjState.Shrtgrncol, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc34 + 32, // doomEdNum + MobjState.Tallredcol, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc35 + 33, // doomEdNum + MobjState.Shrtredcol, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc36 + 37, // doomEdNum + MobjState.Skullcol, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc37 + 36, // doomEdNum + MobjState.Heartcol, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc38 + 41, // doomEdNum + MobjState.Evileye, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc39 + 42, // doomEdNum + MobjState.Floatskull, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc40 + 43, // doomEdNum + MobjState.Torchtree, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc41 + 44, // doomEdNum + MobjState.Bluetorch, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc42 + 45, // doomEdNum + MobjState.Greentorch, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc43 + 46, // doomEdNum + MobjState.Redtorch, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc44 + 55, // doomEdNum + MobjState.Btorchshrt, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc45 + 56, // doomEdNum + MobjState.Gtorchshrt, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc46 + 57, // doomEdNum + MobjState.Rtorchshrt, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc47 + 47, // doomEdNum + MobjState.Stalagtite, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc48 + 48, // doomEdNum + MobjState.Techpillar, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc49 + 34, // doomEdNum + MobjState.Candlestik, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc50 + 35, // doomEdNum + MobjState.Candelabra, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc51 + 49, // doomEdNum + MobjState.Bloodytwitch, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(68), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc52 + 50, // doomEdNum + MobjState.Meat2, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(84), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc53 + 51, // doomEdNum + MobjState.Meat3, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(84), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc54 + 52, // doomEdNum + MobjState.Meat4, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(68), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc55 + 53, // doomEdNum + MobjState.Meat5, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(52), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc56 + 59, // doomEdNum + MobjState.Meat2, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(84), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc57 + 60, // doomEdNum + MobjState.Meat4, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(68), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc58 + 61, // doomEdNum + MobjState.Meat3, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(52), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc59 + 62, // doomEdNum + MobjState.Meat5, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(52), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc60 + 63, // doomEdNum + MobjState.Bloodytwitch, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(68), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc61 + 22, // doomEdNum + MobjState.HeadDie6, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc62 + 15, // doomEdNum + MobjState.PlayDie7, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc63 + 18, // doomEdNum + MobjState.PossDie5, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc64 + 21, // doomEdNum + MobjState.SargDie6, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc65 + 23, // doomEdNum + MobjState.SkullDie6, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc66 + 20, // doomEdNum + MobjState.TrooDie5, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc67 + 19, // doomEdNum + MobjState.SposDie5, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc68 + 10, // doomEdNum + MobjState.PlayXdie9, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc69 + 12, // doomEdNum + MobjState.PlayXdie9, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc70 + 28, // doomEdNum + MobjState.Headsonstick, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc71 + 24, // doomEdNum + MobjState.Gibs, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + 0, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc72 + 27, // doomEdNum + MobjState.Headonastick, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc73 + 29, // doomEdNum + MobjState.Headcandles, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc74 + 25, // doomEdNum + MobjState.Deadstick, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc75 + 26, // doomEdNum + MobjState.Livestick, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc76 + 54, // doomEdNum + MobjState.Bigtree, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(32), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc77 + 70, // doomEdNum + MobjState.Bbar1, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc78 + 73, // doomEdNum + MobjState.Hangnoguts, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(88), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc79 + 74, // doomEdNum + MobjState.Hangbnobrain, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(88), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc80 + 75, // doomEdNum + MobjState.Hangtlookdn, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(64), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc81 + 76, // doomEdNum + MobjState.Hangtskull, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(64), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc82 + 77, // doomEdNum + MobjState.Hangtlookup, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(64), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc83 + 78, // doomEdNum + MobjState.Hangtnobrain, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(16), // radius + Fixed.FromInt(64), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.Solid | MobjFlags.SpawnCeiling | MobjFlags.NoGravity, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc84 + 79, // doomEdNum + MobjState.Colongibs, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc85 + 80, // doomEdNum + MobjState.Smallpool, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap, // flags + MobjState.Null // raiseState + ), + + new MobjInfo( // MobjType.Misc86 + 81, // doomEdNum + MobjState.Brainstem, // spawnState + 1000, // spawnHealth + MobjState.Null, // seeState + Sfx.NONE, // seeSound + 8, // reactionTime + Sfx.NONE, // attackSound + MobjState.Null, // painState + 0, // painChance + Sfx.NONE, // painSound + MobjState.Null, // meleeState + MobjState.Null, // missileState + MobjState.Null, // deathState + MobjState.Null, // xdeathState + Sfx.NONE, // deathSound + 0, // speed + Fixed.FromInt(20), // radius + Fixed.FromInt(16), // height + 100, // mass + 0, // damage + Sfx.NONE, // activeSound + MobjFlags.NoBlockMap, // flags + MobjState.Null // raiseState + ) + + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.ParTimes.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.ParTimes.cs new file mode 100644 index 00000000..65143f14 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.ParTimes.cs @@ -0,0 +1,44 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static class ParTimes + { + public static readonly IReadOnlyList> Doom1 = new int[][] + { + new int[] { 30, 75, 120, 90, 165, 180, 180, 30, 165 }, + new int[] { 90, 90, 90, 120, 90, 360, 240, 30, 170 }, + new int[] { 90, 45, 90, 150, 90, 90, 165, 30, 135 }, + new int[] { 165, 255, 135, 150, 180, 390, 135, 360, 180 } + }; + + public static readonly IReadOnlyList Doom2 = new int[] + { + 30, 90, 120, 120, 90, 150, 120, 120, 270, 90, + 210, 150, 150, 150, 210, 150, 420, 150, 210, 150, + 240, 150, 180, 150, 150, 300, 330, 420, 300, 180, + 120, 30 + }; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.PlayerActions.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.PlayerActions.cs new file mode 100644 index 00000000..3d525ea5 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.PlayerActions.cs @@ -0,0 +1,137 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + private class PlayerActions + { + public void Light0(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.Light0(player); + } + + public void WeaponReady(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.WeaponReady(player, psp); + } + + public void Lower(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.Lower(player, psp); + } + + public void Raise(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.Raise(player, psp); + } + + public void Punch(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.Punch(player); + } + + public void ReFire(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.ReFire(player); + } + + public void FirePistol(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.FirePistol(player); + } + + public void Light1(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.Light1(player); + } + + public void FireShotgun(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.FireShotgun(player); + } + + public void Light2(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.Light2(player); + } + + public void FireShotgun2(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.FireShotgun2(player); + } + + public void CheckReload(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.CheckReload(player); + } + + public void OpenShotgun2(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.OpenShotgun2(player); + } + + public void LoadShotgun2(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.LoadShotgun2(player); + } + + public void CloseShotgun2(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.CloseShotgun2(player); + } + + public void FireCGun(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.FireCGun(player, psp); + } + + public void GunFlash(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.GunFlash(player); + } + + public void FireMissile(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.FireMissile(player); + } + + public void Saw(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.Saw(player); + } + + public void FirePlasma(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.FirePlasma(player); + } + + public void BFGsound(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.A_BFGsound(player); + } + + public void FireBFG(World world, Player player, PlayerSpriteDef psp) + { + world.WeaponBehavior.FireBFG(player); + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.PowerDuration.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.PowerDuration.cs new file mode 100644 index 00000000..110de7e9 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.PowerDuration.cs @@ -0,0 +1,32 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static class PowerDuration + { + public static readonly int Invulnerability = 30 * GameConst.TicRate; + public static readonly int Invisibility = 60 * GameConst.TicRate; + public static readonly int Infrared = 120 * GameConst.TicRate; + public static readonly int IronFeet = 60 * GameConst.TicRate; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.QuitMessages.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.QuitMessages.cs new file mode 100644 index 00000000..805273ad --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.QuitMessages.cs @@ -0,0 +1,62 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static class QuitMessages + { + public static readonly IReadOnlyList Doom = new DoomString[] + { + Strings.QUITMSG, + new DoomString("please don't leave, there's more\ndemons to toast!"), + new DoomString("let's beat it -- this is turning\ninto a bloodbath!"), + new DoomString("i wouldn't leave if i were you.\ndos is much worse."), + new DoomString("you're trying to say you like dos\nbetter than me, right?"), + new DoomString("don't leave yet -- there's a\ndemon around that corner!"), + new DoomString("ya know, next time you come in here\ni'm gonna toast ya."), + new DoomString("go ahead and leave. see if i care.") + }; + + public static readonly IReadOnlyList Doom2 = new DoomString[] + { + new DoomString("you want to quit?\nthen, thou hast lost an eighth!"), + new DoomString("don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!"), + new DoomString("get outta here and go back\nto your boring programs."), + new DoomString("if i were your boss, i'd \n deathmatch ya in a minute!"), + new DoomString("look, bud. you leave now\nand you forfeit your body count!"), + new DoomString("just leave. when you come\nback, i'll be waiting with a bat."), + new DoomString("you're lucky i don't smack\nyou for thinking about leaving.") + }; + + public static readonly IReadOnlyList FinalDoom = new DoomString[] + { + new DoomString("fuck you, pussy!\nget the fuck out!"), + new DoomString("you quit and i'll jizz\nin your cystholes!"), + new DoomString("if you leave, i'll make\nthe lord drink my jizz."), + new DoomString("hey, ron! can we say\n'fuck' in the game?"), + new DoomString("i'd leave: this is just\nmore monsters and levels.\nwhat a load."), + new DoomString("suck it down, asshole!\nyou're a fucking wimp!"), + new DoomString("don't quit now! we're \nstill spending your money!") + }; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.SfxNames.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.SfxNames.cs new file mode 100644 index 00000000..c1e285bf --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.SfxNames.cs @@ -0,0 +1,137 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static readonly DoomString[] SfxNames = new DoomString[] + { + new DoomString("NONE"), + new DoomString("PISTOL"), + new DoomString("SHOTGN"), + new DoomString("SGCOCK"), + new DoomString("DSHTGN"), + new DoomString("DBOPN"), + new DoomString("DBCLS"), + new DoomString("DBLOAD"), + new DoomString("PLASMA"), + new DoomString("BFG"), + new DoomString("SAWUP"), + new DoomString("SAWIDL"), + new DoomString("SAWFUL"), + new DoomString("SAWHIT"), + new DoomString("RLAUNC"), + new DoomString("RXPLOD"), + new DoomString("FIRSHT"), + new DoomString("FIRXPL"), + new DoomString("PSTART"), + new DoomString("PSTOP"), + new DoomString("DOROPN"), + new DoomString("DORCLS"), + new DoomString("STNMOV"), + new DoomString("SWTCHN"), + new DoomString("SWTCHX"), + new DoomString("PLPAIN"), + new DoomString("DMPAIN"), + new DoomString("POPAIN"), + new DoomString("VIPAIN"), + new DoomString("MNPAIN"), + new DoomString("PEPAIN"), + new DoomString("SLOP"), + new DoomString("ITEMUP"), + new DoomString("WPNUP"), + new DoomString("OOF"), + new DoomString("TELEPT"), + new DoomString("POSIT1"), + new DoomString("POSIT2"), + new DoomString("POSIT3"), + new DoomString("BGSIT1"), + new DoomString("BGSIT2"), + new DoomString("SGTSIT"), + new DoomString("CACSIT"), + new DoomString("BRSSIT"), + new DoomString("CYBSIT"), + new DoomString("SPISIT"), + new DoomString("BSPSIT"), + new DoomString("KNTSIT"), + new DoomString("VILSIT"), + new DoomString("MANSIT"), + new DoomString("PESIT"), + new DoomString("SKLATK"), + new DoomString("SGTATK"), + new DoomString("SKEPCH"), + new DoomString("VILATK"), + new DoomString("CLAW"), + new DoomString("SKESWG"), + new DoomString("PLDETH"), + new DoomString("PDIEHI"), + new DoomString("PODTH1"), + new DoomString("PODTH2"), + new DoomString("PODTH3"), + new DoomString("BGDTH1"), + new DoomString("BGDTH2"), + new DoomString("SGTDTH"), + new DoomString("CACDTH"), + new DoomString("SKLDTH"), + new DoomString("BRSDTH"), + new DoomString("CYBDTH"), + new DoomString("SPIDTH"), + new DoomString("BSPDTH"), + new DoomString("VILDTH"), + new DoomString("KNTDTH"), + new DoomString("PEDTH"), + new DoomString("SKEDTH"), + new DoomString("POSACT"), + new DoomString("BGACT"), + new DoomString("DMACT"), + new DoomString("BSPACT"), + new DoomString("BSPWLK"), + new DoomString("VILACT"), + new DoomString("NOWAY"), + new DoomString("BAREXP"), + new DoomString("PUNCH"), + new DoomString("HOOF"), + new DoomString("METAL"), + new DoomString("CHGUN"), + new DoomString("TINK"), + new DoomString("BDOPN"), + new DoomString("BDCLS"), + new DoomString("ITMBK"), + new DoomString("FLAME"), + new DoomString("FLAMST"), + new DoomString("GETPOW"), + new DoomString("BOSPIT"), + new DoomString("BOSCUB"), + new DoomString("BOSSIT"), + new DoomString("BOSPN"), + new DoomString("BOSDTH"), + new DoomString("MANATK"), + new DoomString("MANDTH"), + new DoomString("SSSIT"), + new DoomString("SSDTH"), + new DoomString("KEENPN"), + new DoomString("KEENDT"), + new DoomString("SKEACT"), + new DoomString("SKESIT"), + new DoomString("SKEATK"), + new DoomString("RADIO") + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.SpriteNames.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.SpriteNames.cs new file mode 100644 index 00000000..856be0f6 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.SpriteNames.cs @@ -0,0 +1,166 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static readonly DoomString[] SpriteNames = new DoomString[] + { + new DoomString("TROO"), + new DoomString("SHTG"), + new DoomString("PUNG"), + new DoomString("PISG"), + new DoomString("PISF"), + new DoomString("SHTF"), + new DoomString("SHT2"), + new DoomString("CHGG"), + new DoomString("CHGF"), + new DoomString("MISG"), + new DoomString("MISF"), + new DoomString("SAWG"), + new DoomString("PLSG"), + new DoomString("PLSF"), + new DoomString("BFGG"), + new DoomString("BFGF"), + new DoomString("BLUD"), + new DoomString("PUFF"), + new DoomString("BAL1"), + new DoomString("BAL2"), + new DoomString("PLSS"), + new DoomString("PLSE"), + new DoomString("MISL"), + new DoomString("BFS1"), + new DoomString("BFE1"), + new DoomString("BFE2"), + new DoomString("TFOG"), + new DoomString("IFOG"), + new DoomString("PLAY"), + new DoomString("POSS"), + new DoomString("SPOS"), + new DoomString("VILE"), + new DoomString("FIRE"), + new DoomString("FATB"), + new DoomString("FBXP"), + new DoomString("SKEL"), + new DoomString("MANF"), + new DoomString("FATT"), + new DoomString("CPOS"), + new DoomString("SARG"), + new DoomString("HEAD"), + new DoomString("BAL7"), + new DoomString("BOSS"), + new DoomString("BOS2"), + new DoomString("SKUL"), + new DoomString("SPID"), + new DoomString("BSPI"), + new DoomString("APLS"), + new DoomString("APBX"), + new DoomString("CYBR"), + new DoomString("PAIN"), + new DoomString("SSWV"), + new DoomString("KEEN"), + new DoomString("BBRN"), + new DoomString("BOSF"), + new DoomString("ARM1"), + new DoomString("ARM2"), + new DoomString("BAR1"), + new DoomString("BEXP"), + new DoomString("FCAN"), + new DoomString("BON1"), + new DoomString("BON2"), + new DoomString("BKEY"), + new DoomString("RKEY"), + new DoomString("YKEY"), + new DoomString("BSKU"), + new DoomString("RSKU"), + new DoomString("YSKU"), + new DoomString("STIM"), + new DoomString("MEDI"), + new DoomString("SOUL"), + new DoomString("PINV"), + new DoomString("PSTR"), + new DoomString("PINS"), + new DoomString("MEGA"), + new DoomString("SUIT"), + new DoomString("PMAP"), + new DoomString("PVIS"), + new DoomString("CLIP"), + new DoomString("AMMO"), + new DoomString("ROCK"), + new DoomString("BROK"), + new DoomString("CELL"), + new DoomString("CELP"), + new DoomString("SHEL"), + new DoomString("SBOX"), + new DoomString("BPAK"), + new DoomString("BFUG"), + new DoomString("MGUN"), + new DoomString("CSAW"), + new DoomString("LAUN"), + new DoomString("PLAS"), + new DoomString("SHOT"), + new DoomString("SGN2"), + new DoomString("COLU"), + new DoomString("SMT2"), + new DoomString("GOR1"), + new DoomString("POL2"), + new DoomString("POL5"), + new DoomString("POL4"), + new DoomString("POL3"), + new DoomString("POL1"), + new DoomString("POL6"), + new DoomString("GOR2"), + new DoomString("GOR3"), + new DoomString("GOR4"), + new DoomString("GOR5"), + new DoomString("SMIT"), + new DoomString("COL1"), + new DoomString("COL2"), + new DoomString("COL3"), + new DoomString("COL4"), + new DoomString("CAND"), + new DoomString("CBRA"), + new DoomString("COL6"), + new DoomString("TRE1"), + new DoomString("TRE2"), + new DoomString("ELEC"), + new DoomString("CEYE"), + new DoomString("FSKU"), + new DoomString("COL5"), + new DoomString("TBLU"), + new DoomString("TGRN"), + new DoomString("TRED"), + new DoomString("SMBT"), + new DoomString("SMGT"), + new DoomString("SMRT"), + new DoomString("HDB1"), + new DoomString("HDB2"), + new DoomString("HDB3"), + new DoomString("HDB4"), + new DoomString("HDB5"), + new DoomString("HDB6"), + new DoomString("POB1"), + new DoomString("POB2"), + new DoomString("BRS1"), + new DoomString("TLMP"), + new DoomString("TLP2") + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.States.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.States.cs new file mode 100644 index 00000000..328f993d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.States.cs @@ -0,0 +1,998 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + private static PlayerActions pa = new PlayerActions(); + private static MobjActions ma = new MobjActions(); + + public static readonly MobjStateDef[] States = new MobjStateDef[] + { + new MobjStateDef(0, Sprite.TROO, 0, -1, null, null, MobjState.Null, 0, 0), // State.Null + new MobjStateDef(1, Sprite.SHTG, 4, 0, pa.Light0, null, MobjState.Null, 0, 0), // State.Lightdone + new MobjStateDef(2, Sprite.PUNG, 0, 1, pa.WeaponReady, null, MobjState.Punch, 0, 0), // State.Punch + new MobjStateDef(3, Sprite.PUNG, 0, 1, pa.Lower, null, MobjState.Punchdown, 0, 0), // State.Punchdown + new MobjStateDef(4, Sprite.PUNG, 0, 1, pa.Raise, null, MobjState.Punchup, 0, 0), // State.Punchup + new MobjStateDef(5, Sprite.PUNG, 1, 4, null, null, MobjState.Punch2, 0, 0), // State.Punch1 + new MobjStateDef(6, Sprite.PUNG, 2, 4, pa.Punch, null, MobjState.Punch3, 0, 0), // State.Punch2 + new MobjStateDef(7, Sprite.PUNG, 3, 5, null, null, MobjState.Punch4, 0, 0), // State.Punch3 + new MobjStateDef(8, Sprite.PUNG, 2, 4, null, null, MobjState.Punch5, 0, 0), // State.Punch4 + new MobjStateDef(9, Sprite.PUNG, 1, 5, pa.ReFire, null, MobjState.Punch, 0, 0), // State.Punch5 + new MobjStateDef(10, Sprite.PISG, 0, 1, pa.WeaponReady, null, MobjState.Pistol, 0, 0), // State.Pistol + new MobjStateDef(11, Sprite.PISG, 0, 1, pa.Lower, null, MobjState.Pistoldown, 0, 0), // State.Pistoldown + new MobjStateDef(12, Sprite.PISG, 0, 1, pa.Raise, null, MobjState.Pistolup, 0, 0), // State.Pistolup + new MobjStateDef(13, Sprite.PISG, 0, 4, null, null, MobjState.Pistol2, 0, 0), // State.Pistol1 + new MobjStateDef(14, Sprite.PISG, 1, 6, pa.FirePistol, null, MobjState.Pistol3, 0, 0), // State.Pistol2 + new MobjStateDef(15, Sprite.PISG, 2, 4, null, null, MobjState.Pistol4, 0, 0), // State.Pistol3 + new MobjStateDef(16, Sprite.PISG, 1, 5, pa.ReFire, null, MobjState.Pistol, 0, 0), // State.Pistol4 + new MobjStateDef(17, Sprite.PISF, 32768, 7, pa.Light1, null, MobjState.Lightdone, 0, 0), // State.Pistolflash + new MobjStateDef(18, Sprite.SHTG, 0, 1, pa.WeaponReady, null, MobjState.Sgun, 0, 0), // State.Sgun + new MobjStateDef(19, Sprite.SHTG, 0, 1, pa.Lower, null, MobjState.Sgundown, 0, 0), // State.Sgundown + new MobjStateDef(20, Sprite.SHTG, 0, 1, pa.Raise, null, MobjState.Sgunup, 0, 0), // State.Sgunup + new MobjStateDef(21, Sprite.SHTG, 0, 3, null, null, MobjState.Sgun2, 0, 0), // State.Sgun1 + new MobjStateDef(22, Sprite.SHTG, 0, 7, pa.FireShotgun, null, MobjState.Sgun3, 0, 0), // State.Sgun2 + new MobjStateDef(23, Sprite.SHTG, 1, 5, null, null, MobjState.Sgun4, 0, 0), // State.Sgun3 + new MobjStateDef(24, Sprite.SHTG, 2, 5, null, null, MobjState.Sgun5, 0, 0), // State.Sgun4 + new MobjStateDef(25, Sprite.SHTG, 3, 4, null, null, MobjState.Sgun6, 0, 0), // State.Sgun5 + new MobjStateDef(26, Sprite.SHTG, 2, 5, null, null, MobjState.Sgun7, 0, 0), // State.Sgun6 + new MobjStateDef(27, Sprite.SHTG, 1, 5, null, null, MobjState.Sgun8, 0, 0), // State.Sgun7 + new MobjStateDef(28, Sprite.SHTG, 0, 3, null, null, MobjState.Sgun9, 0, 0), // State.Sgun8 + new MobjStateDef(29, Sprite.SHTG, 0, 7, pa.ReFire, null, MobjState.Sgun, 0, 0), // State.Sgun9 + new MobjStateDef(30, Sprite.SHTF, 32768, 4, pa.Light1, null, MobjState.Sgunflash2, 0, 0), // State.Sgunflash1 + new MobjStateDef(31, Sprite.SHTF, 32769, 3, pa.Light2, null, MobjState.Lightdone, 0, 0), // State.Sgunflash2 + new MobjStateDef(32, Sprite.SHT2, 0, 1, pa.WeaponReady, null, MobjState.Dsgun, 0, 0), // State.Dsgun + new MobjStateDef(33, Sprite.SHT2, 0, 1, pa.Lower, null, MobjState.Dsgundown, 0, 0), // State.Dsgundown + new MobjStateDef(34, Sprite.SHT2, 0, 1, pa.Raise, null, MobjState.Dsgunup, 0, 0), // State.Dsgunup + new MobjStateDef(35, Sprite.SHT2, 0, 3, null, null, MobjState.Dsgun2, 0, 0), // State.Dsgun1 + new MobjStateDef(36, Sprite.SHT2, 0, 7, pa.FireShotgun2, null, MobjState.Dsgun3, 0, 0), // State.Dsgun2 + new MobjStateDef(37, Sprite.SHT2, 1, 7, null, null, MobjState.Dsgun4, 0, 0), // State.Dsgun3 + new MobjStateDef(38, Sprite.SHT2, 2, 7, pa.CheckReload, null, MobjState.Dsgun5, 0, 0), // State.Dsgun4 + new MobjStateDef(39, Sprite.SHT2, 3, 7, pa.OpenShotgun2, null, MobjState.Dsgun6, 0, 0), // State.Dsgun5 + new MobjStateDef(40, Sprite.SHT2, 4, 7, null, null, MobjState.Dsgun7, 0, 0), // State.Dsgun6 + new MobjStateDef(41, Sprite.SHT2, 5, 7, pa.LoadShotgun2, null, MobjState.Dsgun8, 0, 0), // State.Dsgun7 + new MobjStateDef(42, Sprite.SHT2, 6, 6, null, null, MobjState.Dsgun9, 0, 0), // State.Dsgun8 + new MobjStateDef(43, Sprite.SHT2, 7, 6, pa.CloseShotgun2, null, MobjState.Dsgun10, 0, 0), // State.Dsgun9 + new MobjStateDef(44, Sprite.SHT2, 0, 5, pa.ReFire, null, MobjState.Dsgun, 0, 0), // State.Dsgun10 + new MobjStateDef(45, Sprite.SHT2, 1, 7, null, null, MobjState.Dsnr2, 0, 0), // State.Dsnr1 + new MobjStateDef(46, Sprite.SHT2, 0, 3, null, null, MobjState.Dsgundown, 0, 0), // State.Dsnr2 + new MobjStateDef(47, Sprite.SHT2, 32776, 5, pa.Light1, null, MobjState.Dsgunflash2, 0, 0), // State.Dsgunflash1 + new MobjStateDef(48, Sprite.SHT2, 32777, 4, pa.Light2, null, MobjState.Lightdone, 0, 0), // State.Dsgunflash2 + new MobjStateDef(49, Sprite.CHGG, 0, 1, pa.WeaponReady, null, MobjState.Chain, 0, 0), // State.Chain + new MobjStateDef(50, Sprite.CHGG, 0, 1, pa.Lower, null, MobjState.Chaindown, 0, 0), // State.Chaindown + new MobjStateDef(51, Sprite.CHGG, 0, 1, pa.Raise, null, MobjState.Chainup, 0, 0), // State.Chainup + new MobjStateDef(52, Sprite.CHGG, 0, 4, pa.FireCGun, null, MobjState.Chain2, 0, 0), // State.Chain1 + new MobjStateDef(53, Sprite.CHGG, 1, 4, pa.FireCGun, null, MobjState.Chain3, 0, 0), // State.Chain2 + new MobjStateDef(54, Sprite.CHGG, 1, 0, pa.ReFire, null, MobjState.Chain, 0, 0), // State.Chain3 + new MobjStateDef(55, Sprite.CHGF, 32768, 5, pa.Light1, null, MobjState.Lightdone, 0, 0), // State.Chainflash1 + new MobjStateDef(56, Sprite.CHGF, 32769, 5, pa.Light2, null, MobjState.Lightdone, 0, 0), // State.Chainflash2 + new MobjStateDef(57, Sprite.MISG, 0, 1, pa.WeaponReady, null, MobjState.Missile, 0, 0), // State.Missile + new MobjStateDef(58, Sprite.MISG, 0, 1, pa.Lower, null, MobjState.Missiledown, 0, 0), // State.Missiledown + new MobjStateDef(59, Sprite.MISG, 0, 1, pa.Raise, null, MobjState.Missileup, 0, 0), // State.Missileup + new MobjStateDef(60, Sprite.MISG, 1, 8, pa.GunFlash, null, MobjState.Missile2, 0, 0), // State.Missile1 + new MobjStateDef(61, Sprite.MISG, 1, 12, pa.FireMissile, null, MobjState.Missile3, 0, 0), // State.Missile2 + new MobjStateDef(62, Sprite.MISG, 1, 0, pa.ReFire, null, MobjState.Missile, 0, 0), // State.Missile3 + new MobjStateDef(63, Sprite.MISF, 32768, 3, pa.Light1, null, MobjState.Missileflash2, 0, 0), // State.Missileflash1 + new MobjStateDef(64, Sprite.MISF, 32769, 4, null, null, MobjState.Missileflash3, 0, 0), // State.Missileflash2 + new MobjStateDef(65, Sprite.MISF, 32770, 4, pa.Light2, null, MobjState.Missileflash4, 0, 0), // State.Missileflash3 + new MobjStateDef(66, Sprite.MISF, 32771, 4, pa.Light2, null, MobjState.Lightdone, 0, 0), // State.Missileflash4 + new MobjStateDef(67, Sprite.SAWG, 2, 4, pa.WeaponReady, null, MobjState.Sawb, 0, 0), // State.Saw + new MobjStateDef(68, Sprite.SAWG, 3, 4, pa.WeaponReady, null, MobjState.Saw, 0, 0), // State.Sawb + new MobjStateDef(69, Sprite.SAWG, 2, 1, pa.Lower, null, MobjState.Sawdown, 0, 0), // State.Sawdown + new MobjStateDef(70, Sprite.SAWG, 2, 1, pa.Raise, null, MobjState.Sawup, 0, 0), // State.Sawup + new MobjStateDef(71, Sprite.SAWG, 0, 4, pa.Saw, null, MobjState.Saw2, 0, 0), // State.Saw1 + new MobjStateDef(72, Sprite.SAWG, 1, 4, pa.Saw, null, MobjState.Saw3, 0, 0), // State.Saw2 + new MobjStateDef(73, Sprite.SAWG, 1, 0, pa.ReFire, null, MobjState.Saw, 0, 0), // State.Saw3 + new MobjStateDef(74, Sprite.PLSG, 0, 1, pa.WeaponReady, null, MobjState.Plasma, 0, 0), // State.Plasma + new MobjStateDef(75, Sprite.PLSG, 0, 1, pa.Lower, null, MobjState.Plasmadown, 0, 0), // State.Plasmadown + new MobjStateDef(76, Sprite.PLSG, 0, 1, pa.Raise, null, MobjState.Plasmaup, 0, 0), // State.Plasmaup + new MobjStateDef(77, Sprite.PLSG, 0, 3, pa.FirePlasma, null, MobjState.Plasma2, 0, 0), // State.Plasma1 + new MobjStateDef(78, Sprite.PLSG, 1, 20, pa.ReFire, null, MobjState.Plasma, 0, 0), // State.Plasma2 + new MobjStateDef(79, Sprite.PLSF, 32768, 4, pa.Light1, null, MobjState.Lightdone, 0, 0), // State.Plasmaflash1 + new MobjStateDef(80, Sprite.PLSF, 32769, 4, pa.Light1, null, MobjState.Lightdone, 0, 0), // State.Plasmaflash2 + new MobjStateDef(81, Sprite.BFGG, 0, 1, pa.WeaponReady, null, MobjState.Bfg, 0, 0), // State.Bfg + new MobjStateDef(82, Sprite.BFGG, 0, 1, pa.Lower, null, MobjState.Bfgdown, 0, 0), // State.Bfgdown + new MobjStateDef(83, Sprite.BFGG, 0, 1, pa.Raise, null, MobjState.Bfgup, 0, 0), // State.Bfgup + new MobjStateDef(84, Sprite.BFGG, 0, 20, pa.BFGsound, null, MobjState.Bfg2, 0, 0), // State.Bfg1 + new MobjStateDef(85, Sprite.BFGG, 1, 10, pa.GunFlash, null, MobjState.Bfg3, 0, 0), // State.Bfg2 + new MobjStateDef(86, Sprite.BFGG, 1, 10, pa.FireBFG, null, MobjState.Bfg4, 0, 0), // State.Bfg3 + new MobjStateDef(87, Sprite.BFGG, 1, 20, pa.ReFire, null, MobjState.Bfg, 0, 0), // State.Bfg4 + new MobjStateDef(88, Sprite.BFGF, 32768, 11, pa.Light1, null, MobjState.Bfgflash2, 0, 0), // State.Bfgflash1 + new MobjStateDef(89, Sprite.BFGF, 32769, 6, pa.Light2, null, MobjState.Lightdone, 0, 0), // State.Bfgflash2 + new MobjStateDef(90, Sprite.BLUD, 2, 8, null, null, MobjState.Blood2, 0, 0), // State.Blood1 + new MobjStateDef(91, Sprite.BLUD, 1, 8, null, null, MobjState.Blood3, 0, 0), // State.Blood2 + new MobjStateDef(92, Sprite.BLUD, 0, 8, null, null, MobjState.Null, 0, 0), // State.Blood3 + new MobjStateDef(93, Sprite.PUFF, 32768, 4, null, null, MobjState.Puff2, 0, 0), // State.Puff1 + new MobjStateDef(94, Sprite.PUFF, 1, 4, null, null, MobjState.Puff3, 0, 0), // State.Puff2 + new MobjStateDef(95, Sprite.PUFF, 2, 4, null, null, MobjState.Puff4, 0, 0), // State.Puff3 + new MobjStateDef(96, Sprite.PUFF, 3, 4, null, null, MobjState.Null, 0, 0), // State.Puff4 + new MobjStateDef(97, Sprite.BAL1, 32768, 4, null, null, MobjState.Tball2, 0, 0), // State.Tball1 + new MobjStateDef(98, Sprite.BAL1, 32769, 4, null, null, MobjState.Tball1, 0, 0), // State.Tball2 + new MobjStateDef(99, Sprite.BAL1, 32770, 6, null, null, MobjState.Tballx2, 0, 0), // State.Tballx1 + new MobjStateDef(100, Sprite.BAL1, 32771, 6, null, null, MobjState.Tballx3, 0, 0), // State.Tballx2 + new MobjStateDef(101, Sprite.BAL1, 32772, 6, null, null, MobjState.Null, 0, 0), // State.Tballx3 + new MobjStateDef(102, Sprite.BAL2, 32768, 4, null, null, MobjState.Rball2, 0, 0), // State.Rball1 + new MobjStateDef(103, Sprite.BAL2, 32769, 4, null, null, MobjState.Rball1, 0, 0), // State.Rball2 + new MobjStateDef(104, Sprite.BAL2, 32770, 6, null, null, MobjState.Rballx2, 0, 0), // State.Rballx1 + new MobjStateDef(105, Sprite.BAL2, 32771, 6, null, null, MobjState.Rballx3, 0, 0), // State.Rballx2 + new MobjStateDef(106, Sprite.BAL2, 32772, 6, null, null, MobjState.Null, 0, 0), // State.Rballx3 + new MobjStateDef(107, Sprite.PLSS, 32768, 6, null, null, MobjState.Plasball2, 0, 0), // State.Plasball + new MobjStateDef(108, Sprite.PLSS, 32769, 6, null, null, MobjState.Plasball, 0, 0), // State.Plasball2 + new MobjStateDef(109, Sprite.PLSE, 32768, 4, null, null, MobjState.Plasexp2, 0, 0), // State.Plasexp + new MobjStateDef(110, Sprite.PLSE, 32769, 4, null, null, MobjState.Plasexp3, 0, 0), // State.Plasexp2 + new MobjStateDef(111, Sprite.PLSE, 32770, 4, null, null, MobjState.Plasexp4, 0, 0), // State.Plasexp3 + new MobjStateDef(112, Sprite.PLSE, 32771, 4, null, null, MobjState.Plasexp5, 0, 0), // State.Plasexp4 + new MobjStateDef(113, Sprite.PLSE, 32772, 4, null, null, MobjState.Null, 0, 0), // State.Plasexp5 + new MobjStateDef(114, Sprite.MISL, 32768, 1, null, null, MobjState.Rocket, 0, 0), // State.Rocket + new MobjStateDef(115, Sprite.BFS1, 32768, 4, null, null, MobjState.Bfgshot2, 0, 0), // State.Bfgshot + new MobjStateDef(116, Sprite.BFS1, 32769, 4, null, null, MobjState.Bfgshot, 0, 0), // State.Bfgshot2 + new MobjStateDef(117, Sprite.BFE1, 32768, 8, null, null, MobjState.Bfgland2, 0, 0), // State.Bfgland + new MobjStateDef(118, Sprite.BFE1, 32769, 8, null, null, MobjState.Bfgland3, 0, 0), // State.Bfgland2 + new MobjStateDef(119, Sprite.BFE1, 32770, 8, null, ma.BFGSpray, MobjState.Bfgland4, 0, 0), // State.Bfgland3 + new MobjStateDef(120, Sprite.BFE1, 32771, 8, null, null, MobjState.Bfgland5, 0, 0), // State.Bfgland4 + new MobjStateDef(121, Sprite.BFE1, 32772, 8, null, null, MobjState.Bfgland6, 0, 0), // State.Bfgland5 + new MobjStateDef(122, Sprite.BFE1, 32773, 8, null, null, MobjState.Null, 0, 0), // State.Bfgland6 + new MobjStateDef(123, Sprite.BFE2, 32768, 8, null, null, MobjState.Bfgexp2, 0, 0), // State.Bfgexp + new MobjStateDef(124, Sprite.BFE2, 32769, 8, null, null, MobjState.Bfgexp3, 0, 0), // State.Bfgexp2 + new MobjStateDef(125, Sprite.BFE2, 32770, 8, null, null, MobjState.Bfgexp4, 0, 0), // State.Bfgexp3 + new MobjStateDef(126, Sprite.BFE2, 32771, 8, null, null, MobjState.Null, 0, 0), // State.Bfgexp4 + new MobjStateDef(127, Sprite.MISL, 32769, 8, null, ma.Explode, MobjState.Explode2, 0, 0), // State.Explode1 + new MobjStateDef(128, Sprite.MISL, 32770, 6, null, null, MobjState.Explode3, 0, 0), // State.Explode2 + new MobjStateDef(129, Sprite.MISL, 32771, 4, null, null, MobjState.Null, 0, 0), // State.Explode3 + new MobjStateDef(130, Sprite.TFOG, 32768, 6, null, null, MobjState.Tfog01, 0, 0), // State.Tfog + new MobjStateDef(131, Sprite.TFOG, 32769, 6, null, null, MobjState.Tfog02, 0, 0), // State.Tfog01 + new MobjStateDef(132, Sprite.TFOG, 32768, 6, null, null, MobjState.Tfog2, 0, 0), // State.Tfog02 + new MobjStateDef(133, Sprite.TFOG, 32769, 6, null, null, MobjState.Tfog3, 0, 0), // State.Tfog2 + new MobjStateDef(134, Sprite.TFOG, 32770, 6, null, null, MobjState.Tfog4, 0, 0), // State.Tfog3 + new MobjStateDef(135, Sprite.TFOG, 32771, 6, null, null, MobjState.Tfog5, 0, 0), // State.Tfog4 + new MobjStateDef(136, Sprite.TFOG, 32772, 6, null, null, MobjState.Tfog6, 0, 0), // State.Tfog5 + new MobjStateDef(137, Sprite.TFOG, 32773, 6, null, null, MobjState.Tfog7, 0, 0), // State.Tfog6 + new MobjStateDef(138, Sprite.TFOG, 32774, 6, null, null, MobjState.Tfog8, 0, 0), // State.Tfog7 + new MobjStateDef(139, Sprite.TFOG, 32775, 6, null, null, MobjState.Tfog9, 0, 0), // State.Tfog8 + new MobjStateDef(140, Sprite.TFOG, 32776, 6, null, null, MobjState.Tfog10, 0, 0), // State.Tfog9 + new MobjStateDef(141, Sprite.TFOG, 32777, 6, null, null, MobjState.Null, 0, 0), // State.Tfog10 + new MobjStateDef(142, Sprite.IFOG, 32768, 6, null, null, MobjState.Ifog01, 0, 0), // State.Ifog + new MobjStateDef(143, Sprite.IFOG, 32769, 6, null, null, MobjState.Ifog02, 0, 0), // State.Ifog01 + new MobjStateDef(144, Sprite.IFOG, 32768, 6, null, null, MobjState.Ifog2, 0, 0), // State.Ifog02 + new MobjStateDef(145, Sprite.IFOG, 32769, 6, null, null, MobjState.Ifog3, 0, 0), // State.Ifog2 + new MobjStateDef(146, Sprite.IFOG, 32770, 6, null, null, MobjState.Ifog4, 0, 0), // State.Ifog3 + new MobjStateDef(147, Sprite.IFOG, 32771, 6, null, null, MobjState.Ifog5, 0, 0), // State.Ifog4 + new MobjStateDef(148, Sprite.IFOG, 32772, 6, null, null, MobjState.Null, 0, 0), // State.Ifog5 + new MobjStateDef(149, Sprite.PLAY, 0, -1, null, null, MobjState.Null, 0, 0), // State.Play + new MobjStateDef(150, Sprite.PLAY, 0, 4, null, null, MobjState.PlayRun2, 0, 0), // State.PlayRun1 + new MobjStateDef(151, Sprite.PLAY, 1, 4, null, null, MobjState.PlayRun3, 0, 0), // State.PlayRun2 + new MobjStateDef(152, Sprite.PLAY, 2, 4, null, null, MobjState.PlayRun4, 0, 0), // State.PlayRun3 + new MobjStateDef(153, Sprite.PLAY, 3, 4, null, null, MobjState.PlayRun1, 0, 0), // State.PlayRun4 + new MobjStateDef(154, Sprite.PLAY, 4, 12, null, null, MobjState.Play, 0, 0), // State.PlayAtk1 + new MobjStateDef(155, Sprite.PLAY, 32773, 6, null, null, MobjState.PlayAtk1, 0, 0), // State.PlayAtk2 + new MobjStateDef(156, Sprite.PLAY, 6, 4, null, null, MobjState.PlayPain2, 0, 0), // State.PlayPain + new MobjStateDef(157, Sprite.PLAY, 6, 4, null, ma.Pain, MobjState.Play, 0, 0), // State.PlayPain2 + new MobjStateDef(158, Sprite.PLAY, 7, 10, null, null, MobjState.PlayDie2, 0, 0), // State.PlayDie1 + new MobjStateDef(159, Sprite.PLAY, 8, 10, null, ma.PlayerScream, MobjState.PlayDie3, 0, 0), // State.PlayDie2 + new MobjStateDef(160, Sprite.PLAY, 9, 10, null, ma.Fall, MobjState.PlayDie4, 0, 0), // State.PlayDie3 + new MobjStateDef(161, Sprite.PLAY, 10, 10, null, null, MobjState.PlayDie5, 0, 0), // State.PlayDie4 + new MobjStateDef(162, Sprite.PLAY, 11, 10, null, null, MobjState.PlayDie6, 0, 0), // State.PlayDie5 + new MobjStateDef(163, Sprite.PLAY, 12, 10, null, null, MobjState.PlayDie7, 0, 0), // State.PlayDie6 + new MobjStateDef(164, Sprite.PLAY, 13, -1, null, null, MobjState.Null, 0, 0), // State.PlayDie7 + new MobjStateDef(165, Sprite.PLAY, 14, 5, null, null, MobjState.PlayXdie2, 0, 0), // State.PlayXdie1 + new MobjStateDef(166, Sprite.PLAY, 15, 5, null, ma.XScream, MobjState.PlayXdie3, 0, 0), // State.PlayXdie2 + new MobjStateDef(167, Sprite.PLAY, 16, 5, null, ma.Fall, MobjState.PlayXdie4, 0, 0), // State.PlayXdie3 + new MobjStateDef(168, Sprite.PLAY, 17, 5, null, null, MobjState.PlayXdie5, 0, 0), // State.PlayXdie4 + new MobjStateDef(169, Sprite.PLAY, 18, 5, null, null, MobjState.PlayXdie6, 0, 0), // State.PlayXdie5 + new MobjStateDef(170, Sprite.PLAY, 19, 5, null, null, MobjState.PlayXdie7, 0, 0), // State.PlayXdie6 + new MobjStateDef(171, Sprite.PLAY, 20, 5, null, null, MobjState.PlayXdie8, 0, 0), // State.PlayXdie7 + new MobjStateDef(172, Sprite.PLAY, 21, 5, null, null, MobjState.PlayXdie9, 0, 0), // State.PlayXdie8 + new MobjStateDef(173, Sprite.PLAY, 22, -1, null, null, MobjState.Null, 0, 0), // State.PlayXdie9 + new MobjStateDef(174, Sprite.POSS, 0, 10, null, ma.Look, MobjState.PossStnd2, 0, 0), // State.PossStnd + new MobjStateDef(175, Sprite.POSS, 1, 10, null, ma.Look, MobjState.PossStnd, 0, 0), // State.PossStnd2 + new MobjStateDef(176, Sprite.POSS, 0, 4, null, ma.Chase, MobjState.PossRun2, 0, 0), // State.PossRun1 + new MobjStateDef(177, Sprite.POSS, 0, 4, null, ma.Chase, MobjState.PossRun3, 0, 0), // State.PossRun2 + new MobjStateDef(178, Sprite.POSS, 1, 4, null, ma.Chase, MobjState.PossRun4, 0, 0), // State.PossRun3 + new MobjStateDef(179, Sprite.POSS, 1, 4, null, ma.Chase, MobjState.PossRun5, 0, 0), // State.PossRun4 + new MobjStateDef(180, Sprite.POSS, 2, 4, null, ma.Chase, MobjState.PossRun6, 0, 0), // State.PossRun5 + new MobjStateDef(181, Sprite.POSS, 2, 4, null, ma.Chase, MobjState.PossRun7, 0, 0), // State.PossRun6 + new MobjStateDef(182, Sprite.POSS, 3, 4, null, ma.Chase, MobjState.PossRun8, 0, 0), // State.PossRun7 + new MobjStateDef(183, Sprite.POSS, 3, 4, null, ma.Chase, MobjState.PossRun1, 0, 0), // State.PossRun8 + new MobjStateDef(184, Sprite.POSS, 4, 10, null, ma.FaceTarget, MobjState.PossAtk2, 0, 0), // State.PossAtk1 + new MobjStateDef(185, Sprite.POSS, 5, 8, null, ma.PosAttack, MobjState.PossAtk3, 0, 0), // State.PossAtk2 + new MobjStateDef(186, Sprite.POSS, 4, 8, null, null, MobjState.PossRun1, 0, 0), // State.PossAtk3 + new MobjStateDef(187, Sprite.POSS, 6, 3, null, null, MobjState.PossPain2, 0, 0), // State.PossPain + new MobjStateDef(188, Sprite.POSS, 6, 3, null, ma.Pain, MobjState.PossRun1, 0, 0), // State.PossPain2 + new MobjStateDef(189, Sprite.POSS, 7, 5, null, null, MobjState.PossDie2, 0, 0), // State.PossDie1 + new MobjStateDef(190, Sprite.POSS, 8, 5, null, ma.Scream, MobjState.PossDie3, 0, 0), // State.PossDie2 + new MobjStateDef(191, Sprite.POSS, 9, 5, null, ma.Fall, MobjState.PossDie4, 0, 0), // State.PossDie3 + new MobjStateDef(192, Sprite.POSS, 10, 5, null, null, MobjState.PossDie5, 0, 0), // State.PossDie4 + new MobjStateDef(193, Sprite.POSS, 11, -1, null, null, MobjState.Null, 0, 0), // State.PossDie5 + new MobjStateDef(194, Sprite.POSS, 12, 5, null, null, MobjState.PossXdie2, 0, 0), // State.PossXdie1 + new MobjStateDef(195, Sprite.POSS, 13, 5, null, ma.XScream, MobjState.PossXdie3, 0, 0), // State.PossXdie2 + new MobjStateDef(196, Sprite.POSS, 14, 5, null, ma.Fall, MobjState.PossXdie4, 0, 0), // State.PossXdie3 + new MobjStateDef(197, Sprite.POSS, 15, 5, null, null, MobjState.PossXdie5, 0, 0), // State.PossXdie4 + new MobjStateDef(198, Sprite.POSS, 16, 5, null, null, MobjState.PossXdie6, 0, 0), // State.PossXdie5 + new MobjStateDef(199, Sprite.POSS, 17, 5, null, null, MobjState.PossXdie7, 0, 0), // State.PossXdie6 + new MobjStateDef(200, Sprite.POSS, 18, 5, null, null, MobjState.PossXdie8, 0, 0), // State.PossXdie7 + new MobjStateDef(201, Sprite.POSS, 19, 5, null, null, MobjState.PossXdie9, 0, 0), // State.PossXdie8 + new MobjStateDef(202, Sprite.POSS, 20, -1, null, null, MobjState.Null, 0, 0), // State.PossXdie9 + new MobjStateDef(203, Sprite.POSS, 10, 5, null, null, MobjState.PossRaise2, 0, 0), // State.PossRaise1 + new MobjStateDef(204, Sprite.POSS, 9, 5, null, null, MobjState.PossRaise3, 0, 0), // State.PossRaise2 + new MobjStateDef(205, Sprite.POSS, 8, 5, null, null, MobjState.PossRaise4, 0, 0), // State.PossRaise3 + new MobjStateDef(206, Sprite.POSS, 7, 5, null, null, MobjState.PossRun1, 0, 0), // State.PossRaise4 + new MobjStateDef(207, Sprite.SPOS, 0, 10, null, ma.Look, MobjState.SposStnd2, 0, 0), // State.SposStnd + new MobjStateDef(208, Sprite.SPOS, 1, 10, null, ma.Look, MobjState.SposStnd, 0, 0), // State.SposStnd2 + new MobjStateDef(209, Sprite.SPOS, 0, 3, null, ma.Chase, MobjState.SposRun2, 0, 0), // State.SposRun1 + new MobjStateDef(210, Sprite.SPOS, 0, 3, null, ma.Chase, MobjState.SposRun3, 0, 0), // State.SposRun2 + new MobjStateDef(211, Sprite.SPOS, 1, 3, null, ma.Chase, MobjState.SposRun4, 0, 0), // State.SposRun3 + new MobjStateDef(212, Sprite.SPOS, 1, 3, null, ma.Chase, MobjState.SposRun5, 0, 0), // State.SposRun4 + new MobjStateDef(213, Sprite.SPOS, 2, 3, null, ma.Chase, MobjState.SposRun6, 0, 0), // State.SposRun5 + new MobjStateDef(214, Sprite.SPOS, 2, 3, null, ma.Chase, MobjState.SposRun7, 0, 0), // State.SposRun6 + new MobjStateDef(215, Sprite.SPOS, 3, 3, null, ma.Chase, MobjState.SposRun8, 0, 0), // State.SposRun7 + new MobjStateDef(216, Sprite.SPOS, 3, 3, null, ma.Chase, MobjState.SposRun1, 0, 0), // State.SposRun8 + new MobjStateDef(217, Sprite.SPOS, 4, 10, null, ma.FaceTarget, MobjState.SposAtk2, 0, 0), // State.SposAtk1 + new MobjStateDef(218, Sprite.SPOS, 32773, 10, null, ma.SPosAttack, MobjState.SposAtk3, 0, 0), // State.SposAtk2 + new MobjStateDef(219, Sprite.SPOS, 4, 10, null, null, MobjState.SposRun1, 0, 0), // State.SposAtk3 + new MobjStateDef(220, Sprite.SPOS, 6, 3, null, null, MobjState.SposPain2, 0, 0), // State.SposPain + new MobjStateDef(221, Sprite.SPOS, 6, 3, null, ma.Pain, MobjState.SposRun1, 0, 0), // State.SposPain2 + new MobjStateDef(222, Sprite.SPOS, 7, 5, null, null, MobjState.SposDie2, 0, 0), // State.SposDie1 + new MobjStateDef(223, Sprite.SPOS, 8, 5, null, ma.Scream, MobjState.SposDie3, 0, 0), // State.SposDie2 + new MobjStateDef(224, Sprite.SPOS, 9, 5, null, ma.Fall, MobjState.SposDie4, 0, 0), // State.SposDie3 + new MobjStateDef(225, Sprite.SPOS, 10, 5, null, null, MobjState.SposDie5, 0, 0), // State.SposDie4 + new MobjStateDef(226, Sprite.SPOS, 11, -1, null, null, MobjState.Null, 0, 0), // State.SposDie5 + new MobjStateDef(227, Sprite.SPOS, 12, 5, null, null, MobjState.SposXdie2, 0, 0), // State.SposXdie1 + new MobjStateDef(228, Sprite.SPOS, 13, 5, null, ma.XScream, MobjState.SposXdie3, 0, 0), // State.SposXdie2 + new MobjStateDef(229, Sprite.SPOS, 14, 5, null, ma.Fall, MobjState.SposXdie4, 0, 0), // State.SposXdie3 + new MobjStateDef(230, Sprite.SPOS, 15, 5, null, null, MobjState.SposXdie5, 0, 0), // State.SposXdie4 + new MobjStateDef(231, Sprite.SPOS, 16, 5, null, null, MobjState.SposXdie6, 0, 0), // State.SposXdie5 + new MobjStateDef(232, Sprite.SPOS, 17, 5, null, null, MobjState.SposXdie7, 0, 0), // State.SposXdie6 + new MobjStateDef(233, Sprite.SPOS, 18, 5, null, null, MobjState.SposXdie8, 0, 0), // State.SposXdie7 + new MobjStateDef(234, Sprite.SPOS, 19, 5, null, null, MobjState.SposXdie9, 0, 0), // State.SposXdie8 + new MobjStateDef(235, Sprite.SPOS, 20, -1, null, null, MobjState.Null, 0, 0), // State.SposXdie9 + new MobjStateDef(236, Sprite.SPOS, 11, 5, null, null, MobjState.SposRaise2, 0, 0), // State.SposRaise1 + new MobjStateDef(237, Sprite.SPOS, 10, 5, null, null, MobjState.SposRaise3, 0, 0), // State.SposRaise2 + new MobjStateDef(238, Sprite.SPOS, 9, 5, null, null, MobjState.SposRaise4, 0, 0), // State.SposRaise3 + new MobjStateDef(239, Sprite.SPOS, 8, 5, null, null, MobjState.SposRaise5, 0, 0), // State.SposRaise4 + new MobjStateDef(240, Sprite.SPOS, 7, 5, null, null, MobjState.SposRun1, 0, 0), // State.SposRaise5 + new MobjStateDef(241, Sprite.VILE, 0, 10, null, ma.Look, MobjState.VileStnd2, 0, 0), // State.VileStnd + new MobjStateDef(242, Sprite.VILE, 1, 10, null, ma.Look, MobjState.VileStnd, 0, 0), // State.VileStnd2 + new MobjStateDef(243, Sprite.VILE, 0, 2, null, ma.VileChase, MobjState.VileRun2, 0, 0), // State.VileRun1 + new MobjStateDef(244, Sprite.VILE, 0, 2, null, ma.VileChase, MobjState.VileRun3, 0, 0), // State.VileRun2 + new MobjStateDef(245, Sprite.VILE, 1, 2, null, ma.VileChase, MobjState.VileRun4, 0, 0), // State.VileRun3 + new MobjStateDef(246, Sprite.VILE, 1, 2, null, ma.VileChase, MobjState.VileRun5, 0, 0), // State.VileRun4 + new MobjStateDef(247, Sprite.VILE, 2, 2, null, ma.VileChase, MobjState.VileRun6, 0, 0), // State.VileRun5 + new MobjStateDef(248, Sprite.VILE, 2, 2, null, ma.VileChase, MobjState.VileRun7, 0, 0), // State.VileRun6 + new MobjStateDef(249, Sprite.VILE, 3, 2, null, ma.VileChase, MobjState.VileRun8, 0, 0), // State.VileRun7 + new MobjStateDef(250, Sprite.VILE, 3, 2, null, ma.VileChase, MobjState.VileRun9, 0, 0), // State.VileRun8 + new MobjStateDef(251, Sprite.VILE, 4, 2, null, ma.VileChase, MobjState.VileRun10, 0, 0), // State.VileRun9 + new MobjStateDef(252, Sprite.VILE, 4, 2, null, ma.VileChase, MobjState.VileRun11, 0, 0), // State.VileRun10 + new MobjStateDef(253, Sprite.VILE, 5, 2, null, ma.VileChase, MobjState.VileRun12, 0, 0), // State.VileRun11 + new MobjStateDef(254, Sprite.VILE, 5, 2, null, ma.VileChase, MobjState.VileRun1, 0, 0), // State.VileRun12 + new MobjStateDef(255, Sprite.VILE, 32774, 0, null, ma.VileStart, MobjState.VileAtk2, 0, 0), // State.VileAtk1 + new MobjStateDef(256, Sprite.VILE, 32774, 10, null, ma.FaceTarget, MobjState.VileAtk3, 0, 0), // State.VileAtk2 + new MobjStateDef(257, Sprite.VILE, 32775, 8, null, ma.VileTarget, MobjState.VileAtk4, 0, 0), // State.VileAtk3 + new MobjStateDef(258, Sprite.VILE, 32776, 8, null, ma.FaceTarget, MobjState.VileAtk5, 0, 0), // State.VileAtk4 + new MobjStateDef(259, Sprite.VILE, 32777, 8, null, ma.FaceTarget, MobjState.VileAtk6, 0, 0), // State.VileAtk5 + new MobjStateDef(260, Sprite.VILE, 32778, 8, null, ma.FaceTarget, MobjState.VileAtk7, 0, 0), // State.VileAtk6 + new MobjStateDef(261, Sprite.VILE, 32779, 8, null, ma.FaceTarget, MobjState.VileAtk8, 0, 0), // State.VileAtk7 + new MobjStateDef(262, Sprite.VILE, 32780, 8, null, ma.FaceTarget, MobjState.VileAtk9, 0, 0), // State.VileAtk8 + new MobjStateDef(263, Sprite.VILE, 32781, 8, null, ma.FaceTarget, MobjState.VileAtk10, 0, 0), // State.VileAtk9 + new MobjStateDef(264, Sprite.VILE, 32782, 8, null, ma.VileAttack, MobjState.VileAtk11, 0, 0), // State.VileAtk10 + new MobjStateDef(265, Sprite.VILE, 32783, 20, null, null, MobjState.VileRun1, 0, 0), // State.VileAtk11 + new MobjStateDef(266, Sprite.VILE, 32794, 10, null, null, MobjState.VileHeal2, 0, 0), // State.VileHeal1 + new MobjStateDef(267, Sprite.VILE, 32795, 10, null, null, MobjState.VileHeal3, 0, 0), // State.VileHeal2 + new MobjStateDef(268, Sprite.VILE, 32796, 10, null, null, MobjState.VileRun1, 0, 0), // State.VileHeal3 + new MobjStateDef(269, Sprite.VILE, 16, 5, null, null, MobjState.VilePain2, 0, 0), // State.VilePain + new MobjStateDef(270, Sprite.VILE, 16, 5, null, ma.Pain, MobjState.VileRun1, 0, 0), // State.VilePain2 + new MobjStateDef(271, Sprite.VILE, 16, 7, null, null, MobjState.VileDie2, 0, 0), // State.VileDie1 + new MobjStateDef(272, Sprite.VILE, 17, 7, null, ma.Scream, MobjState.VileDie3, 0, 0), // State.VileDie2 + new MobjStateDef(273, Sprite.VILE, 18, 7, null, ma.Fall, MobjState.VileDie4, 0, 0), // State.VileDie3 + new MobjStateDef(274, Sprite.VILE, 19, 7, null, null, MobjState.VileDie5, 0, 0), // State.VileDie4 + new MobjStateDef(275, Sprite.VILE, 20, 7, null, null, MobjState.VileDie6, 0, 0), // State.VileDie5 + new MobjStateDef(276, Sprite.VILE, 21, 7, null, null, MobjState.VileDie7, 0, 0), // State.VileDie6 + new MobjStateDef(277, Sprite.VILE, 22, 7, null, null, MobjState.VileDie8, 0, 0), // State.VileDie7 + new MobjStateDef(278, Sprite.VILE, 23, 5, null, null, MobjState.VileDie9, 0, 0), // State.VileDie8 + new MobjStateDef(279, Sprite.VILE, 24, 5, null, null, MobjState.VileDie10, 0, 0), // State.VileDie9 + new MobjStateDef(280, Sprite.VILE, 25, -1, null, null, MobjState.Null, 0, 0), // State.VileDie10 + new MobjStateDef(281, Sprite.FIRE, 32768, 2, null, ma.StartFire, MobjState.Fire2, 0, 0), // State.Fire1 + new MobjStateDef(282, Sprite.FIRE, 32769, 2, null, ma.Fire, MobjState.Fire3, 0, 0), // State.Fire2 + new MobjStateDef(283, Sprite.FIRE, 32768, 2, null, ma.Fire, MobjState.Fire4, 0, 0), // State.Fire3 + new MobjStateDef(284, Sprite.FIRE, 32769, 2, null, ma.Fire, MobjState.Fire5, 0, 0), // State.Fire4 + new MobjStateDef(285, Sprite.FIRE, 32770, 2, null, ma.FireCrackle, MobjState.Fire6, 0, 0), // State.Fire5 + new MobjStateDef(286, Sprite.FIRE, 32769, 2, null, ma.Fire, MobjState.Fire7, 0, 0), // State.Fire6 + new MobjStateDef(287, Sprite.FIRE, 32770, 2, null, ma.Fire, MobjState.Fire8, 0, 0), // State.Fire7 + new MobjStateDef(288, Sprite.FIRE, 32769, 2, null, ma.Fire, MobjState.Fire9, 0, 0), // State.Fire8 + new MobjStateDef(289, Sprite.FIRE, 32770, 2, null, ma.Fire, MobjState.Fire10, 0, 0), // State.Fire9 + new MobjStateDef(290, Sprite.FIRE, 32771, 2, null, ma.Fire, MobjState.Fire11, 0, 0), // State.Fire10 + new MobjStateDef(291, Sprite.FIRE, 32770, 2, null, ma.Fire, MobjState.Fire12, 0, 0), // State.Fire11 + new MobjStateDef(292, Sprite.FIRE, 32771, 2, null, ma.Fire, MobjState.Fire13, 0, 0), // State.Fire12 + new MobjStateDef(293, Sprite.FIRE, 32770, 2, null, ma.Fire, MobjState.Fire14, 0, 0), // State.Fire13 + new MobjStateDef(294, Sprite.FIRE, 32771, 2, null, ma.Fire, MobjState.Fire15, 0, 0), // State.Fire14 + new MobjStateDef(295, Sprite.FIRE, 32772, 2, null, ma.Fire, MobjState.Fire16, 0, 0), // State.Fire15 + new MobjStateDef(296, Sprite.FIRE, 32771, 2, null, ma.Fire, MobjState.Fire17, 0, 0), // State.Fire16 + new MobjStateDef(297, Sprite.FIRE, 32772, 2, null, ma.Fire, MobjState.Fire18, 0, 0), // State.Fire17 + new MobjStateDef(298, Sprite.FIRE, 32771, 2, null, ma.Fire, MobjState.Fire19, 0, 0), // State.Fire18 + new MobjStateDef(299, Sprite.FIRE, 32772, 2, null, ma.FireCrackle, MobjState.Fire20, 0, 0), // State.Fire19 + new MobjStateDef(300, Sprite.FIRE, 32773, 2, null, ma.Fire, MobjState.Fire21, 0, 0), // State.Fire20 + new MobjStateDef(301, Sprite.FIRE, 32772, 2, null, ma.Fire, MobjState.Fire22, 0, 0), // State.Fire21 + new MobjStateDef(302, Sprite.FIRE, 32773, 2, null, ma.Fire, MobjState.Fire23, 0, 0), // State.Fire22 + new MobjStateDef(303, Sprite.FIRE, 32772, 2, null, ma.Fire, MobjState.Fire24, 0, 0), // State.Fire23 + new MobjStateDef(304, Sprite.FIRE, 32773, 2, null, ma.Fire, MobjState.Fire25, 0, 0), // State.Fire24 + new MobjStateDef(305, Sprite.FIRE, 32774, 2, null, ma.Fire, MobjState.Fire26, 0, 0), // State.Fire25 + new MobjStateDef(306, Sprite.FIRE, 32775, 2, null, ma.Fire, MobjState.Fire27, 0, 0), // State.Fire26 + new MobjStateDef(307, Sprite.FIRE, 32774, 2, null, ma.Fire, MobjState.Fire28, 0, 0), // State.Fire27 + new MobjStateDef(308, Sprite.FIRE, 32775, 2, null, ma.Fire, MobjState.Fire29, 0, 0), // State.Fire28 + new MobjStateDef(309, Sprite.FIRE, 32774, 2, null, ma.Fire, MobjState.Fire30, 0, 0), // State.Fire29 + new MobjStateDef(310, Sprite.FIRE, 32775, 2, null, ma.Fire, MobjState.Null, 0, 0), // State.Fire30 + new MobjStateDef(311, Sprite.PUFF, 1, 4, null, null, MobjState.Smoke2, 0, 0), // State.Smoke1 + new MobjStateDef(312, Sprite.PUFF, 2, 4, null, null, MobjState.Smoke3, 0, 0), // State.Smoke2 + new MobjStateDef(313, Sprite.PUFF, 1, 4, null, null, MobjState.Smoke4, 0, 0), // State.Smoke3 + new MobjStateDef(314, Sprite.PUFF, 2, 4, null, null, MobjState.Smoke5, 0, 0), // State.Smoke4 + new MobjStateDef(315, Sprite.PUFF, 3, 4, null, null, MobjState.Null, 0, 0), // State.Smoke5 + new MobjStateDef(316, Sprite.FATB, 32768, 2, null, ma.Tracer, MobjState.Tracer2, 0, 0), // State.Tracer + new MobjStateDef(317, Sprite.FATB, 32769, 2, null, ma.Tracer, MobjState.Tracer, 0, 0), // State.Tracer2 + new MobjStateDef(318, Sprite.FBXP, 32768, 8, null, null, MobjState.Traceexp2, 0, 0), // State.Traceexp1 + new MobjStateDef(319, Sprite.FBXP, 32769, 6, null, null, MobjState.Traceexp3, 0, 0), // State.Traceexp2 + new MobjStateDef(320, Sprite.FBXP, 32770, 4, null, null, MobjState.Null, 0, 0), // State.Traceexp3 + new MobjStateDef(321, Sprite.SKEL, 0, 10, null, ma.Look, MobjState.SkelStnd2, 0, 0), // State.SkelStnd + new MobjStateDef(322, Sprite.SKEL, 1, 10, null, ma.Look, MobjState.SkelStnd, 0, 0), // State.SkelStnd2 + new MobjStateDef(323, Sprite.SKEL, 0, 2, null, ma.Chase, MobjState.SkelRun2, 0, 0), // State.SkelRun1 + new MobjStateDef(324, Sprite.SKEL, 0, 2, null, ma.Chase, MobjState.SkelRun3, 0, 0), // State.SkelRun2 + new MobjStateDef(325, Sprite.SKEL, 1, 2, null, ma.Chase, MobjState.SkelRun4, 0, 0), // State.SkelRun3 + new MobjStateDef(326, Sprite.SKEL, 1, 2, null, ma.Chase, MobjState.SkelRun5, 0, 0), // State.SkelRun4 + new MobjStateDef(327, Sprite.SKEL, 2, 2, null, ma.Chase, MobjState.SkelRun6, 0, 0), // State.SkelRun5 + new MobjStateDef(328, Sprite.SKEL, 2, 2, null, ma.Chase, MobjState.SkelRun7, 0, 0), // State.SkelRun6 + new MobjStateDef(329, Sprite.SKEL, 3, 2, null, ma.Chase, MobjState.SkelRun8, 0, 0), // State.SkelRun7 + new MobjStateDef(330, Sprite.SKEL, 3, 2, null, ma.Chase, MobjState.SkelRun9, 0, 0), // State.SkelRun8 + new MobjStateDef(331, Sprite.SKEL, 4, 2, null, ma.Chase, MobjState.SkelRun10, 0, 0), // State.SkelRun9 + new MobjStateDef(332, Sprite.SKEL, 4, 2, null, ma.Chase, MobjState.SkelRun11, 0, 0), // State.SkelRun10 + new MobjStateDef(333, Sprite.SKEL, 5, 2, null, ma.Chase, MobjState.SkelRun12, 0, 0), // State.SkelRun11 + new MobjStateDef(334, Sprite.SKEL, 5, 2, null, ma.Chase, MobjState.SkelRun1, 0, 0), // State.SkelRun12 + new MobjStateDef(335, Sprite.SKEL, 6, 0, null, ma.FaceTarget, MobjState.SkelFist2, 0, 0), // State.SkelFist1 + new MobjStateDef(336, Sprite.SKEL, 6, 6, null, ma.SkelWhoosh, MobjState.SkelFist3, 0, 0), // State.SkelFist2 + new MobjStateDef(337, Sprite.SKEL, 7, 6, null, ma.FaceTarget, MobjState.SkelFist4, 0, 0), // State.SkelFist3 + new MobjStateDef(338, Sprite.SKEL, 8, 6, null, ma.SkelFist, MobjState.SkelRun1, 0, 0), // State.SkelFist4 + new MobjStateDef(339, Sprite.SKEL, 32777, 0, null, ma.FaceTarget, MobjState.SkelMiss2, 0, 0), // State.SkelMiss1 + new MobjStateDef(340, Sprite.SKEL, 32777, 10, null, ma.FaceTarget, MobjState.SkelMiss3, 0, 0), // State.SkelMiss2 + new MobjStateDef(341, Sprite.SKEL, 10, 10, null, ma.SkelMissile, MobjState.SkelMiss4, 0, 0), // State.SkelMiss3 + new MobjStateDef(342, Sprite.SKEL, 10, 10, null, ma.FaceTarget, MobjState.SkelRun1, 0, 0), // State.SkelMiss4 + new MobjStateDef(343, Sprite.SKEL, 11, 5, null, null, MobjState.SkelPain2, 0, 0), // State.SkelPain + new MobjStateDef(344, Sprite.SKEL, 11, 5, null, ma.Pain, MobjState.SkelRun1, 0, 0), // State.SkelPain2 + new MobjStateDef(345, Sprite.SKEL, 11, 7, null, null, MobjState.SkelDie2, 0, 0), // State.SkelDie1 + new MobjStateDef(346, Sprite.SKEL, 12, 7, null, null, MobjState.SkelDie3, 0, 0), // State.SkelDie2 + new MobjStateDef(347, Sprite.SKEL, 13, 7, null, ma.Scream, MobjState.SkelDie4, 0, 0), // State.SkelDie3 + new MobjStateDef(348, Sprite.SKEL, 14, 7, null, ma.Fall, MobjState.SkelDie5, 0, 0), // State.SkelDie4 + new MobjStateDef(349, Sprite.SKEL, 15, 7, null, null, MobjState.SkelDie6, 0, 0), // State.SkelDie5 + new MobjStateDef(350, Sprite.SKEL, 16, -1, null, null, MobjState.Null, 0, 0), // State.SkelDie6 + new MobjStateDef(351, Sprite.SKEL, 16, 5, null, null, MobjState.SkelRaise2, 0, 0), // State.SkelRaise1 + new MobjStateDef(352, Sprite.SKEL, 15, 5, null, null, MobjState.SkelRaise3, 0, 0), // State.SkelRaise2 + new MobjStateDef(353, Sprite.SKEL, 14, 5, null, null, MobjState.SkelRaise4, 0, 0), // State.SkelRaise3 + new MobjStateDef(354, Sprite.SKEL, 13, 5, null, null, MobjState.SkelRaise5, 0, 0), // State.SkelRaise4 + new MobjStateDef(355, Sprite.SKEL, 12, 5, null, null, MobjState.SkelRaise6, 0, 0), // State.SkelRaise5 + new MobjStateDef(356, Sprite.SKEL, 11, 5, null, null, MobjState.SkelRun1, 0, 0), // State.SkelRaise6 + new MobjStateDef(357, Sprite.MANF, 32768, 4, null, null, MobjState.Fatshot2, 0, 0), // State.Fatshot1 + new MobjStateDef(358, Sprite.MANF, 32769, 4, null, null, MobjState.Fatshot1, 0, 0), // State.Fatshot2 + new MobjStateDef(359, Sprite.MISL, 32769, 8, null, null, MobjState.Fatshotx2, 0, 0), // State.Fatshotx1 + new MobjStateDef(360, Sprite.MISL, 32770, 6, null, null, MobjState.Fatshotx3, 0, 0), // State.Fatshotx2 + new MobjStateDef(361, Sprite.MISL, 32771, 4, null, null, MobjState.Null, 0, 0), // State.Fatshotx3 + new MobjStateDef(362, Sprite.FATT, 0, 15, null, ma.Look, MobjState.FattStnd2, 0, 0), // State.FattStnd + new MobjStateDef(363, Sprite.FATT, 1, 15, null, ma.Look, MobjState.FattStnd, 0, 0), // State.FattStnd2 + new MobjStateDef(364, Sprite.FATT, 0, 4, null, ma.Chase, MobjState.FattRun2, 0, 0), // State.FattRun1 + new MobjStateDef(365, Sprite.FATT, 0, 4, null, ma.Chase, MobjState.FattRun3, 0, 0), // State.FattRun2 + new MobjStateDef(366, Sprite.FATT, 1, 4, null, ma.Chase, MobjState.FattRun4, 0, 0), // State.FattRun3 + new MobjStateDef(367, Sprite.FATT, 1, 4, null, ma.Chase, MobjState.FattRun5, 0, 0), // State.FattRun4 + new MobjStateDef(368, Sprite.FATT, 2, 4, null, ma.Chase, MobjState.FattRun6, 0, 0), // State.FattRun5 + new MobjStateDef(369, Sprite.FATT, 2, 4, null, ma.Chase, MobjState.FattRun7, 0, 0), // State.FattRun6 + new MobjStateDef(370, Sprite.FATT, 3, 4, null, ma.Chase, MobjState.FattRun8, 0, 0), // State.FattRun7 + new MobjStateDef(371, Sprite.FATT, 3, 4, null, ma.Chase, MobjState.FattRun9, 0, 0), // State.FattRun8 + new MobjStateDef(372, Sprite.FATT, 4, 4, null, ma.Chase, MobjState.FattRun10, 0, 0), // State.FattRun9 + new MobjStateDef(373, Sprite.FATT, 4, 4, null, ma.Chase, MobjState.FattRun11, 0, 0), // State.FattRun10 + new MobjStateDef(374, Sprite.FATT, 5, 4, null, ma.Chase, MobjState.FattRun12, 0, 0), // State.FattRun11 + new MobjStateDef(375, Sprite.FATT, 5, 4, null, ma.Chase, MobjState.FattRun1, 0, 0), // State.FattRun12 + new MobjStateDef(376, Sprite.FATT, 6, 20, null, ma.FatRaise, MobjState.FattAtk2, 0, 0), // State.FattAtk1 + new MobjStateDef(377, Sprite.FATT, 32775, 10, null, ma.FatAttack1, MobjState.FattAtk3, 0, 0), // State.FattAtk2 + new MobjStateDef(378, Sprite.FATT, 8, 5, null, ma.FaceTarget, MobjState.FattAtk4, 0, 0), // State.FattAtk3 + new MobjStateDef(379, Sprite.FATT, 6, 5, null, ma.FaceTarget, MobjState.FattAtk5, 0, 0), // State.FattAtk4 + new MobjStateDef(380, Sprite.FATT, 32775, 10, null, ma.FatAttack2, MobjState.FattAtk6, 0, 0), // State.FattAtk5 + new MobjStateDef(381, Sprite.FATT, 8, 5, null, ma.FaceTarget, MobjState.FattAtk7, 0, 0), // State.FattAtk6 + new MobjStateDef(382, Sprite.FATT, 6, 5, null, ma.FaceTarget, MobjState.FattAtk8, 0, 0), // State.FattAtk7 + new MobjStateDef(383, Sprite.FATT, 32775, 10, null, ma.FatAttack3, MobjState.FattAtk9, 0, 0), // State.FattAtk8 + new MobjStateDef(384, Sprite.FATT, 8, 5, null, ma.FaceTarget, MobjState.FattAtk10, 0, 0), // State.FattAtk9 + new MobjStateDef(385, Sprite.FATT, 6, 5, null, ma.FaceTarget, MobjState.FattRun1, 0, 0), // State.FattAtk10 + new MobjStateDef(386, Sprite.FATT, 9, 3, null, null, MobjState.FattPain2, 0, 0), // State.FattPain + new MobjStateDef(387, Sprite.FATT, 9, 3, null, ma.Pain, MobjState.FattRun1, 0, 0), // State.FattPain2 + new MobjStateDef(388, Sprite.FATT, 10, 6, null, null, MobjState.FattDie2, 0, 0), // State.FattDie1 + new MobjStateDef(389, Sprite.FATT, 11, 6, null, ma.Scream, MobjState.FattDie3, 0, 0), // State.FattDie2 + new MobjStateDef(390, Sprite.FATT, 12, 6, null, ma.Fall, MobjState.FattDie4, 0, 0), // State.FattDie3 + new MobjStateDef(391, Sprite.FATT, 13, 6, null, null, MobjState.FattDie5, 0, 0), // State.FattDie4 + new MobjStateDef(392, Sprite.FATT, 14, 6, null, null, MobjState.FattDie6, 0, 0), // State.FattDie5 + new MobjStateDef(393, Sprite.FATT, 15, 6, null, null, MobjState.FattDie7, 0, 0), // State.FattDie6 + new MobjStateDef(394, Sprite.FATT, 16, 6, null, null, MobjState.FattDie8, 0, 0), // State.FattDie7 + new MobjStateDef(395, Sprite.FATT, 17, 6, null, null, MobjState.FattDie9, 0, 0), // State.FattDie8 + new MobjStateDef(396, Sprite.FATT, 18, 6, null, null, MobjState.FattDie10, 0, 0), // State.FattDie9 + new MobjStateDef(397, Sprite.FATT, 19, -1, null, ma.BossDeath, MobjState.Null, 0, 0), // State.FattDie10 + new MobjStateDef(398, Sprite.FATT, 17, 5, null, null, MobjState.FattRaise2, 0, 0), // State.FattRaise1 + new MobjStateDef(399, Sprite.FATT, 16, 5, null, null, MobjState.FattRaise3, 0, 0), // State.FattRaise2 + new MobjStateDef(400, Sprite.FATT, 15, 5, null, null, MobjState.FattRaise4, 0, 0), // State.FattRaise3 + new MobjStateDef(401, Sprite.FATT, 14, 5, null, null, MobjState.FattRaise5, 0, 0), // State.FattRaise4 + new MobjStateDef(402, Sprite.FATT, 13, 5, null, null, MobjState.FattRaise6, 0, 0), // State.FattRaise5 + new MobjStateDef(403, Sprite.FATT, 12, 5, null, null, MobjState.FattRaise7, 0, 0), // State.FattRaise6 + new MobjStateDef(404, Sprite.FATT, 11, 5, null, null, MobjState.FattRaise8, 0, 0), // State.FattRaise7 + new MobjStateDef(405, Sprite.FATT, 10, 5, null, null, MobjState.FattRun1, 0, 0), // State.FattRaise8 + new MobjStateDef(406, Sprite.CPOS, 0, 10, null, ma.Look, MobjState.CposStnd2, 0, 0), // State.CposStnd + new MobjStateDef(407, Sprite.CPOS, 1, 10, null, ma.Look, MobjState.CposStnd, 0, 0), // State.CposStnd2 + new MobjStateDef(408, Sprite.CPOS, 0, 3, null, ma.Chase, MobjState.CposRun2, 0, 0), // State.CposRun1 + new MobjStateDef(409, Sprite.CPOS, 0, 3, null, ma.Chase, MobjState.CposRun3, 0, 0), // State.CposRun2 + new MobjStateDef(410, Sprite.CPOS, 1, 3, null, ma.Chase, MobjState.CposRun4, 0, 0), // State.CposRun3 + new MobjStateDef(411, Sprite.CPOS, 1, 3, null, ma.Chase, MobjState.CposRun5, 0, 0), // State.CposRun4 + new MobjStateDef(412, Sprite.CPOS, 2, 3, null, ma.Chase, MobjState.CposRun6, 0, 0), // State.CposRun5 + new MobjStateDef(413, Sprite.CPOS, 2, 3, null, ma.Chase, MobjState.CposRun7, 0, 0), // State.CposRun6 + new MobjStateDef(414, Sprite.CPOS, 3, 3, null, ma.Chase, MobjState.CposRun8, 0, 0), // State.CposRun7 + new MobjStateDef(415, Sprite.CPOS, 3, 3, null, ma.Chase, MobjState.CposRun1, 0, 0), // State.CposRun8 + new MobjStateDef(416, Sprite.CPOS, 4, 10, null, ma.FaceTarget, MobjState.CposAtk2, 0, 0), // State.CposAtk1 + new MobjStateDef(417, Sprite.CPOS, 32773, 4, null, ma.CPosAttack, MobjState.CposAtk3, 0, 0), // State.CposAtk2 + new MobjStateDef(418, Sprite.CPOS, 32772, 4, null, ma.CPosAttack, MobjState.CposAtk4, 0, 0), // State.CposAtk3 + new MobjStateDef(419, Sprite.CPOS, 5, 1, null, ma.CPosRefire, MobjState.CposAtk2, 0, 0), // State.CposAtk4 + new MobjStateDef(420, Sprite.CPOS, 6, 3, null, null, MobjState.CposPain2, 0, 0), // State.CposPain + new MobjStateDef(421, Sprite.CPOS, 6, 3, null, ma.Pain, MobjState.CposRun1, 0, 0), // State.CposPain2 + new MobjStateDef(422, Sprite.CPOS, 7, 5, null, null, MobjState.CposDie2, 0, 0), // State.CposDie1 + new MobjStateDef(423, Sprite.CPOS, 8, 5, null, ma.Scream, MobjState.CposDie3, 0, 0), // State.CposDie2 + new MobjStateDef(424, Sprite.CPOS, 9, 5, null, ma.Fall, MobjState.CposDie4, 0, 0), // State.CposDie3 + new MobjStateDef(425, Sprite.CPOS, 10, 5, null, null, MobjState.CposDie5, 0, 0), // State.CposDie4 + new MobjStateDef(426, Sprite.CPOS, 11, 5, null, null, MobjState.CposDie6, 0, 0), // State.CposDie5 + new MobjStateDef(427, Sprite.CPOS, 12, 5, null, null, MobjState.CposDie7, 0, 0), // State.CposDie6 + new MobjStateDef(428, Sprite.CPOS, 13, -1, null, null, MobjState.Null, 0, 0), // State.CposDie7 + new MobjStateDef(429, Sprite.CPOS, 14, 5, null, null, MobjState.CposXdie2, 0, 0), // State.CposXdie1 + new MobjStateDef(430, Sprite.CPOS, 15, 5, null, ma.XScream, MobjState.CposXdie3, 0, 0), // State.CposXdie2 + new MobjStateDef(431, Sprite.CPOS, 16, 5, null, ma.Fall, MobjState.CposXdie4, 0, 0), // State.CposXdie3 + new MobjStateDef(432, Sprite.CPOS, 17, 5, null, null, MobjState.CposXdie5, 0, 0), // State.CposXdie4 + new MobjStateDef(433, Sprite.CPOS, 18, 5, null, null, MobjState.CposXdie6, 0, 0), // State.CposXdie5 + new MobjStateDef(434, Sprite.CPOS, 19, -1, null, null, MobjState.Null, 0, 0), // State.CposXdie6 + new MobjStateDef(435, Sprite.CPOS, 13, 5, null, null, MobjState.CposRaise2, 0, 0), // State.CposRaise1 + new MobjStateDef(436, Sprite.CPOS, 12, 5, null, null, MobjState.CposRaise3, 0, 0), // State.CposRaise2 + new MobjStateDef(437, Sprite.CPOS, 11, 5, null, null, MobjState.CposRaise4, 0, 0), // State.CposRaise3 + new MobjStateDef(438, Sprite.CPOS, 10, 5, null, null, MobjState.CposRaise5, 0, 0), // State.CposRaise4 + new MobjStateDef(439, Sprite.CPOS, 9, 5, null, null, MobjState.CposRaise6, 0, 0), // State.CposRaise5 + new MobjStateDef(440, Sprite.CPOS, 8, 5, null, null, MobjState.CposRaise7, 0, 0), // State.CposRaise6 + new MobjStateDef(441, Sprite.CPOS, 7, 5, null, null, MobjState.CposRun1, 0, 0), // State.CposRaise7 + new MobjStateDef(442, Sprite.TROO, 0, 10, null, ma.Look, MobjState.TrooStnd2, 0, 0), // State.TrooStnd + new MobjStateDef(443, Sprite.TROO, 1, 10, null, ma.Look, MobjState.TrooStnd, 0, 0), // State.TrooStnd2 + new MobjStateDef(444, Sprite.TROO, 0, 3, null, ma.Chase, MobjState.TrooRun2, 0, 0), // State.TrooRun1 + new MobjStateDef(445, Sprite.TROO, 0, 3, null, ma.Chase, MobjState.TrooRun3, 0, 0), // State.TrooRun2 + new MobjStateDef(446, Sprite.TROO, 1, 3, null, ma.Chase, MobjState.TrooRun4, 0, 0), // State.TrooRun3 + new MobjStateDef(447, Sprite.TROO, 1, 3, null, ma.Chase, MobjState.TrooRun5, 0, 0), // State.TrooRun4 + new MobjStateDef(448, Sprite.TROO, 2, 3, null, ma.Chase, MobjState.TrooRun6, 0, 0), // State.TrooRun5 + new MobjStateDef(449, Sprite.TROO, 2, 3, null, ma.Chase, MobjState.TrooRun7, 0, 0), // State.TrooRun6 + new MobjStateDef(450, Sprite.TROO, 3, 3, null, ma.Chase, MobjState.TrooRun8, 0, 0), // State.TrooRun7 + new MobjStateDef(451, Sprite.TROO, 3, 3, null, ma.Chase, MobjState.TrooRun1, 0, 0), // State.TrooRun8 + new MobjStateDef(452, Sprite.TROO, 4, 8, null, ma.FaceTarget, MobjState.TrooAtk2, 0, 0), // State.TrooAtk1 + new MobjStateDef(453, Sprite.TROO, 5, 8, null, ma.FaceTarget, MobjState.TrooAtk3, 0, 0), // State.TrooAtk2 + new MobjStateDef(454, Sprite.TROO, 6, 6, null, ma.TroopAttack, MobjState.TrooRun1, 0, 0), // State.TrooAtk3 + new MobjStateDef(455, Sprite.TROO, 7, 2, null, null, MobjState.TrooPain2, 0, 0), // State.TrooPain + new MobjStateDef(456, Sprite.TROO, 7, 2, null, ma.Pain, MobjState.TrooRun1, 0, 0), // State.TrooPain2 + new MobjStateDef(457, Sprite.TROO, 8, 8, null, null, MobjState.TrooDie2, 0, 0), // State.TrooDie1 + new MobjStateDef(458, Sprite.TROO, 9, 8, null, ma.Scream, MobjState.TrooDie3, 0, 0), // State.TrooDie2 + new MobjStateDef(459, Sprite.TROO, 10, 6, null, null, MobjState.TrooDie4, 0, 0), // State.TrooDie3 + new MobjStateDef(460, Sprite.TROO, 11, 6, null, ma.Fall, MobjState.TrooDie5, 0, 0), // State.TrooDie4 + new MobjStateDef(461, Sprite.TROO, 12, -1, null, null, MobjState.Null, 0, 0), // State.TrooDie5 + new MobjStateDef(462, Sprite.TROO, 13, 5, null, null, MobjState.TrooXdie2, 0, 0), // State.TrooXdie1 + new MobjStateDef(463, Sprite.TROO, 14, 5, null, ma.XScream, MobjState.TrooXdie3, 0, 0), // State.TrooXdie2 + new MobjStateDef(464, Sprite.TROO, 15, 5, null, null, MobjState.TrooXdie4, 0, 0), // State.TrooXdie3 + new MobjStateDef(465, Sprite.TROO, 16, 5, null, ma.Fall, MobjState.TrooXdie5, 0, 0), // State.TrooXdie4 + new MobjStateDef(466, Sprite.TROO, 17, 5, null, null, MobjState.TrooXdie6, 0, 0), // State.TrooXdie5 + new MobjStateDef(467, Sprite.TROO, 18, 5, null, null, MobjState.TrooXdie7, 0, 0), // State.TrooXdie6 + new MobjStateDef(468, Sprite.TROO, 19, 5, null, null, MobjState.TrooXdie8, 0, 0), // State.TrooXdie7 + new MobjStateDef(469, Sprite.TROO, 20, -1, null, null, MobjState.Null, 0, 0), // State.TrooXdie8 + new MobjStateDef(470, Sprite.TROO, 12, 8, null, null, MobjState.TrooRaise2, 0, 0), // State.TrooRaise1 + new MobjStateDef(471, Sprite.TROO, 11, 8, null, null, MobjState.TrooRaise3, 0, 0), // State.TrooRaise2 + new MobjStateDef(472, Sprite.TROO, 10, 6, null, null, MobjState.TrooRaise4, 0, 0), // State.TrooRaise3 + new MobjStateDef(473, Sprite.TROO, 9, 6, null, null, MobjState.TrooRaise5, 0, 0), // State.TrooRaise4 + new MobjStateDef(474, Sprite.TROO, 8, 6, null, null, MobjState.TrooRun1, 0, 0), // State.TrooRaise5 + new MobjStateDef(475, Sprite.SARG, 0, 10, null, ma.Look, MobjState.SargStnd2, 0, 0), // State.SargStnd + new MobjStateDef(476, Sprite.SARG, 1, 10, null, ma.Look, MobjState.SargStnd, 0, 0), // State.SargStnd2 + new MobjStateDef(477, Sprite.SARG, 0, 2, null, ma.Chase, MobjState.SargRun2, 0, 0), // State.SargRun1 + new MobjStateDef(478, Sprite.SARG, 0, 2, null, ma.Chase, MobjState.SargRun3, 0, 0), // State.SargRun2 + new MobjStateDef(479, Sprite.SARG, 1, 2, null, ma.Chase, MobjState.SargRun4, 0, 0), // State.SargRun3 + new MobjStateDef(480, Sprite.SARG, 1, 2, null, ma.Chase, MobjState.SargRun5, 0, 0), // State.SargRun4 + new MobjStateDef(481, Sprite.SARG, 2, 2, null, ma.Chase, MobjState.SargRun6, 0, 0), // State.SargRun5 + new MobjStateDef(482, Sprite.SARG, 2, 2, null, ma.Chase, MobjState.SargRun7, 0, 0), // State.SargRun6 + new MobjStateDef(483, Sprite.SARG, 3, 2, null, ma.Chase, MobjState.SargRun8, 0, 0), // State.SargRun7 + new MobjStateDef(484, Sprite.SARG, 3, 2, null, ma.Chase, MobjState.SargRun1, 0, 0), // State.SargRun8 + new MobjStateDef(485, Sprite.SARG, 4, 8, null, ma.FaceTarget, MobjState.SargAtk2, 0, 0), // State.SargAtk1 + new MobjStateDef(486, Sprite.SARG, 5, 8, null, ma.FaceTarget, MobjState.SargAtk3, 0, 0), // State.SargAtk2 + new MobjStateDef(487, Sprite.SARG, 6, 8, null, ma.SargAttack, MobjState.SargRun1, 0, 0), // State.SargAtk3 + new MobjStateDef(488, Sprite.SARG, 7, 2, null, null, MobjState.SargPain2, 0, 0), // State.SargPain + new MobjStateDef(489, Sprite.SARG, 7, 2, null, ma.Pain, MobjState.SargRun1, 0, 0), // State.SargPain2 + new MobjStateDef(490, Sprite.SARG, 8, 8, null, null, MobjState.SargDie2, 0, 0), // State.SargDie1 + new MobjStateDef(491, Sprite.SARG, 9, 8, null, ma.Scream, MobjState.SargDie3, 0, 0), // State.SargDie2 + new MobjStateDef(492, Sprite.SARG, 10, 4, null, null, MobjState.SargDie4, 0, 0), // State.SargDie3 + new MobjStateDef(493, Sprite.SARG, 11, 4, null, ma.Fall, MobjState.SargDie5, 0, 0), // State.SargDie4 + new MobjStateDef(494, Sprite.SARG, 12, 4, null, null, MobjState.SargDie6, 0, 0), // State.SargDie5 + new MobjStateDef(495, Sprite.SARG, 13, -1, null, null, MobjState.Null, 0, 0), // State.SargDie6 + new MobjStateDef(496, Sprite.SARG, 13, 5, null, null, MobjState.SargRaise2, 0, 0), // State.SargRaise1 + new MobjStateDef(497, Sprite.SARG, 12, 5, null, null, MobjState.SargRaise3, 0, 0), // State.SargRaise2 + new MobjStateDef(498, Sprite.SARG, 11, 5, null, null, MobjState.SargRaise4, 0, 0), // State.SargRaise3 + new MobjStateDef(499, Sprite.SARG, 10, 5, null, null, MobjState.SargRaise5, 0, 0), // State.SargRaise4 + new MobjStateDef(500, Sprite.SARG, 9, 5, null, null, MobjState.SargRaise6, 0, 0), // State.SargRaise5 + new MobjStateDef(501, Sprite.SARG, 8, 5, null, null, MobjState.SargRun1, 0, 0), // State.SargRaise6 + new MobjStateDef(502, Sprite.HEAD, 0, 10, null, ma.Look, MobjState.HeadStnd, 0, 0), // State.HeadStnd + new MobjStateDef(503, Sprite.HEAD, 0, 3, null, ma.Chase, MobjState.HeadRun1, 0, 0), // State.HeadRun1 + new MobjStateDef(504, Sprite.HEAD, 1, 5, null, ma.FaceTarget, MobjState.HeadAtk2, 0, 0), // State.HeadAtk1 + new MobjStateDef(505, Sprite.HEAD, 2, 5, null, ma.FaceTarget, MobjState.HeadAtk3, 0, 0), // State.HeadAtk2 + new MobjStateDef(506, Sprite.HEAD, 32771, 5, null, ma.HeadAttack, MobjState.HeadRun1, 0, 0), // State.HeadAtk3 + new MobjStateDef(507, Sprite.HEAD, 4, 3, null, null, MobjState.HeadPain2, 0, 0), // State.HeadPain + new MobjStateDef(508, Sprite.HEAD, 4, 3, null, ma.Pain, MobjState.HeadPain3, 0, 0), // State.HeadPain2 + new MobjStateDef(509, Sprite.HEAD, 5, 6, null, null, MobjState.HeadRun1, 0, 0), // State.HeadPain3 + new MobjStateDef(510, Sprite.HEAD, 6, 8, null, null, MobjState.HeadDie2, 0, 0), // State.HeadDie1 + new MobjStateDef(511, Sprite.HEAD, 7, 8, null, ma.Scream, MobjState.HeadDie3, 0, 0), // State.HeadDie2 + new MobjStateDef(512, Sprite.HEAD, 8, 8, null, null, MobjState.HeadDie4, 0, 0), // State.HeadDie3 + new MobjStateDef(513, Sprite.HEAD, 9, 8, null, null, MobjState.HeadDie5, 0, 0), // State.HeadDie4 + new MobjStateDef(514, Sprite.HEAD, 10, 8, null, ma.Fall, MobjState.HeadDie6, 0, 0), // State.HeadDie5 + new MobjStateDef(515, Sprite.HEAD, 11, -1, null, null, MobjState.Null, 0, 0), // State.HeadDie6 + new MobjStateDef(516, Sprite.HEAD, 11, 8, null, null, MobjState.HeadRaise2, 0, 0), // State.HeadRaise1 + new MobjStateDef(517, Sprite.HEAD, 10, 8, null, null, MobjState.HeadRaise3, 0, 0), // State.HeadRaise2 + new MobjStateDef(518, Sprite.HEAD, 9, 8, null, null, MobjState.HeadRaise4, 0, 0), // State.HeadRaise3 + new MobjStateDef(519, Sprite.HEAD, 8, 8, null, null, MobjState.HeadRaise5, 0, 0), // State.HeadRaise4 + new MobjStateDef(520, Sprite.HEAD, 7, 8, null, null, MobjState.HeadRaise6, 0, 0), // State.HeadRaise5 + new MobjStateDef(521, Sprite.HEAD, 6, 8, null, null, MobjState.HeadRun1, 0, 0), // State.HeadRaise6 + new MobjStateDef(522, Sprite.BAL7, 32768, 4, null, null, MobjState.Brball2, 0, 0), // State.Brball1 + new MobjStateDef(523, Sprite.BAL7, 32769, 4, null, null, MobjState.Brball1, 0, 0), // State.Brball2 + new MobjStateDef(524, Sprite.BAL7, 32770, 6, null, null, MobjState.Brballx2, 0, 0), // State.Brballx1 + new MobjStateDef(525, Sprite.BAL7, 32771, 6, null, null, MobjState.Brballx3, 0, 0), // State.Brballx2 + new MobjStateDef(526, Sprite.BAL7, 32772, 6, null, null, MobjState.Null, 0, 0), // State.Brballx3 + new MobjStateDef(527, Sprite.BOSS, 0, 10, null, ma.Look, MobjState.BossStnd2, 0, 0), // State.BossStnd + new MobjStateDef(528, Sprite.BOSS, 1, 10, null, ma.Look, MobjState.BossStnd, 0, 0), // State.BossStnd2 + new MobjStateDef(529, Sprite.BOSS, 0, 3, null, ma.Chase, MobjState.BossRun2, 0, 0), // State.BossRun1 + new MobjStateDef(530, Sprite.BOSS, 0, 3, null, ma.Chase, MobjState.BossRun3, 0, 0), // State.BossRun2 + new MobjStateDef(531, Sprite.BOSS, 1, 3, null, ma.Chase, MobjState.BossRun4, 0, 0), // State.BossRun3 + new MobjStateDef(532, Sprite.BOSS, 1, 3, null, ma.Chase, MobjState.BossRun5, 0, 0), // State.BossRun4 + new MobjStateDef(533, Sprite.BOSS, 2, 3, null, ma.Chase, MobjState.BossRun6, 0, 0), // State.BossRun5 + new MobjStateDef(534, Sprite.BOSS, 2, 3, null, ma.Chase, MobjState.BossRun7, 0, 0), // State.BossRun6 + new MobjStateDef(535, Sprite.BOSS, 3, 3, null, ma.Chase, MobjState.BossRun8, 0, 0), // State.BossRun7 + new MobjStateDef(536, Sprite.BOSS, 3, 3, null, ma.Chase, MobjState.BossRun1, 0, 0), // State.BossRun8 + new MobjStateDef(537, Sprite.BOSS, 4, 8, null, ma.FaceTarget, MobjState.BossAtk2, 0, 0), // State.BossAtk1 + new MobjStateDef(538, Sprite.BOSS, 5, 8, null, ma.FaceTarget, MobjState.BossAtk3, 0, 0), // State.BossAtk2 + new MobjStateDef(539, Sprite.BOSS, 6, 8, null, ma.BruisAttack, MobjState.BossRun1, 0, 0), // State.BossAtk3 + new MobjStateDef(540, Sprite.BOSS, 7, 2, null, null, MobjState.BossPain2, 0, 0), // State.BossPain + new MobjStateDef(541, Sprite.BOSS, 7, 2, null, ma.Pain, MobjState.BossRun1, 0, 0), // State.BossPain2 + new MobjStateDef(542, Sprite.BOSS, 8, 8, null, null, MobjState.BossDie2, 0, 0), // State.BossDie1 + new MobjStateDef(543, Sprite.BOSS, 9, 8, null, ma.Scream, MobjState.BossDie3, 0, 0), // State.BossDie2 + new MobjStateDef(544, Sprite.BOSS, 10, 8, null, null, MobjState.BossDie4, 0, 0), // State.BossDie3 + new MobjStateDef(545, Sprite.BOSS, 11, 8, null, ma.Fall, MobjState.BossDie5, 0, 0), // State.BossDie4 + new MobjStateDef(546, Sprite.BOSS, 12, 8, null, null, MobjState.BossDie6, 0, 0), // State.BossDie5 + new MobjStateDef(547, Sprite.BOSS, 13, 8, null, null, MobjState.BossDie7, 0, 0), // State.BossDie6 + new MobjStateDef(548, Sprite.BOSS, 14, -1, null, ma.BossDeath, MobjState.Null, 0, 0), // State.BossDie7 + new MobjStateDef(549, Sprite.BOSS, 14, 8, null, null, MobjState.BossRaise2, 0, 0), // State.BossRaise1 + new MobjStateDef(550, Sprite.BOSS, 13, 8, null, null, MobjState.BossRaise3, 0, 0), // State.BossRaise2 + new MobjStateDef(551, Sprite.BOSS, 12, 8, null, null, MobjState.BossRaise4, 0, 0), // State.BossRaise3 + new MobjStateDef(552, Sprite.BOSS, 11, 8, null, null, MobjState.BossRaise5, 0, 0), // State.BossRaise4 + new MobjStateDef(553, Sprite.BOSS, 10, 8, null, null, MobjState.BossRaise6, 0, 0), // State.BossRaise5 + new MobjStateDef(554, Sprite.BOSS, 9, 8, null, null, MobjState.BossRaise7, 0, 0), // State.BossRaise6 + new MobjStateDef(555, Sprite.BOSS, 8, 8, null, null, MobjState.BossRun1, 0, 0), // State.BossRaise7 + new MobjStateDef(556, Sprite.BOS2, 0, 10, null, ma.Look, MobjState.Bos2Stnd2, 0, 0), // State.Bos2Stnd + new MobjStateDef(557, Sprite.BOS2, 1, 10, null, ma.Look, MobjState.Bos2Stnd, 0, 0), // State.Bos2Stnd2 + new MobjStateDef(558, Sprite.BOS2, 0, 3, null, ma.Chase, MobjState.Bos2Run2, 0, 0), // State.Bos2Run1 + new MobjStateDef(559, Sprite.BOS2, 0, 3, null, ma.Chase, MobjState.Bos2Run3, 0, 0), // State.Bos2Run2 + new MobjStateDef(560, Sprite.BOS2, 1, 3, null, ma.Chase, MobjState.Bos2Run4, 0, 0), // State.Bos2Run3 + new MobjStateDef(561, Sprite.BOS2, 1, 3, null, ma.Chase, MobjState.Bos2Run5, 0, 0), // State.Bos2Run4 + new MobjStateDef(562, Sprite.BOS2, 2, 3, null, ma.Chase, MobjState.Bos2Run6, 0, 0), // State.Bos2Run5 + new MobjStateDef(563, Sprite.BOS2, 2, 3, null, ma.Chase, MobjState.Bos2Run7, 0, 0), // State.Bos2Run6 + new MobjStateDef(564, Sprite.BOS2, 3, 3, null, ma.Chase, MobjState.Bos2Run8, 0, 0), // State.Bos2Run7 + new MobjStateDef(565, Sprite.BOS2, 3, 3, null, ma.Chase, MobjState.Bos2Run1, 0, 0), // State.Bos2Run8 + new MobjStateDef(566, Sprite.BOS2, 4, 8, null, ma.FaceTarget, MobjState.Bos2Atk2, 0, 0), // State.Bos2Atk1 + new MobjStateDef(567, Sprite.BOS2, 5, 8, null, ma.FaceTarget, MobjState.Bos2Atk3, 0, 0), // State.Bos2Atk2 + new MobjStateDef(568, Sprite.BOS2, 6, 8, null, ma.BruisAttack, MobjState.Bos2Run1, 0, 0), // State.Bos2Atk3 + new MobjStateDef(569, Sprite.BOS2, 7, 2, null, null, MobjState.Bos2Pain2, 0, 0), // State.Bos2Pain + new MobjStateDef(570, Sprite.BOS2, 7, 2, null, ma.Pain, MobjState.Bos2Run1, 0, 0), // State.Bos2Pain2 + new MobjStateDef(571, Sprite.BOS2, 8, 8, null, null, MobjState.Bos2Die2, 0, 0), // State.Bos2Die1 + new MobjStateDef(572, Sprite.BOS2, 9, 8, null, ma.Scream, MobjState.Bos2Die3, 0, 0), // State.Bos2Die2 + new MobjStateDef(573, Sprite.BOS2, 10, 8, null, null, MobjState.Bos2Die4, 0, 0), // State.Bos2Die3 + new MobjStateDef(574, Sprite.BOS2, 11, 8, null, ma.Fall, MobjState.Bos2Die5, 0, 0), // State.Bos2Die4 + new MobjStateDef(575, Sprite.BOS2, 12, 8, null, null, MobjState.Bos2Die6, 0, 0), // State.Bos2Die5 + new MobjStateDef(576, Sprite.BOS2, 13, 8, null, null, MobjState.Bos2Die7, 0, 0), // State.Bos2Die6 + new MobjStateDef(577, Sprite.BOS2, 14, -1, null, null, MobjState.Null, 0, 0), // State.Bos2Die7 + new MobjStateDef(578, Sprite.BOS2, 14, 8, null, null, MobjState.Bos2Raise2, 0, 0), // State.Bos2Raise1 + new MobjStateDef(579, Sprite.BOS2, 13, 8, null, null, MobjState.Bos2Raise3, 0, 0), // State.Bos2Raise2 + new MobjStateDef(580, Sprite.BOS2, 12, 8, null, null, MobjState.Bos2Raise4, 0, 0), // State.Bos2Raise3 + new MobjStateDef(581, Sprite.BOS2, 11, 8, null, null, MobjState.Bos2Raise5, 0, 0), // State.Bos2Raise4 + new MobjStateDef(582, Sprite.BOS2, 10, 8, null, null, MobjState.Bos2Raise6, 0, 0), // State.Bos2Raise5 + new MobjStateDef(583, Sprite.BOS2, 9, 8, null, null, MobjState.Bos2Raise7, 0, 0), // State.Bos2Raise6 + new MobjStateDef(584, Sprite.BOS2, 8, 8, null, null, MobjState.Bos2Run1, 0, 0), // State.Bos2Raise7 + new MobjStateDef(585, Sprite.SKUL, 32768, 10, null, ma.Look, MobjState.SkullStnd2, 0, 0), // State.SkullStnd + new MobjStateDef(586, Sprite.SKUL, 32769, 10, null, ma.Look, MobjState.SkullStnd, 0, 0), // State.SkullStnd2 + new MobjStateDef(587, Sprite.SKUL, 32768, 6, null, ma.Chase, MobjState.SkullRun2, 0, 0), // State.SkullRun1 + new MobjStateDef(588, Sprite.SKUL, 32769, 6, null, ma.Chase, MobjState.SkullRun1, 0, 0), // State.SkullRun2 + new MobjStateDef(589, Sprite.SKUL, 32770, 10, null, ma.FaceTarget, MobjState.SkullAtk2, 0, 0), // State.SkullAtk1 + new MobjStateDef(590, Sprite.SKUL, 32771, 4, null, ma.SkullAttack, MobjState.SkullAtk3, 0, 0), // State.SkullAtk2 + new MobjStateDef(591, Sprite.SKUL, 32770, 4, null, null, MobjState.SkullAtk4, 0, 0), // State.SkullAtk3 + new MobjStateDef(592, Sprite.SKUL, 32771, 4, null, null, MobjState.SkullAtk3, 0, 0), // State.SkullAtk4 + new MobjStateDef(593, Sprite.SKUL, 32772, 3, null, null, MobjState.SkullPain2, 0, 0), // State.SkullPain + new MobjStateDef(594, Sprite.SKUL, 32772, 3, null, ma.Pain, MobjState.SkullRun1, 0, 0), // State.SkullPain2 + new MobjStateDef(595, Sprite.SKUL, 32773, 6, null, null, MobjState.SkullDie2, 0, 0), // State.SkullDie1 + new MobjStateDef(596, Sprite.SKUL, 32774, 6, null, ma.Scream, MobjState.SkullDie3, 0, 0), // State.SkullDie2 + new MobjStateDef(597, Sprite.SKUL, 32775, 6, null, null, MobjState.SkullDie4, 0, 0), // State.SkullDie3 + new MobjStateDef(598, Sprite.SKUL, 32776, 6, null, ma.Fall, MobjState.SkullDie5, 0, 0), // State.SkullDie4 + new MobjStateDef(599, Sprite.SKUL, 9, 6, null, null, MobjState.SkullDie6, 0, 0), // State.SkullDie5 + new MobjStateDef(600, Sprite.SKUL, 10, 6, null, null, MobjState.Null, 0, 0), // State.SkullDie6 + new MobjStateDef(601, Sprite.SPID, 0, 10, null, ma.Look, MobjState.SpidStnd2, 0, 0), // State.SpidStnd + new MobjStateDef(602, Sprite.SPID, 1, 10, null, ma.Look, MobjState.SpidStnd, 0, 0), // State.SpidStnd2 + new MobjStateDef(603, Sprite.SPID, 0, 3, null, ma.Metal, MobjState.SpidRun2, 0, 0), // State.SpidRun1 + new MobjStateDef(604, Sprite.SPID, 0, 3, null, ma.Chase, MobjState.SpidRun3, 0, 0), // State.SpidRun2 + new MobjStateDef(605, Sprite.SPID, 1, 3, null, ma.Chase, MobjState.SpidRun4, 0, 0), // State.SpidRun3 + new MobjStateDef(606, Sprite.SPID, 1, 3, null, ma.Chase, MobjState.SpidRun5, 0, 0), // State.SpidRun4 + new MobjStateDef(607, Sprite.SPID, 2, 3, null, ma.Metal, MobjState.SpidRun6, 0, 0), // State.SpidRun5 + new MobjStateDef(608, Sprite.SPID, 2, 3, null, ma.Chase, MobjState.SpidRun7, 0, 0), // State.SpidRun6 + new MobjStateDef(609, Sprite.SPID, 3, 3, null, ma.Chase, MobjState.SpidRun8, 0, 0), // State.SpidRun7 + new MobjStateDef(610, Sprite.SPID, 3, 3, null, ma.Chase, MobjState.SpidRun9, 0, 0), // State.SpidRun8 + new MobjStateDef(611, Sprite.SPID, 4, 3, null, ma.Metal, MobjState.SpidRun10, 0, 0), // State.SpidRun9 + new MobjStateDef(612, Sprite.SPID, 4, 3, null, ma.Chase, MobjState.SpidRun11, 0, 0), // State.SpidRun10 + new MobjStateDef(613, Sprite.SPID, 5, 3, null, ma.Chase, MobjState.SpidRun12, 0, 0), // State.SpidRun11 + new MobjStateDef(614, Sprite.SPID, 5, 3, null, ma.Chase, MobjState.SpidRun1, 0, 0), // State.SpidRun12 + new MobjStateDef(615, Sprite.SPID, 32768, 20, null, ma.FaceTarget, MobjState.SpidAtk2, 0, 0), // State.SpidAtk1 + new MobjStateDef(616, Sprite.SPID, 32774, 4, null, ma.SPosAttack, MobjState.SpidAtk3, 0, 0), // State.SpidAtk2 + new MobjStateDef(617, Sprite.SPID, 32775, 4, null, ma.SPosAttack, MobjState.SpidAtk4, 0, 0), // State.SpidAtk3 + new MobjStateDef(618, Sprite.SPID, 32775, 1, null, ma.SpidRefire, MobjState.SpidAtk2, 0, 0), // State.SpidAtk4 + new MobjStateDef(619, Sprite.SPID, 8, 3, null, null, MobjState.SpidPain2, 0, 0), // State.SpidPain + new MobjStateDef(620, Sprite.SPID, 8, 3, null, ma.Pain, MobjState.SpidRun1, 0, 0), // State.SpidPain2 + new MobjStateDef(621, Sprite.SPID, 9, 20, null, ma.Scream, MobjState.SpidDie2, 0, 0), // State.SpidDie1 + new MobjStateDef(622, Sprite.SPID, 10, 10, null, ma.Fall, MobjState.SpidDie3, 0, 0), // State.SpidDie2 + new MobjStateDef(623, Sprite.SPID, 11, 10, null, null, MobjState.SpidDie4, 0, 0), // State.SpidDie3 + new MobjStateDef(624, Sprite.SPID, 12, 10, null, null, MobjState.SpidDie5, 0, 0), // State.SpidDie4 + new MobjStateDef(625, Sprite.SPID, 13, 10, null, null, MobjState.SpidDie6, 0, 0), // State.SpidDie5 + new MobjStateDef(626, Sprite.SPID, 14, 10, null, null, MobjState.SpidDie7, 0, 0), // State.SpidDie6 + new MobjStateDef(627, Sprite.SPID, 15, 10, null, null, MobjState.SpidDie8, 0, 0), // State.SpidDie7 + new MobjStateDef(628, Sprite.SPID, 16, 10, null, null, MobjState.SpidDie9, 0, 0), // State.SpidDie8 + new MobjStateDef(629, Sprite.SPID, 17, 10, null, null, MobjState.SpidDie10, 0, 0), // State.SpidDie9 + new MobjStateDef(630, Sprite.SPID, 18, 30, null, null, MobjState.SpidDie11, 0, 0), // State.SpidDie10 + new MobjStateDef(631, Sprite.SPID, 18, -1, null, ma.BossDeath, MobjState.Null, 0, 0), // State.SpidDie11 + new MobjStateDef(632, Sprite.BSPI, 0, 10, null, ma.Look, MobjState.BspiStnd2, 0, 0), // State.BspiStnd + new MobjStateDef(633, Sprite.BSPI, 1, 10, null, ma.Look, MobjState.BspiStnd, 0, 0), // State.BspiStnd2 + new MobjStateDef(634, Sprite.BSPI, 0, 20, null, null, MobjState.BspiRun1, 0, 0), // State.BspiSight + new MobjStateDef(635, Sprite.BSPI, 0, 3, null, ma.BabyMetal, MobjState.BspiRun2, 0, 0), // State.BspiRun1 + new MobjStateDef(636, Sprite.BSPI, 0, 3, null, ma.Chase, MobjState.BspiRun3, 0, 0), // State.BspiRun2 + new MobjStateDef(637, Sprite.BSPI, 1, 3, null, ma.Chase, MobjState.BspiRun4, 0, 0), // State.BspiRun3 + new MobjStateDef(638, Sprite.BSPI, 1, 3, null, ma.Chase, MobjState.BspiRun5, 0, 0), // State.BspiRun4 + new MobjStateDef(639, Sprite.BSPI, 2, 3, null, ma.Chase, MobjState.BspiRun6, 0, 0), // State.BspiRun5 + new MobjStateDef(640, Sprite.BSPI, 2, 3, null, ma.Chase, MobjState.BspiRun7, 0, 0), // State.BspiRun6 + new MobjStateDef(641, Sprite.BSPI, 3, 3, null, ma.BabyMetal, MobjState.BspiRun8, 0, 0), // State.BspiRun7 + new MobjStateDef(642, Sprite.BSPI, 3, 3, null, ma.Chase, MobjState.BspiRun9, 0, 0), // State.BspiRun8 + new MobjStateDef(643, Sprite.BSPI, 4, 3, null, ma.Chase, MobjState.BspiRun10, 0, 0), // State.BspiRun9 + new MobjStateDef(644, Sprite.BSPI, 4, 3, null, ma.Chase, MobjState.BspiRun11, 0, 0), // State.BspiRun10 + new MobjStateDef(645, Sprite.BSPI, 5, 3, null, ma.Chase, MobjState.BspiRun12, 0, 0), // State.BspiRun11 + new MobjStateDef(646, Sprite.BSPI, 5, 3, null, ma.Chase, MobjState.BspiRun1, 0, 0), // State.BspiRun12 + new MobjStateDef(647, Sprite.BSPI, 32768, 20, null, ma.FaceTarget, MobjState.BspiAtk2, 0, 0), // State.BspiAtk1 + new MobjStateDef(648, Sprite.BSPI, 32774, 4, null, ma.BspiAttack, MobjState.BspiAtk3, 0, 0), // State.BspiAtk2 + new MobjStateDef(649, Sprite.BSPI, 32775, 4, null, null, MobjState.BspiAtk4, 0, 0), // State.BspiAtk3 + new MobjStateDef(650, Sprite.BSPI, 32775, 1, null, ma.SpidRefire, MobjState.BspiAtk2, 0, 0), // State.BspiAtk4 + new MobjStateDef(651, Sprite.BSPI, 8, 3, null, null, MobjState.BspiPain2, 0, 0), // State.BspiPain + new MobjStateDef(652, Sprite.BSPI, 8, 3, null, ma.Pain, MobjState.BspiRun1, 0, 0), // State.BspiPain2 + new MobjStateDef(653, Sprite.BSPI, 9, 20, null, ma.Scream, MobjState.BspiDie2, 0, 0), // State.BspiDie1 + new MobjStateDef(654, Sprite.BSPI, 10, 7, null, ma.Fall, MobjState.BspiDie3, 0, 0), // State.BspiDie2 + new MobjStateDef(655, Sprite.BSPI, 11, 7, null, null, MobjState.BspiDie4, 0, 0), // State.BspiDie3 + new MobjStateDef(656, Sprite.BSPI, 12, 7, null, null, MobjState.BspiDie5, 0, 0), // State.BspiDie4 + new MobjStateDef(657, Sprite.BSPI, 13, 7, null, null, MobjState.BspiDie6, 0, 0), // State.BspiDie5 + new MobjStateDef(658, Sprite.BSPI, 14, 7, null, null, MobjState.BspiDie7, 0, 0), // State.BspiDie6 + new MobjStateDef(659, Sprite.BSPI, 15, -1, null, ma.BossDeath, MobjState.Null, 0, 0), // State.BspiDie7 + new MobjStateDef(660, Sprite.BSPI, 15, 5, null, null, MobjState.BspiRaise2, 0, 0), // State.BspiRaise1 + new MobjStateDef(661, Sprite.BSPI, 14, 5, null, null, MobjState.BspiRaise3, 0, 0), // State.BspiRaise2 + new MobjStateDef(662, Sprite.BSPI, 13, 5, null, null, MobjState.BspiRaise4, 0, 0), // State.BspiRaise3 + new MobjStateDef(663, Sprite.BSPI, 12, 5, null, null, MobjState.BspiRaise5, 0, 0), // State.BspiRaise4 + new MobjStateDef(664, Sprite.BSPI, 11, 5, null, null, MobjState.BspiRaise6, 0, 0), // State.BspiRaise5 + new MobjStateDef(665, Sprite.BSPI, 10, 5, null, null, MobjState.BspiRaise7, 0, 0), // State.BspiRaise6 + new MobjStateDef(666, Sprite.BSPI, 9, 5, null, null, MobjState.BspiRun1, 0, 0), // State.BspiRaise7 + new MobjStateDef(667, Sprite.APLS, 32768, 5, null, null, MobjState.ArachPlaz2, 0, 0), // State.ArachPlaz + new MobjStateDef(668, Sprite.APLS, 32769, 5, null, null, MobjState.ArachPlaz, 0, 0), // State.ArachPlaz2 + new MobjStateDef(669, Sprite.APBX, 32768, 5, null, null, MobjState.ArachPlex2, 0, 0), // State.ArachPlex + new MobjStateDef(670, Sprite.APBX, 32769, 5, null, null, MobjState.ArachPlex3, 0, 0), // State.ArachPlex2 + new MobjStateDef(671, Sprite.APBX, 32770, 5, null, null, MobjState.ArachPlex4, 0, 0), // State.ArachPlex3 + new MobjStateDef(672, Sprite.APBX, 32771, 5, null, null, MobjState.ArachPlex5, 0, 0), // State.ArachPlex4 + new MobjStateDef(673, Sprite.APBX, 32772, 5, null, null, MobjState.Null, 0, 0), // State.ArachPlex5 + new MobjStateDef(674, Sprite.CYBR, 0, 10, null, ma.Look, MobjState.CyberStnd2, 0, 0), // State.CyberStnd + new MobjStateDef(675, Sprite.CYBR, 1, 10, null, ma.Look, MobjState.CyberStnd, 0, 0), // State.CyberStnd2 + new MobjStateDef(676, Sprite.CYBR, 0, 3, null, ma.Hoof, MobjState.CyberRun2, 0, 0), // State.CyberRun1 + new MobjStateDef(677, Sprite.CYBR, 0, 3, null, ma.Chase, MobjState.CyberRun3, 0, 0), // State.CyberRun2 + new MobjStateDef(678, Sprite.CYBR, 1, 3, null, ma.Chase, MobjState.CyberRun4, 0, 0), // State.CyberRun3 + new MobjStateDef(679, Sprite.CYBR, 1, 3, null, ma.Chase, MobjState.CyberRun5, 0, 0), // State.CyberRun4 + new MobjStateDef(680, Sprite.CYBR, 2, 3, null, ma.Chase, MobjState.CyberRun6, 0, 0), // State.CyberRun5 + new MobjStateDef(681, Sprite.CYBR, 2, 3, null, ma.Chase, MobjState.CyberRun7, 0, 0), // State.CyberRun6 + new MobjStateDef(682, Sprite.CYBR, 3, 3, null, ma.Metal, MobjState.CyberRun8, 0, 0), // State.CyberRun7 + new MobjStateDef(683, Sprite.CYBR, 3, 3, null, ma.Chase, MobjState.CyberRun1, 0, 0), // State.CyberRun8 + new MobjStateDef(684, Sprite.CYBR, 4, 6, null, ma.FaceTarget, MobjState.CyberAtk2, 0, 0), // State.CyberAtk1 + new MobjStateDef(685, Sprite.CYBR, 5, 12, null, ma.CyberAttack, MobjState.CyberAtk3, 0, 0), // State.CyberAtk2 + new MobjStateDef(686, Sprite.CYBR, 4, 12, null, ma.FaceTarget, MobjState.CyberAtk4, 0, 0), // State.CyberAtk3 + new MobjStateDef(687, Sprite.CYBR, 5, 12, null, ma.CyberAttack, MobjState.CyberAtk5, 0, 0), // State.CyberAtk4 + new MobjStateDef(688, Sprite.CYBR, 4, 12, null, ma.FaceTarget, MobjState.CyberAtk6, 0, 0), // State.CyberAtk5 + new MobjStateDef(689, Sprite.CYBR, 5, 12, null, ma.CyberAttack, MobjState.CyberRun1, 0, 0), // State.CyberAtk6 + new MobjStateDef(690, Sprite.CYBR, 6, 10, null, ma.Pain, MobjState.CyberRun1, 0, 0), // State.CyberPain + new MobjStateDef(691, Sprite.CYBR, 7, 10, null, null, MobjState.CyberDie2, 0, 0), // State.CyberDie1 + new MobjStateDef(692, Sprite.CYBR, 8, 10, null, ma.Scream, MobjState.CyberDie3, 0, 0), // State.CyberDie2 + new MobjStateDef(693, Sprite.CYBR, 9, 10, null, null, MobjState.CyberDie4, 0, 0), // State.CyberDie3 + new MobjStateDef(694, Sprite.CYBR, 10, 10, null, null, MobjState.CyberDie5, 0, 0), // State.CyberDie4 + new MobjStateDef(695, Sprite.CYBR, 11, 10, null, null, MobjState.CyberDie6, 0, 0), // State.CyberDie5 + new MobjStateDef(696, Sprite.CYBR, 12, 10, null, ma.Fall, MobjState.CyberDie7, 0, 0), // State.CyberDie6 + new MobjStateDef(697, Sprite.CYBR, 13, 10, null, null, MobjState.CyberDie8, 0, 0), // State.CyberDie7 + new MobjStateDef(698, Sprite.CYBR, 14, 10, null, null, MobjState.CyberDie9, 0, 0), // State.CyberDie8 + new MobjStateDef(699, Sprite.CYBR, 15, 30, null, null, MobjState.CyberDie10, 0, 0), // State.CyberDie9 + new MobjStateDef(700, Sprite.CYBR, 15, -1, null, ma.BossDeath, MobjState.Null, 0, 0), // State.CyberDie10 + new MobjStateDef(701, Sprite.PAIN, 0, 10, null, ma.Look, MobjState.PainStnd, 0, 0), // State.PainStnd + new MobjStateDef(702, Sprite.PAIN, 0, 3, null, ma.Chase, MobjState.PainRun2, 0, 0), // State.PainRun1 + new MobjStateDef(703, Sprite.PAIN, 0, 3, null, ma.Chase, MobjState.PainRun3, 0, 0), // State.PainRun2 + new MobjStateDef(704, Sprite.PAIN, 1, 3, null, ma.Chase, MobjState.PainRun4, 0, 0), // State.PainRun3 + new MobjStateDef(705, Sprite.PAIN, 1, 3, null, ma.Chase, MobjState.PainRun5, 0, 0), // State.PainRun4 + new MobjStateDef(706, Sprite.PAIN, 2, 3, null, ma.Chase, MobjState.PainRun6, 0, 0), // State.PainRun5 + new MobjStateDef(707, Sprite.PAIN, 2, 3, null, ma.Chase, MobjState.PainRun1, 0, 0), // State.PainRun6 + new MobjStateDef(708, Sprite.PAIN, 3, 5, null, ma.FaceTarget, MobjState.PainAtk2, 0, 0), // State.PainAtk1 + new MobjStateDef(709, Sprite.PAIN, 4, 5, null, ma.FaceTarget, MobjState.PainAtk3, 0, 0), // State.PainAtk2 + new MobjStateDef(710, Sprite.PAIN, 32773, 5, null, ma.FaceTarget, MobjState.PainAtk4, 0, 0), // State.PainAtk3 + new MobjStateDef(711, Sprite.PAIN, 32773, 0, null, ma.PainAttack, MobjState.PainRun1, 0, 0), // State.PainAtk4 + new MobjStateDef(712, Sprite.PAIN, 6, 6, null, null, MobjState.PainPain2, 0, 0), // State.PainPain + new MobjStateDef(713, Sprite.PAIN, 6, 6, null, ma.Pain, MobjState.PainRun1, 0, 0), // State.PainPain2 + new MobjStateDef(714, Sprite.PAIN, 32775, 8, null, null, MobjState.PainDie2, 0, 0), // State.PainDie1 + new MobjStateDef(715, Sprite.PAIN, 32776, 8, null, ma.Scream, MobjState.PainDie3, 0, 0), // State.PainDie2 + new MobjStateDef(716, Sprite.PAIN, 32777, 8, null, null, MobjState.PainDie4, 0, 0), // State.PainDie3 + new MobjStateDef(717, Sprite.PAIN, 32778, 8, null, null, MobjState.PainDie5, 0, 0), // State.PainDie4 + new MobjStateDef(718, Sprite.PAIN, 32779, 8, null, ma.PainDie, MobjState.PainDie6, 0, 0), // State.PainDie5 + new MobjStateDef(719, Sprite.PAIN, 32780, 8, null, null, MobjState.Null, 0, 0), // State.PainDie6 + new MobjStateDef(720, Sprite.PAIN, 12, 8, null, null, MobjState.PainRaise2, 0, 0), // State.PainRaise1 + new MobjStateDef(721, Sprite.PAIN, 11, 8, null, null, MobjState.PainRaise3, 0, 0), // State.PainRaise2 + new MobjStateDef(722, Sprite.PAIN, 10, 8, null, null, MobjState.PainRaise4, 0, 0), // State.PainRaise3 + new MobjStateDef(723, Sprite.PAIN, 9, 8, null, null, MobjState.PainRaise5, 0, 0), // State.PainRaise4 + new MobjStateDef(724, Sprite.PAIN, 8, 8, null, null, MobjState.PainRaise6, 0, 0), // State.PainRaise5 + new MobjStateDef(725, Sprite.PAIN, 7, 8, null, null, MobjState.PainRun1, 0, 0), // State.PainRaise6 + new MobjStateDef(726, Sprite.SSWV, 0, 10, null, ma.Look, MobjState.SswvStnd2, 0, 0), // State.SswvStnd + new MobjStateDef(727, Sprite.SSWV, 1, 10, null, ma.Look, MobjState.SswvStnd, 0, 0), // State.SswvStnd2 + new MobjStateDef(728, Sprite.SSWV, 0, 3, null, ma.Chase, MobjState.SswvRun2, 0, 0), // State.SswvRun1 + new MobjStateDef(729, Sprite.SSWV, 0, 3, null, ma.Chase, MobjState.SswvRun3, 0, 0), // State.SswvRun2 + new MobjStateDef(730, Sprite.SSWV, 1, 3, null, ma.Chase, MobjState.SswvRun4, 0, 0), // State.SswvRun3 + new MobjStateDef(731, Sprite.SSWV, 1, 3, null, ma.Chase, MobjState.SswvRun5, 0, 0), // State.SswvRun4 + new MobjStateDef(732, Sprite.SSWV, 2, 3, null, ma.Chase, MobjState.SswvRun6, 0, 0), // State.SswvRun5 + new MobjStateDef(733, Sprite.SSWV, 2, 3, null, ma.Chase, MobjState.SswvRun7, 0, 0), // State.SswvRun6 + new MobjStateDef(734, Sprite.SSWV, 3, 3, null, ma.Chase, MobjState.SswvRun8, 0, 0), // State.SswvRun7 + new MobjStateDef(735, Sprite.SSWV, 3, 3, null, ma.Chase, MobjState.SswvRun1, 0, 0), // State.SswvRun8 + new MobjStateDef(736, Sprite.SSWV, 4, 10, null, ma.FaceTarget, MobjState.SswvAtk2, 0, 0), // State.SswvAtk1 + new MobjStateDef(737, Sprite.SSWV, 5, 10, null, ma.FaceTarget, MobjState.SswvAtk3, 0, 0), // State.SswvAtk2 + new MobjStateDef(738, Sprite.SSWV, 32774, 4, null, ma.CPosAttack, MobjState.SswvAtk4, 0, 0), // State.SswvAtk3 + new MobjStateDef(739, Sprite.SSWV, 5, 6, null, ma.FaceTarget, MobjState.SswvAtk5, 0, 0), // State.SswvAtk4 + new MobjStateDef(740, Sprite.SSWV, 32774, 4, null, ma.CPosAttack, MobjState.SswvAtk6, 0, 0), // State.SswvAtk5 + new MobjStateDef(741, Sprite.SSWV, 5, 1, null, ma.CPosRefire, MobjState.SswvAtk2, 0, 0), // State.SswvAtk6 + new MobjStateDef(742, Sprite.SSWV, 7, 3, null, null, MobjState.SswvPain2, 0, 0), // State.SswvPain + new MobjStateDef(743, Sprite.SSWV, 7, 3, null, ma.Pain, MobjState.SswvRun1, 0, 0), // State.SswvPain2 + new MobjStateDef(744, Sprite.SSWV, 8, 5, null, null, MobjState.SswvDie2, 0, 0), // State.SswvDie1 + new MobjStateDef(745, Sprite.SSWV, 9, 5, null, ma.Scream, MobjState.SswvDie3, 0, 0), // State.SswvDie2 + new MobjStateDef(746, Sprite.SSWV, 10, 5, null, ma.Fall, MobjState.SswvDie4, 0, 0), // State.SswvDie3 + new MobjStateDef(747, Sprite.SSWV, 11, 5, null, null, MobjState.SswvDie5, 0, 0), // State.SswvDie4 + new MobjStateDef(748, Sprite.SSWV, 12, -1, null, null, MobjState.Null, 0, 0), // State.SswvDie5 + new MobjStateDef(749, Sprite.SSWV, 13, 5, null, null, MobjState.SswvXdie2, 0, 0), // State.SswvXdie1 + new MobjStateDef(750, Sprite.SSWV, 14, 5, null, ma.XScream, MobjState.SswvXdie3, 0, 0), // State.SswvXdie2 + new MobjStateDef(751, Sprite.SSWV, 15, 5, null, ma.Fall, MobjState.SswvXdie4, 0, 0), // State.SswvXdie3 + new MobjStateDef(752, Sprite.SSWV, 16, 5, null, null, MobjState.SswvXdie5, 0, 0), // State.SswvXdie4 + new MobjStateDef(753, Sprite.SSWV, 17, 5, null, null, MobjState.SswvXdie6, 0, 0), // State.SswvXdie5 + new MobjStateDef(754, Sprite.SSWV, 18, 5, null, null, MobjState.SswvXdie7, 0, 0), // State.SswvXdie6 + new MobjStateDef(755, Sprite.SSWV, 19, 5, null, null, MobjState.SswvXdie8, 0, 0), // State.SswvXdie7 + new MobjStateDef(756, Sprite.SSWV, 20, 5, null, null, MobjState.SswvXdie9, 0, 0), // State.SswvXdie8 + new MobjStateDef(757, Sprite.SSWV, 21, -1, null, null, MobjState.Null, 0, 0), // State.SswvXdie9 + new MobjStateDef(758, Sprite.SSWV, 12, 5, null, null, MobjState.SswvRaise2, 0, 0), // State.SswvRaise1 + new MobjStateDef(759, Sprite.SSWV, 11, 5, null, null, MobjState.SswvRaise3, 0, 0), // State.SswvRaise2 + new MobjStateDef(760, Sprite.SSWV, 10, 5, null, null, MobjState.SswvRaise4, 0, 0), // State.SswvRaise3 + new MobjStateDef(761, Sprite.SSWV, 9, 5, null, null, MobjState.SswvRaise5, 0, 0), // State.SswvRaise4 + new MobjStateDef(762, Sprite.SSWV, 8, 5, null, null, MobjState.SswvRun1, 0, 0), // State.SswvRaise5 + new MobjStateDef(763, Sprite.KEEN, 0, -1, null, null, MobjState.Keenstnd, 0, 0), // State.Keenstnd + new MobjStateDef(764, Sprite.KEEN, 0, 6, null, null, MobjState.Commkeen2, 0, 0), // State.Commkeen + new MobjStateDef(765, Sprite.KEEN, 1, 6, null, null, MobjState.Commkeen3, 0, 0), // State.Commkeen2 + new MobjStateDef(766, Sprite.KEEN, 2, 6, null, ma.Scream, MobjState.Commkeen4, 0, 0), // State.Commkeen3 + new MobjStateDef(767, Sprite.KEEN, 3, 6, null, null, MobjState.Commkeen5, 0, 0), // State.Commkeen4 + new MobjStateDef(768, Sprite.KEEN, 4, 6, null, null, MobjState.Commkeen6, 0, 0), // State.Commkeen5 + new MobjStateDef(769, Sprite.KEEN, 5, 6, null, null, MobjState.Commkeen7, 0, 0), // State.Commkeen6 + new MobjStateDef(770, Sprite.KEEN, 6, 6, null, null, MobjState.Commkeen8, 0, 0), // State.Commkeen7 + new MobjStateDef(771, Sprite.KEEN, 7, 6, null, null, MobjState.Commkeen9, 0, 0), // State.Commkeen8 + new MobjStateDef(772, Sprite.KEEN, 8, 6, null, null, MobjState.Commkeen10, 0, 0), // State.Commkeen9 + new MobjStateDef(773, Sprite.KEEN, 9, 6, null, null, MobjState.Commkeen11, 0, 0), // State.Commkeen10 + new MobjStateDef(774, Sprite.KEEN, 10, 6, null, ma.KeenDie, MobjState.Commkeen12, 0, 0), // State.Commkeen11 + new MobjStateDef(775, Sprite.KEEN, 11, -1, null, null, MobjState.Null, 0, 0), // State.Commkeen12 + new MobjStateDef(776, Sprite.KEEN, 12, 4, null, null, MobjState.Keenpain2, 0, 0), // State.Keenpain + new MobjStateDef(777, Sprite.KEEN, 12, 8, null, ma.Pain, MobjState.Keenstnd, 0, 0), // State.Keenpain2 + new MobjStateDef(778, Sprite.BBRN, 0, -1, null, null, MobjState.Null, 0, 0), // State.Brain + new MobjStateDef(779, Sprite.BBRN, 1, 36, null, ma.BrainPain, MobjState.Brain, 0, 0), // State.BrainPain + new MobjStateDef(780, Sprite.BBRN, 0, 100, null, ma.BrainScream, MobjState.BrainDie2, 0, 0), // State.BrainDie1 + new MobjStateDef(781, Sprite.BBRN, 0, 10, null, null, MobjState.BrainDie3, 0, 0), // State.BrainDie2 + new MobjStateDef(782, Sprite.BBRN, 0, 10, null, null, MobjState.BrainDie4, 0, 0), // State.BrainDie3 + new MobjStateDef(783, Sprite.BBRN, 0, -1, null, ma.BrainDie, MobjState.Null, 0, 0), // State.BrainDie4 + new MobjStateDef(784, Sprite.SSWV, 0, 10, null, ma.Look, MobjState.Braineye, 0, 0), // State.Braineye + new MobjStateDef(785, Sprite.SSWV, 0, 181, null, ma.BrainAwake, MobjState.Braineye1, 0, 0), // State.Braineyesee + new MobjStateDef(786, Sprite.SSWV, 0, 150, null, ma.BrainSpit, MobjState.Braineye1, 0, 0), // State.Braineye1 + new MobjStateDef(787, Sprite.BOSF, 32768, 3, null, ma.SpawnSound, MobjState.Spawn2, 0, 0), // State.Spawn1 + new MobjStateDef(788, Sprite.BOSF, 32769, 3, null, ma.SpawnFly, MobjState.Spawn3, 0, 0), // State.Spawn2 + new MobjStateDef(789, Sprite.BOSF, 32770, 3, null, ma.SpawnFly, MobjState.Spawn4, 0, 0), // State.Spawn3 + new MobjStateDef(790, Sprite.BOSF, 32771, 3, null, ma.SpawnFly, MobjState.Spawn1, 0, 0), // State.Spawn4 + new MobjStateDef(791, Sprite.FIRE, 32768, 4, null, ma.Fire, MobjState.Spawnfire2, 0, 0), // State.Spawnfire1 + new MobjStateDef(792, Sprite.FIRE, 32769, 4, null, ma.Fire, MobjState.Spawnfire3, 0, 0), // State.Spawnfire2 + new MobjStateDef(793, Sprite.FIRE, 32770, 4, null, ma.Fire, MobjState.Spawnfire4, 0, 0), // State.Spawnfire3 + new MobjStateDef(794, Sprite.FIRE, 32771, 4, null, ma.Fire, MobjState.Spawnfire5, 0, 0), // State.Spawnfire4 + new MobjStateDef(795, Sprite.FIRE, 32772, 4, null, ma.Fire, MobjState.Spawnfire6, 0, 0), // State.Spawnfire5 + new MobjStateDef(796, Sprite.FIRE, 32773, 4, null, ma.Fire, MobjState.Spawnfire7, 0, 0), // State.Spawnfire6 + new MobjStateDef(797, Sprite.FIRE, 32774, 4, null, ma.Fire, MobjState.Spawnfire8, 0, 0), // State.Spawnfire7 + new MobjStateDef(798, Sprite.FIRE, 32775, 4, null, ma.Fire, MobjState.Null, 0, 0), // State.Spawnfire8 + new MobjStateDef(799, Sprite.MISL, 32769, 10, null, null, MobjState.Brainexplode2, 0, 0), // State.Brainexplode1 + new MobjStateDef(800, Sprite.MISL, 32770, 10, null, null, MobjState.Brainexplode3, 0, 0), // State.Brainexplode2 + new MobjStateDef(801, Sprite.MISL, 32771, 10, null, ma.BrainExplode, MobjState.Null, 0, 0), // State.Brainexplode3 + new MobjStateDef(802, Sprite.ARM1, 0, 6, null, null, MobjState.Arm1A, 0, 0), // State.Arm1 + new MobjStateDef(803, Sprite.ARM1, 32769, 7, null, null, MobjState.Arm1, 0, 0), // State.Arm1A + new MobjStateDef(804, Sprite.ARM2, 0, 6, null, null, MobjState.Arm2A, 0, 0), // State.Arm2 + new MobjStateDef(805, Sprite.ARM2, 32769, 6, null, null, MobjState.Arm2, 0, 0), // State.Arm2A + new MobjStateDef(806, Sprite.BAR1, 0, 6, null, null, MobjState.Bar2, 0, 0), // State.Bar1 + new MobjStateDef(807, Sprite.BAR1, 1, 6, null, null, MobjState.Bar1, 0, 0), // State.Bar2 + new MobjStateDef(808, Sprite.BEXP, 32768, 5, null, null, MobjState.Bexp2, 0, 0), // State.Bexp + new MobjStateDef(809, Sprite.BEXP, 32769, 5, null, ma.Scream, MobjState.Bexp3, 0, 0), // State.Bexp2 + new MobjStateDef(810, Sprite.BEXP, 32770, 5, null, null, MobjState.Bexp4, 0, 0), // State.Bexp3 + new MobjStateDef(811, Sprite.BEXP, 32771, 10, null, ma.Explode, MobjState.Bexp5, 0, 0), // State.Bexp4 + new MobjStateDef(812, Sprite.BEXP, 32772, 10, null, null, MobjState.Null, 0, 0), // State.Bexp5 + new MobjStateDef(813, Sprite.FCAN, 32768, 4, null, null, MobjState.Bbar2, 0, 0), // State.Bbar1 + new MobjStateDef(814, Sprite.FCAN, 32769, 4, null, null, MobjState.Bbar3, 0, 0), // State.Bbar2 + new MobjStateDef(815, Sprite.FCAN, 32770, 4, null, null, MobjState.Bbar1, 0, 0), // State.Bbar3 + new MobjStateDef(816, Sprite.BON1, 0, 6, null, null, MobjState.Bon1A, 0, 0), // State.Bon1 + new MobjStateDef(817, Sprite.BON1, 1, 6, null, null, MobjState.Bon1B, 0, 0), // State.Bon1A + new MobjStateDef(818, Sprite.BON1, 2, 6, null, null, MobjState.Bon1C, 0, 0), // State.Bon1B + new MobjStateDef(819, Sprite.BON1, 3, 6, null, null, MobjState.Bon1D, 0, 0), // State.Bon1C + new MobjStateDef(820, Sprite.BON1, 2, 6, null, null, MobjState.Bon1E, 0, 0), // State.Bon1D + new MobjStateDef(821, Sprite.BON1, 1, 6, null, null, MobjState.Bon1, 0, 0), // State.Bon1E + new MobjStateDef(822, Sprite.BON2, 0, 6, null, null, MobjState.Bon2A, 0, 0), // State.Bon2 + new MobjStateDef(823, Sprite.BON2, 1, 6, null, null, MobjState.Bon2B, 0, 0), // State.Bon2A + new MobjStateDef(824, Sprite.BON2, 2, 6, null, null, MobjState.Bon2C, 0, 0), // State.Bon2B + new MobjStateDef(825, Sprite.BON2, 3, 6, null, null, MobjState.Bon2D, 0, 0), // State.Bon2C + new MobjStateDef(826, Sprite.BON2, 2, 6, null, null, MobjState.Bon2E, 0, 0), // State.Bon2D + new MobjStateDef(827, Sprite.BON2, 1, 6, null, null, MobjState.Bon2, 0, 0), // State.Bon2E + new MobjStateDef(828, Sprite.BKEY, 0, 10, null, null, MobjState.Bkey2, 0, 0), // State.Bkey + new MobjStateDef(829, Sprite.BKEY, 32769, 10, null, null, MobjState.Bkey, 0, 0), // State.Bkey2 + new MobjStateDef(830, Sprite.RKEY, 0, 10, null, null, MobjState.Rkey2, 0, 0), // State.Rkey + new MobjStateDef(831, Sprite.RKEY, 32769, 10, null, null, MobjState.Rkey, 0, 0), // State.Rkey2 + new MobjStateDef(832, Sprite.YKEY, 0, 10, null, null, MobjState.Ykey2, 0, 0), // State.Ykey + new MobjStateDef(833, Sprite.YKEY, 32769, 10, null, null, MobjState.Ykey, 0, 0), // State.Ykey2 + new MobjStateDef(834, Sprite.BSKU, 0, 10, null, null, MobjState.Bskull2, 0, 0), // State.Bskull + new MobjStateDef(835, Sprite.BSKU, 32769, 10, null, null, MobjState.Bskull, 0, 0), // State.Bskull2 + new MobjStateDef(836, Sprite.RSKU, 0, 10, null, null, MobjState.Rskull2, 0, 0), // State.Rskull + new MobjStateDef(837, Sprite.RSKU, 32769, 10, null, null, MobjState.Rskull, 0, 0), // State.Rskull2 + new MobjStateDef(838, Sprite.YSKU, 0, 10, null, null, MobjState.Yskull2, 0, 0), // State.Yskull + new MobjStateDef(839, Sprite.YSKU, 32769, 10, null, null, MobjState.Yskull, 0, 0), // State.Yskull2 + new MobjStateDef(840, Sprite.STIM, 0, -1, null, null, MobjState.Null, 0, 0), // State.Stim + new MobjStateDef(841, Sprite.MEDI, 0, -1, null, null, MobjState.Null, 0, 0), // State.Medi + new MobjStateDef(842, Sprite.SOUL, 32768, 6, null, null, MobjState.Soul2, 0, 0), // State.Soul + new MobjStateDef(843, Sprite.SOUL, 32769, 6, null, null, MobjState.Soul3, 0, 0), // State.Soul2 + new MobjStateDef(844, Sprite.SOUL, 32770, 6, null, null, MobjState.Soul4, 0, 0), // State.Soul3 + new MobjStateDef(845, Sprite.SOUL, 32771, 6, null, null, MobjState.Soul5, 0, 0), // State.Soul4 + new MobjStateDef(846, Sprite.SOUL, 32770, 6, null, null, MobjState.Soul6, 0, 0), // State.Soul5 + new MobjStateDef(847, Sprite.SOUL, 32769, 6, null, null, MobjState.Soul, 0, 0), // State.Soul6 + new MobjStateDef(848, Sprite.PINV, 32768, 6, null, null, MobjState.Pinv2, 0, 0), // State.Pinv + new MobjStateDef(849, Sprite.PINV, 32769, 6, null, null, MobjState.Pinv3, 0, 0), // State.Pinv2 + new MobjStateDef(850, Sprite.PINV, 32770, 6, null, null, MobjState.Pinv4, 0, 0), // State.Pinv3 + new MobjStateDef(851, Sprite.PINV, 32771, 6, null, null, MobjState.Pinv, 0, 0), // State.Pinv4 + new MobjStateDef(852, Sprite.PSTR, 32768, -1, null, null, MobjState.Null, 0, 0), // State.Pstr + new MobjStateDef(853, Sprite.PINS, 32768, 6, null, null, MobjState.Pins2, 0, 0), // State.Pins + new MobjStateDef(854, Sprite.PINS, 32769, 6, null, null, MobjState.Pins3, 0, 0), // State.Pins2 + new MobjStateDef(855, Sprite.PINS, 32770, 6, null, null, MobjState.Pins4, 0, 0), // State.Pins3 + new MobjStateDef(856, Sprite.PINS, 32771, 6, null, null, MobjState.Pins, 0, 0), // State.Pins4 + new MobjStateDef(857, Sprite.MEGA, 32768, 6, null, null, MobjState.Mega2, 0, 0), // State.Mega + new MobjStateDef(858, Sprite.MEGA, 32769, 6, null, null, MobjState.Mega3, 0, 0), // State.Mega2 + new MobjStateDef(859, Sprite.MEGA, 32770, 6, null, null, MobjState.Mega4, 0, 0), // State.Mega3 + new MobjStateDef(860, Sprite.MEGA, 32771, 6, null, null, MobjState.Mega, 0, 0), // State.Mega4 + new MobjStateDef(861, Sprite.SUIT, 32768, -1, null, null, MobjState.Null, 0, 0), // State.Suit + new MobjStateDef(862, Sprite.PMAP, 32768, 6, null, null, MobjState.Pmap2, 0, 0), // State.Pmap + new MobjStateDef(863, Sprite.PMAP, 32769, 6, null, null, MobjState.Pmap3, 0, 0), // State.Pmap2 + new MobjStateDef(864, Sprite.PMAP, 32770, 6, null, null, MobjState.Pmap4, 0, 0), // State.Pmap3 + new MobjStateDef(865, Sprite.PMAP, 32771, 6, null, null, MobjState.Pmap5, 0, 0), // State.Pmap4 + new MobjStateDef(866, Sprite.PMAP, 32770, 6, null, null, MobjState.Pmap6, 0, 0), // State.Pmap5 + new MobjStateDef(867, Sprite.PMAP, 32769, 6, null, null, MobjState.Pmap, 0, 0), // State.Pmap6 + new MobjStateDef(868, Sprite.PVIS, 32768, 6, null, null, MobjState.Pvis2, 0, 0), // State.Pvis + new MobjStateDef(869, Sprite.PVIS, 1, 6, null, null, MobjState.Pvis, 0, 0), // State.Pvis2 + new MobjStateDef(870, Sprite.CLIP, 0, -1, null, null, MobjState.Null, 0, 0), // State.Clip + new MobjStateDef(871, Sprite.AMMO, 0, -1, null, null, MobjState.Null, 0, 0), // State.Ammo + new MobjStateDef(872, Sprite.ROCK, 0, -1, null, null, MobjState.Null, 0, 0), // State.Rock + new MobjStateDef(873, Sprite.BROK, 0, -1, null, null, MobjState.Null, 0, 0), // State.Brok + new MobjStateDef(874, Sprite.CELL, 0, -1, null, null, MobjState.Null, 0, 0), // State.Cell + new MobjStateDef(875, Sprite.CELP, 0, -1, null, null, MobjState.Null, 0, 0), // State.Celp + new MobjStateDef(876, Sprite.SHEL, 0, -1, null, null, MobjState.Null, 0, 0), // State.Shel + new MobjStateDef(877, Sprite.SBOX, 0, -1, null, null, MobjState.Null, 0, 0), // State.Sbox + new MobjStateDef(878, Sprite.BPAK, 0, -1, null, null, MobjState.Null, 0, 0), // State.Bpak + new MobjStateDef(879, Sprite.BFUG, 0, -1, null, null, MobjState.Null, 0, 0), // State.Bfug + new MobjStateDef(880, Sprite.MGUN, 0, -1, null, null, MobjState.Null, 0, 0), // State.Mgun + new MobjStateDef(881, Sprite.CSAW, 0, -1, null, null, MobjState.Null, 0, 0), // State.Csaw + new MobjStateDef(882, Sprite.LAUN, 0, -1, null, null, MobjState.Null, 0, 0), // State.Laun + new MobjStateDef(883, Sprite.PLAS, 0, -1, null, null, MobjState.Null, 0, 0), // State.Plas + new MobjStateDef(884, Sprite.SHOT, 0, -1, null, null, MobjState.Null, 0, 0), // State.Shot + new MobjStateDef(885, Sprite.SGN2, 0, -1, null, null, MobjState.Null, 0, 0), // State.Shot2 + new MobjStateDef(886, Sprite.COLU, 32768, -1, null, null, MobjState.Null, 0, 0), // State.Colu + new MobjStateDef(887, Sprite.SMT2, 0, -1, null, null, MobjState.Null, 0, 0), // State.Stalag + new MobjStateDef(888, Sprite.GOR1, 0, 10, null, null, MobjState.Bloodytwitch2, 0, 0), // State.Bloodytwitch + new MobjStateDef(889, Sprite.GOR1, 1, 15, null, null, MobjState.Bloodytwitch3, 0, 0), // State.Bloodytwitch2 + new MobjStateDef(890, Sprite.GOR1, 2, 8, null, null, MobjState.Bloodytwitch4, 0, 0), // State.Bloodytwitch3 + new MobjStateDef(891, Sprite.GOR1, 1, 6, null, null, MobjState.Bloodytwitch, 0, 0), // State.Bloodytwitch4 + new MobjStateDef(892, Sprite.PLAY, 13, -1, null, null, MobjState.Null, 0, 0), // State.Deadtorso + new MobjStateDef(893, Sprite.PLAY, 18, -1, null, null, MobjState.Null, 0, 0), // State.Deadbottom + new MobjStateDef(894, Sprite.POL2, 0, -1, null, null, MobjState.Null, 0, 0), // State.Headsonstick + new MobjStateDef(895, Sprite.POL5, 0, -1, null, null, MobjState.Null, 0, 0), // State.Gibs + new MobjStateDef(896, Sprite.POL4, 0, -1, null, null, MobjState.Null, 0, 0), // State.Headonastick + new MobjStateDef(897, Sprite.POL3, 32768, 6, null, null, MobjState.Headcandles2, 0, 0), // State.Headcandles + new MobjStateDef(898, Sprite.POL3, 32769, 6, null, null, MobjState.Headcandles, 0, 0), // State.Headcandles2 + new MobjStateDef(899, Sprite.POL1, 0, -1, null, null, MobjState.Null, 0, 0), // State.Deadstick + new MobjStateDef(900, Sprite.POL6, 0, 6, null, null, MobjState.Livestick2, 0, 0), // State.Livestick + new MobjStateDef(901, Sprite.POL6, 1, 8, null, null, MobjState.Livestick, 0, 0), // State.Livestick2 + new MobjStateDef(902, Sprite.GOR2, 0, -1, null, null, MobjState.Null, 0, 0), // State.Meat2 + new MobjStateDef(903, Sprite.GOR3, 0, -1, null, null, MobjState.Null, 0, 0), // State.Meat3 + new MobjStateDef(904, Sprite.GOR4, 0, -1, null, null, MobjState.Null, 0, 0), // State.Meat4 + new MobjStateDef(905, Sprite.GOR5, 0, -1, null, null, MobjState.Null, 0, 0), // State.Meat5 + new MobjStateDef(906, Sprite.SMIT, 0, -1, null, null, MobjState.Null, 0, 0), // State.Stalagtite + new MobjStateDef(907, Sprite.COL1, 0, -1, null, null, MobjState.Null, 0, 0), // State.Tallgrncol + new MobjStateDef(908, Sprite.COL2, 0, -1, null, null, MobjState.Null, 0, 0), // State.Shrtgrncol + new MobjStateDef(909, Sprite.COL3, 0, -1, null, null, MobjState.Null, 0, 0), // State.Tallredcol + new MobjStateDef(910, Sprite.COL4, 0, -1, null, null, MobjState.Null, 0, 0), // State.Shrtredcol + new MobjStateDef(911, Sprite.CAND, 32768, -1, null, null, MobjState.Null, 0, 0), // State.Candlestik + new MobjStateDef(912, Sprite.CBRA, 32768, -1, null, null, MobjState.Null, 0, 0), // State.Candelabra + new MobjStateDef(913, Sprite.COL6, 0, -1, null, null, MobjState.Null, 0, 0), // State.Skullcol + new MobjStateDef(914, Sprite.TRE1, 0, -1, null, null, MobjState.Null, 0, 0), // State.Torchtree + new MobjStateDef(915, Sprite.TRE2, 0, -1, null, null, MobjState.Null, 0, 0), // State.Bigtree + new MobjStateDef(916, Sprite.ELEC, 0, -1, null, null, MobjState.Null, 0, 0), // State.Techpillar + new MobjStateDef(917, Sprite.CEYE, 32768, 6, null, null, MobjState.Evileye2, 0, 0), // State.Evileye + new MobjStateDef(918, Sprite.CEYE, 32769, 6, null, null, MobjState.Evileye3, 0, 0), // State.Evileye2 + new MobjStateDef(919, Sprite.CEYE, 32770, 6, null, null, MobjState.Evileye4, 0, 0), // State.Evileye3 + new MobjStateDef(920, Sprite.CEYE, 32769, 6, null, null, MobjState.Evileye, 0, 0), // State.Evileye4 + new MobjStateDef(921, Sprite.FSKU, 32768, 6, null, null, MobjState.Floatskull2, 0, 0), // State.Floatskull + new MobjStateDef(922, Sprite.FSKU, 32769, 6, null, null, MobjState.Floatskull3, 0, 0), // State.Floatskull2 + new MobjStateDef(923, Sprite.FSKU, 32770, 6, null, null, MobjState.Floatskull, 0, 0), // State.Floatskull3 + new MobjStateDef(924, Sprite.COL5, 0, 14, null, null, MobjState.Heartcol2, 0, 0), // State.Heartcol + new MobjStateDef(925, Sprite.COL5, 1, 14, null, null, MobjState.Heartcol, 0, 0), // State.Heartcol2 + new MobjStateDef(926, Sprite.TBLU, 32768, 4, null, null, MobjState.Bluetorch2, 0, 0), // State.Bluetorch + new MobjStateDef(927, Sprite.TBLU, 32769, 4, null, null, MobjState.Bluetorch3, 0, 0), // State.Bluetorch2 + new MobjStateDef(928, Sprite.TBLU, 32770, 4, null, null, MobjState.Bluetorch4, 0, 0), // State.Bluetorch3 + new MobjStateDef(929, Sprite.TBLU, 32771, 4, null, null, MobjState.Bluetorch, 0, 0), // State.Bluetorch4 + new MobjStateDef(930, Sprite.TGRN, 32768, 4, null, null, MobjState.Greentorch2, 0, 0), // State.Greentorch + new MobjStateDef(931, Sprite.TGRN, 32769, 4, null, null, MobjState.Greentorch3, 0, 0), // State.Greentorch2 + new MobjStateDef(932, Sprite.TGRN, 32770, 4, null, null, MobjState.Greentorch4, 0, 0), // State.Greentorch3 + new MobjStateDef(933, Sprite.TGRN, 32771, 4, null, null, MobjState.Greentorch, 0, 0), // State.Greentorch4 + new MobjStateDef(934, Sprite.TRED, 32768, 4, null, null, MobjState.Redtorch2, 0, 0), // State.Redtorch + new MobjStateDef(935, Sprite.TRED, 32769, 4, null, null, MobjState.Redtorch3, 0, 0), // State.Redtorch2 + new MobjStateDef(936, Sprite.TRED, 32770, 4, null, null, MobjState.Redtorch4, 0, 0), // State.Redtorch3 + new MobjStateDef(937, Sprite.TRED, 32771, 4, null, null, MobjState.Redtorch, 0, 0), // State.Redtorch4 + new MobjStateDef(938, Sprite.SMBT, 32768, 4, null, null, MobjState.Btorchshrt2, 0, 0), // State.Btorchshrt + new MobjStateDef(939, Sprite.SMBT, 32769, 4, null, null, MobjState.Btorchshrt3, 0, 0), // State.Btorchshrt2 + new MobjStateDef(940, Sprite.SMBT, 32770, 4, null, null, MobjState.Btorchshrt4, 0, 0), // State.Btorchshrt3 + new MobjStateDef(941, Sprite.SMBT, 32771, 4, null, null, MobjState.Btorchshrt, 0, 0), // State.Btorchshrt4 + new MobjStateDef(942, Sprite.SMGT, 32768, 4, null, null, MobjState.Gtorchshrt2, 0, 0), // State.Gtorchshrt + new MobjStateDef(943, Sprite.SMGT, 32769, 4, null, null, MobjState.Gtorchshrt3, 0, 0), // State.Gtorchshrt2 + new MobjStateDef(944, Sprite.SMGT, 32770, 4, null, null, MobjState.Gtorchshrt4, 0, 0), // State.Gtorchshrt3 + new MobjStateDef(945, Sprite.SMGT, 32771, 4, null, null, MobjState.Gtorchshrt, 0, 0), // State.Gtorchshrt4 + new MobjStateDef(946, Sprite.SMRT, 32768, 4, null, null, MobjState.Rtorchshrt2, 0, 0), // State.Rtorchshrt + new MobjStateDef(947, Sprite.SMRT, 32769, 4, null, null, MobjState.Rtorchshrt3, 0, 0), // State.Rtorchshrt2 + new MobjStateDef(948, Sprite.SMRT, 32770, 4, null, null, MobjState.Rtorchshrt4, 0, 0), // State.Rtorchshrt3 + new MobjStateDef(949, Sprite.SMRT, 32771, 4, null, null, MobjState.Rtorchshrt, 0, 0), // State.Rtorchshrt4 + new MobjStateDef(950, Sprite.HDB1, 0, -1, null, null, MobjState.Null, 0, 0), // State.Hangnoguts + new MobjStateDef(951, Sprite.HDB2, 0, -1, null, null, MobjState.Null, 0, 0), // State.Hangbnobrain + new MobjStateDef(952, Sprite.HDB3, 0, -1, null, null, MobjState.Null, 0, 0), // State.Hangtlookdn + new MobjStateDef(953, Sprite.HDB4, 0, -1, null, null, MobjState.Null, 0, 0), // State.Hangtskull + new MobjStateDef(954, Sprite.HDB5, 0, -1, null, null, MobjState.Null, 0, 0), // State.Hangtlookup + new MobjStateDef(955, Sprite.HDB6, 0, -1, null, null, MobjState.Null, 0, 0), // State.Hangtnobrain + new MobjStateDef(956, Sprite.POB1, 0, -1, null, null, MobjState.Null, 0, 0), // State.Colongibs + new MobjStateDef(957, Sprite.POB2, 0, -1, null, null, MobjState.Null, 0, 0), // State.Smallpool + new MobjStateDef(958, Sprite.BRS1, 0, -1, null, null, MobjState.Null, 0, 0), // State.Brainstem + new MobjStateDef(959, Sprite.TLMP, 32768, 4, null, null, MobjState.Techlamp2, 0, 0), // State.Techlamp + new MobjStateDef(960, Sprite.TLMP, 32769, 4, null, null, MobjState.Techlamp3, 0, 0), // State.Techlamp2 + new MobjStateDef(961, Sprite.TLMP, 32770, 4, null, null, MobjState.Techlamp4, 0, 0), // State.Techlamp3 + new MobjStateDef(962, Sprite.TLMP, 32771, 4, null, null, MobjState.Techlamp, 0, 0), // State.Techlamp4 + new MobjStateDef(963, Sprite.TLP2, 32768, 4, null, null, MobjState.Tech2Lamp2, 0, 0), // State.Tech2Lamp + new MobjStateDef(964, Sprite.TLP2, 32769, 4, null, null, MobjState.Tech2Lamp3, 0, 0), // State.Tech2Lamp2 + new MobjStateDef(965, Sprite.TLP2, 32770, 4, null, null, MobjState.Tech2Lamp4, 0, 0), // State.Tech2Lamp3 + new MobjStateDef(966, Sprite.TLP2, 32771, 4, null, null, MobjState.Tech2Lamp, 0, 0) // State.Tech2Lamp4 + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.Strings.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.Strings.cs new file mode 100644 index 00000000..a709e2c8 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.Strings.cs @@ -0,0 +1,545 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static class Strings + { + public static readonly DoomString PRESSKEY = new DoomString("press a key."); + public static readonly DoomString PRESSYN = new DoomString("press y or n."); + public static readonly DoomString QUITMSG = new DoomString("are you sure you want to\nquit this great game?"); + public static readonly DoomString LOADNET = new DoomString("you can't do load while in a net game!\n\n" + PRESSKEY); + public static readonly DoomString QLOADNET = new DoomString("you can't quickload during a netgame!\n\n" + PRESSKEY); + public static readonly DoomString QSAVESPOT = new DoomString("you haven't picked a quicksave slot yet!\n\n" + PRESSKEY); + public static readonly DoomString SAVEDEAD = new DoomString("you can't save if you aren't playing!\n\n" + PRESSKEY); + public static readonly DoomString QSPROMPT = new DoomString("quicksave over your game named\n\n'%s'?\n\n" + PRESSYN); + public static readonly DoomString QLPROMPT = new DoomString("do you want to quickload the game named\n\n'%s'?\n\n" + PRESSYN); + + public static readonly DoomString NEWGAME = new DoomString( + "you can't start a new game\n" + + "while in a network game.\n\n" + PRESSKEY); + + public static readonly DoomString NIGHTMARE = new DoomString( + "are you sure? this skill level\n" + + "isn't even remotely fair.\n\n" + PRESSYN); + + public static readonly DoomString SWSTRING = new DoomString( + "this is the shareware version of doom.\n\n" + + "you need to order the entire trilogy.\n\n" + PRESSKEY); + + public static readonly DoomString MSGOFF = new DoomString("Messages OFF"); + public static readonly DoomString MSGON = new DoomString("Messages ON"); + public static readonly DoomString NETEND = new DoomString("you can't end a netgame!\n\n" + PRESSKEY); + public static readonly DoomString ENDGAME = new DoomString("are you sure you want to end the game?\n\n" + PRESSYN); + public static readonly DoomString DOSY = new DoomString("(press y to quit)"); + public static readonly DoomString GAMMALVL0 = new DoomString("Gamma correction OFF"); + public static readonly DoomString GAMMALVL1 = new DoomString("Gamma correction level 1"); + public static readonly DoomString GAMMALVL2 = new DoomString("Gamma correction level 2"); + public static readonly DoomString GAMMALVL3 = new DoomString("Gamma correction level 3"); + public static readonly DoomString GAMMALVL4 = new DoomString("Gamma correction level 4"); + public static readonly DoomString EMPTYSTRING = new DoomString("empty slot"); + public static readonly DoomString GOTARMOR = new DoomString("Picked up the armor."); + public static readonly DoomString GOTMEGA = new DoomString("Picked up the MegaArmor!"); + public static readonly DoomString GOTHTHBONUS = new DoomString("Picked up a health bonus."); + public static readonly DoomString GOTARMBONUS = new DoomString("Picked up an armor bonus."); + public static readonly DoomString GOTSTIM = new DoomString("Picked up a stimpack."); + public static readonly DoomString GOTMEDINEED = new DoomString("Picked up a medikit that you REALLY need!"); + public static readonly DoomString GOTMEDIKIT = new DoomString("Picked up a medikit."); + public static readonly DoomString GOTSUPER = new DoomString("Supercharge!"); + public static readonly DoomString GOTBLUECARD = new DoomString("Picked up a blue keycard."); + public static readonly DoomString GOTYELWCARD = new DoomString("Picked up a yellow keycard."); + public static readonly DoomString GOTREDCARD = new DoomString("Picked up a red keycard."); + public static readonly DoomString GOTBLUESKUL = new DoomString("Picked up a blue skull key."); + public static readonly DoomString GOTYELWSKUL = new DoomString("Picked up a yellow skull key."); + public static readonly DoomString GOTREDSKULL = new DoomString("Picked up a red skull key."); + public static readonly DoomString GOTINVUL = new DoomString("Invulnerability!"); + public static readonly DoomString GOTBERSERK = new DoomString("Berserk!"); + public static readonly DoomString GOTINVIS = new DoomString("Partial Invisibility"); + public static readonly DoomString GOTSUIT = new DoomString("Radiation Shielding Suit"); + public static readonly DoomString GOTMAP = new DoomString("Computer Area Map"); + public static readonly DoomString GOTVISOR = new DoomString("Light Amplification Visor"); + public static readonly DoomString GOTMSPHERE = new DoomString("MegaSphere!"); + public static readonly DoomString GOTCLIP = new DoomString("Picked up a clip."); + public static readonly DoomString GOTCLIPBOX = new DoomString("Picked up a box of bullets."); + public static readonly DoomString GOTROCKET = new DoomString("Picked up a rocket."); + public static readonly DoomString GOTROCKBOX = new DoomString("Picked up a box of rockets."); + public static readonly DoomString GOTCELL = new DoomString("Picked up an energy cell."); + public static readonly DoomString GOTCELLBOX = new DoomString("Picked up an energy cell pack."); + public static readonly DoomString GOTSHELLS = new DoomString("Picked up 4 shotgun shells."); + public static readonly DoomString GOTSHELLBOX = new DoomString("Picked up a box of shotgun shells."); + public static readonly DoomString GOTBACKPACK = new DoomString("Picked up a backpack full of ammo!"); + public static readonly DoomString GOTBFG9000 = new DoomString("You got the BFG9000! Oh, yes."); + public static readonly DoomString GOTCHAINGUN = new DoomString("You got the chaingun!"); + public static readonly DoomString GOTCHAINSAW = new DoomString("A chainsaw! Find some meat!"); + public static readonly DoomString GOTLAUNCHER = new DoomString("You got the rocket launcher!"); + public static readonly DoomString GOTPLASMA = new DoomString("You got the plasma gun!"); + public static readonly DoomString GOTSHOTGUN = new DoomString("You got the shotgun!"); + public static readonly DoomString GOTSHOTGUN2 = new DoomString("You got the super shotgun!"); + public static readonly DoomString PD_BLUEO = new DoomString("You need a blue key to activate this object"); + public static readonly DoomString PD_REDO = new DoomString("You need a red key to activate this object"); + public static readonly DoomString PD_YELLOWO = new DoomString("You need a yellow key to activate this object"); + public static readonly DoomString PD_BLUEK = new DoomString("You need a blue key to open this door"); + public static readonly DoomString PD_REDK = new DoomString("You need a red key to open this door"); + public static readonly DoomString PD_YELLOWK = new DoomString("You need a yellow key to open this door"); + public static readonly DoomString GGSAVED = new DoomString("game saved."); + public static readonly DoomString HUSTR_E1M1 = new DoomString("E1M1: Hangar"); + public static readonly DoomString HUSTR_E1M2 = new DoomString("E1M2: Nuclear Plant"); + public static readonly DoomString HUSTR_E1M3 = new DoomString("E1M3: Toxin Refinery"); + public static readonly DoomString HUSTR_E1M4 = new DoomString("E1M4: Command Control"); + public static readonly DoomString HUSTR_E1M5 = new DoomString("E1M5: Phobos Lab"); + public static readonly DoomString HUSTR_E1M6 = new DoomString("E1M6: Central Processing"); + public static readonly DoomString HUSTR_E1M7 = new DoomString("E1M7: Computer Station"); + public static readonly DoomString HUSTR_E1M8 = new DoomString("E1M8: Phobos Anomaly"); + public static readonly DoomString HUSTR_E1M9 = new DoomString("E1M9: Military Base"); + public static readonly DoomString HUSTR_E2M1 = new DoomString("E2M1: Deimos Anomaly"); + public static readonly DoomString HUSTR_E2M2 = new DoomString("E2M2: Containment Area"); + public static readonly DoomString HUSTR_E2M3 = new DoomString("E2M3: Refinery"); + public static readonly DoomString HUSTR_E2M4 = new DoomString("E2M4: Deimos Lab"); + public static readonly DoomString HUSTR_E2M5 = new DoomString("E2M5: Command Center"); + public static readonly DoomString HUSTR_E2M6 = new DoomString("E2M6: Halls of the Damned"); + public static readonly DoomString HUSTR_E2M7 = new DoomString("E2M7: Spawning Vats"); + public static readonly DoomString HUSTR_E2M8 = new DoomString("E2M8: Tower of Babel"); + public static readonly DoomString HUSTR_E2M9 = new DoomString("E2M9: Fortress of Mystery"); + public static readonly DoomString HUSTR_E3M1 = new DoomString("E3M1: Hell Keep"); + public static readonly DoomString HUSTR_E3M2 = new DoomString("E3M2: Slough of Despair"); + public static readonly DoomString HUSTR_E3M3 = new DoomString("E3M3: Pandemonium"); + public static readonly DoomString HUSTR_E3M4 = new DoomString("E3M4: House of Pain"); + public static readonly DoomString HUSTR_E3M5 = new DoomString("E3M5: Unholy Cathedral"); + public static readonly DoomString HUSTR_E3M6 = new DoomString("E3M6: Mt. Erebus"); + public static readonly DoomString HUSTR_E3M7 = new DoomString("E3M7: Limbo"); + public static readonly DoomString HUSTR_E3M8 = new DoomString("E3M8: Dis"); + public static readonly DoomString HUSTR_E3M9 = new DoomString("E3M9: Warrens"); + public static readonly DoomString HUSTR_E4M1 = new DoomString("E4M1: Hell Beneath"); + public static readonly DoomString HUSTR_E4M2 = new DoomString("E4M2: Perfect Hatred"); + public static readonly DoomString HUSTR_E4M3 = new DoomString("E4M3: Sever The Wicked"); + public static readonly DoomString HUSTR_E4M4 = new DoomString("E4M4: Unruly Evil"); + public static readonly DoomString HUSTR_E4M5 = new DoomString("E4M5: They Will Repent"); + public static readonly DoomString HUSTR_E4M6 = new DoomString("E4M6: Against Thee Wickedly"); + public static readonly DoomString HUSTR_E4M7 = new DoomString("E4M7: And Hell Followed"); + public static readonly DoomString HUSTR_E4M8 = new DoomString("E4M8: Unto The Cruel"); + public static readonly DoomString HUSTR_E4M9 = new DoomString("E4M9: Fear"); + public static readonly DoomString HUSTR_1 = new DoomString("level 1: entryway"); + public static readonly DoomString HUSTR_2 = new DoomString("level 2: underhalls"); + public static readonly DoomString HUSTR_3 = new DoomString("level 3: the gantlet"); + public static readonly DoomString HUSTR_4 = new DoomString("level 4: the focus"); + public static readonly DoomString HUSTR_5 = new DoomString("level 5: the waste tunnels"); + public static readonly DoomString HUSTR_6 = new DoomString("level 6: the crusher"); + public static readonly DoomString HUSTR_7 = new DoomString("level 7: dead simple"); + public static readonly DoomString HUSTR_8 = new DoomString("level 8: tricks and traps"); + public static readonly DoomString HUSTR_9 = new DoomString("level 9: the pit"); + public static readonly DoomString HUSTR_10 = new DoomString("level 10: refueling base"); + public static readonly DoomString HUSTR_11 = new DoomString("level 11: 'o' of destruction!"); + public static readonly DoomString HUSTR_12 = new DoomString("level 12: the factory"); + public static readonly DoomString HUSTR_13 = new DoomString("level 13: downtown"); + public static readonly DoomString HUSTR_14 = new DoomString("level 14: the inmost dens"); + public static readonly DoomString HUSTR_15 = new DoomString("level 15: industrial zone"); + public static readonly DoomString HUSTR_16 = new DoomString("level 16: suburbs"); + public static readonly DoomString HUSTR_17 = new DoomString("level 17: tenements"); + public static readonly DoomString HUSTR_18 = new DoomString("level 18: the courtyard"); + public static readonly DoomString HUSTR_19 = new DoomString("level 19: the citadel"); + public static readonly DoomString HUSTR_20 = new DoomString("level 20: gotcha!"); + public static readonly DoomString HUSTR_21 = new DoomString("level 21: nirvana"); + public static readonly DoomString HUSTR_22 = new DoomString("level 22: the catacombs"); + public static readonly DoomString HUSTR_23 = new DoomString("level 23: barrels o' fun"); + public static readonly DoomString HUSTR_24 = new DoomString("level 24: the chasm"); + public static readonly DoomString HUSTR_25 = new DoomString("level 25: bloodfalls"); + public static readonly DoomString HUSTR_26 = new DoomString("level 26: the abandoned mines"); + public static readonly DoomString HUSTR_27 = new DoomString("level 27: monster condo"); + public static readonly DoomString HUSTR_28 = new DoomString("level 28: the spirit world"); + public static readonly DoomString HUSTR_29 = new DoomString("level 29: the living end"); + public static readonly DoomString HUSTR_30 = new DoomString("level 30: icon of sin"); + public static readonly DoomString HUSTR_31 = new DoomString("level 31: wolfenstein"); + public static readonly DoomString HUSTR_32 = new DoomString("level 32: grosse"); + public static readonly DoomString PHUSTR_1 = new DoomString("level 1: congo"); + public static readonly DoomString PHUSTR_2 = new DoomString("level 2: well of souls"); + public static readonly DoomString PHUSTR_3 = new DoomString("level 3: aztec"); + public static readonly DoomString PHUSTR_4 = new DoomString("level 4: caged"); + public static readonly DoomString PHUSTR_5 = new DoomString("level 5: ghost town"); + public static readonly DoomString PHUSTR_6 = new DoomString("level 6: baron's lair"); + public static readonly DoomString PHUSTR_7 = new DoomString("level 7: caughtyard"); + public static readonly DoomString PHUSTR_8 = new DoomString("level 8: realm"); + public static readonly DoomString PHUSTR_9 = new DoomString("level 9: abattoire"); + public static readonly DoomString PHUSTR_10 = new DoomString("level 10: onslaught"); + public static readonly DoomString PHUSTR_11 = new DoomString("level 11: hunted"); + public static readonly DoomString PHUSTR_12 = new DoomString("level 12: speed"); + public static readonly DoomString PHUSTR_13 = new DoomString("level 13: the crypt"); + public static readonly DoomString PHUSTR_14 = new DoomString("level 14: genesis"); + public static readonly DoomString PHUSTR_15 = new DoomString("level 15: the twilight"); + public static readonly DoomString PHUSTR_16 = new DoomString("level 16: the omen"); + public static readonly DoomString PHUSTR_17 = new DoomString("level 17: compound"); + public static readonly DoomString PHUSTR_18 = new DoomString("level 18: neurosphere"); + public static readonly DoomString PHUSTR_19 = new DoomString("level 19: nme"); + public static readonly DoomString PHUSTR_20 = new DoomString("level 20: the death domain"); + public static readonly DoomString PHUSTR_21 = new DoomString("level 21: slayer"); + public static readonly DoomString PHUSTR_22 = new DoomString("level 22: impossible mission"); + public static readonly DoomString PHUSTR_23 = new DoomString("level 23: tombstone"); + public static readonly DoomString PHUSTR_24 = new DoomString("level 24: the final frontier"); + public static readonly DoomString PHUSTR_25 = new DoomString("level 25: the temple of darkness"); + public static readonly DoomString PHUSTR_26 = new DoomString("level 26: bunker"); + public static readonly DoomString PHUSTR_27 = new DoomString("level 27: anti-christ"); + public static readonly DoomString PHUSTR_28 = new DoomString("level 28: the sewers"); + public static readonly DoomString PHUSTR_29 = new DoomString("level 29: odyssey of noises"); + public static readonly DoomString PHUSTR_30 = new DoomString("level 30: the gateway of hell"); + public static readonly DoomString PHUSTR_31 = new DoomString("level 31: cyberden"); + public static readonly DoomString PHUSTR_32 = new DoomString("level 32: go 2 it"); + public static readonly DoomString THUSTR_1 = new DoomString("level 1: system control"); + public static readonly DoomString THUSTR_2 = new DoomString("level 2: human bbq"); + public static readonly DoomString THUSTR_3 = new DoomString("level 3: power control"); + public static readonly DoomString THUSTR_4 = new DoomString("level 4: wormhole"); + public static readonly DoomString THUSTR_5 = new DoomString("level 5: hanger"); + public static readonly DoomString THUSTR_6 = new DoomString("level 6: open season"); + public static readonly DoomString THUSTR_7 = new DoomString("level 7: prison"); + public static readonly DoomString THUSTR_8 = new DoomString("level 8: metal"); + public static readonly DoomString THUSTR_9 = new DoomString("level 9: stronghold"); + public static readonly DoomString THUSTR_10 = new DoomString("level 10: redemption"); + public static readonly DoomString THUSTR_11 = new DoomString("level 11: storage facility"); + public static readonly DoomString THUSTR_12 = new DoomString("level 12: crater"); + public static readonly DoomString THUSTR_13 = new DoomString("level 13: nukage processing"); + public static readonly DoomString THUSTR_14 = new DoomString("level 14: steel works"); + public static readonly DoomString THUSTR_15 = new DoomString("level 15: dead zone"); + public static readonly DoomString THUSTR_16 = new DoomString("level 16: deepest reaches"); + public static readonly DoomString THUSTR_17 = new DoomString("level 17: processing area"); + public static readonly DoomString THUSTR_18 = new DoomString("level 18: mill"); + public static readonly DoomString THUSTR_19 = new DoomString("level 19: shipping/respawning"); + public static readonly DoomString THUSTR_20 = new DoomString("level 20: central processing"); + public static readonly DoomString THUSTR_21 = new DoomString("level 21: administration center"); + public static readonly DoomString THUSTR_22 = new DoomString("level 22: habitat"); + public static readonly DoomString THUSTR_23 = new DoomString("level 23: lunar mining project"); + public static readonly DoomString THUSTR_24 = new DoomString("level 24: quarry"); + public static readonly DoomString THUSTR_25 = new DoomString("level 25: baron's den"); + public static readonly DoomString THUSTR_26 = new DoomString("level 26: ballistyx"); + public static readonly DoomString THUSTR_27 = new DoomString("level 27: mount pain"); + public static readonly DoomString THUSTR_28 = new DoomString("level 28: heck"); + public static readonly DoomString THUSTR_29 = new DoomString("level 29: river styx"); + public static readonly DoomString THUSTR_30 = new DoomString("level 30: last call"); + public static readonly DoomString THUSTR_31 = new DoomString("level 31: pharaoh"); + public static readonly DoomString THUSTR_32 = new DoomString("level 32: caribbean"); + public static readonly DoomString AMSTR_FOLLOWON = new DoomString("Follow Mode ON"); + public static readonly DoomString AMSTR_FOLLOWOFF = new DoomString("Follow Mode OFF"); + public static readonly DoomString AMSTR_GRIDON = new DoomString("Grid ON"); + public static readonly DoomString AMSTR_GRIDOFF = new DoomString("Grid OFF"); + public static readonly DoomString AMSTR_MARKEDSPOT = new DoomString("Marked Spot"); + public static readonly DoomString AMSTR_MARKSCLEARED = new DoomString("All Marks Cleared"); + public static readonly DoomString STSTR_MUS = new DoomString("Music Change"); + public static readonly DoomString STSTR_NOMUS = new DoomString("IMPOSSIBLE SELECTION"); + public static readonly DoomString STSTR_DQDON = new DoomString("Degreelessness Mode On"); + public static readonly DoomString STSTR_DQDOFF = new DoomString("Degreelessness Mode Off"); + public static readonly DoomString STSTR_KFAADDED = new DoomString("Very Happy Ammo Added"); + public static readonly DoomString STSTR_FAADDED = new DoomString("Ammo (no keys) Added"); + public static readonly DoomString STSTR_NCON = new DoomString("No Clipping Mode ON"); + public static readonly DoomString STSTR_NCOFF = new DoomString("No Clipping Mode OFF"); + public static readonly DoomString STSTR_BEHOLD = new DoomString("inVuln, Str, Inviso, Rad, Allmap, or Lite-amp"); + public static readonly DoomString STSTR_BEHOLDX = new DoomString("Power-up Toggled"); + public static readonly DoomString STSTR_CHOPPERS = new DoomString("... doesn't suck - GM"); + public static readonly DoomString STSTR_CLEV = new DoomString("Changing Level..."); + + public static readonly DoomString E1TEXT = new DoomString( + "Once you beat the big badasses and\n" + + "clean out the moon base you're supposed\n" + + "to win, aren't you? Aren't you? Where's\n" + + "your fat reward and ticket home? What\n" + + "the hell is this? It's not supposed to\n" + + "end this way!\n" + + "\n" + + "It stinks like rotten meat, but looks\n" + + "like the lost Deimos base. Looks like\n" + + "you're stuck on The Shores of Hell.\n" + + "The only way out is through.\n" + + "\n" + + "To continue the DOOM experience, play\n" + + "The Shores of Hell and its amazing\n" + + "sequel, Inferno!\n"); + + public static readonly DoomString E2TEXT = new DoomString( + "You've done it! The hideous cyber-\n" + + "demon lord that ruled the lost Deimos\n" + + "moon base has been slain and you\n" + + "are triumphant! But ... where are\n" + + "you? You clamber to the edge of the\n" + + "moon and look down to see the awful\n" + + "truth.\n" + + "\n" + + "Deimos floats above Hell itself!\n" + + "You've never heard of anyone escaping\n" + + "from Hell, but you'll make the bastards\n" + + "sorry they ever heard of you! Quickly,\n" + + "you rappel down to the surface of\n" + + "Hell.\n" + + "\n" + + "Now, it's on to the final chapter of\n" + + "DOOM! -- Inferno."); + + public static readonly DoomString E3TEXT = new DoomString( + "The loathsome spiderdemon that\n" + + "masterminded the invasion of the moon\n" + + "bases and caused so much death has had\n" + + "its ass kicked for all time.\n" + + "\n" + + "A hidden doorway opens and you enter.\n" + + "You've proven too tough for Hell to\n" + + "contain, and now Hell at last plays\n" + + "fair -- for you emerge from the door\n" + + "to see the green fields of Earth!\n" + + "Home at last.\n" + + "\n" + + "You wonder what's been happening on\n" + + "Earth while you were battling evil\n" + + "unleashed. It's good that no Hell-\n" + + "spawn could have come through that\n" + + "door with you ..."); + + public static readonly DoomString E4TEXT = new DoomString( + "the spider mastermind must have sent forth\n" + + "its legions of hellspawn before your\n" + + "final confrontation with that terrible\n" + + "beast from hell. but you stepped forward\n" + + "and brought forth eternal damnation and\n" + + "suffering upon the horde as a true hero\n" + + "would in the face of something so evil.\n" + + "\n" + + "besides, someone was gonna pay for what\n" + + "happened to daisy, your pet rabbit.\n" + + "\n" + + "but now, you see spread before you more\n" + + "potential pain and gibbitude as a nation\n" + + "of demons run amok among our cities.\n" + + "\n" + + "next stop, hell on earth!"); + + public static readonly DoomString C1TEXT = new DoomString( + "YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" + + "STARPORT. BUT SOMETHING IS WRONG. THE\n" + + "MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" + + "WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" + + "IS BEING SUBVERTED BY THEIR PRESENCE.\n" + + "\n" + + "AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" + + "FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" + + "YOU CAN PENETRATE INTO THE HAUNTED HEART\n" + + "OF THE STARBASE AND FIND THE CONTROLLING\n" + + "SWITCH WHICH HOLDS EARTH'S POPULATION\n" + + "HOSTAGE."); + + public static readonly DoomString C2TEXT = new DoomString( + "YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" + + "HUMANKIND TO EVACUATE EARTH AND ESCAPE\n" + + "THE NIGHTMARE. NOW YOU ARE THE ONLY\n" + + "HUMAN LEFT ON THE FACE OF THE PLANET.\n" + + "CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n" + + "AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n" + + "YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n" + + "THAT YOU HAVE SAVED YOUR SPECIES.\n" + + "\n" + + "BUT THEN, EARTH CONTROL BEAMS DOWN A\n" + + "MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n" + + "THE SOURCE OF THE ALIEN INVASION. IF YOU\n" + + "GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n" + + "ENTRY. THE ALIEN BASE IS IN THE HEART OF\n" + + "YOUR OWN HOME CITY, NOT FAR FROM THE\n" + + "STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n" + + "UP AND RETURN TO THE FRAY."); + + public static readonly DoomString C3TEXT = new DoomString( + "YOU ARE AT THE CORRUPT HEART OF THE CITY,\n" + + "SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n" + + "YOU SEE NO WAY TO DESTROY THE CREATURES'\n" + + "ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n" + + "TEETH AND PLUNGE THROUGH IT.\n" + + "\n" + + "THERE MUST BE A WAY TO CLOSE IT ON THE\n" + + "OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n" + + "GOT TO GO THROUGH HELL TO GET TO IT?"); + + public static readonly DoomString C4TEXT = new DoomString( + "THE HORRENDOUS VISAGE OF THE BIGGEST\n" + + "DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n" + + "YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n" + + "HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n" + + "UP AND DIES, ITS THRASHING LIMBS\n" + + "DEVASTATING UNTOLD MILES OF HELL'S\n" + + "SURFACE.\n" + + "\n" + + "YOU'VE DONE IT. THE INVASION IS OVER.\n" + + "EARTH IS SAVED. HELL IS A WRECK. YOU\n" + + "WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n" + + "DIE, NOW. WIPING THE SWEAT FROM YOUR\n" + + "FOREHEAD YOU BEGIN THE LONG TREK BACK\n" + + "HOME. REBUILDING EARTH OUGHT TO BE A\n" + + "LOT MORE FUN THAN RUINING IT WAS.\n"); + + public static readonly DoomString C5TEXT = new DoomString( + "CONGRATULATIONS, YOU'VE FOUND THE SECRET\n" + + "LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n" + + "HUMANS, RATHER THAN DEMONS. YOU WONDER\n" + + "WHO THE INMATES OF THIS CORNER OF HELL\n" + + "WILL BE."); + + public static readonly DoomString C6TEXT = new DoomString( + "CONGRATULATIONS, YOU'VE FOUND THE\n" + + "SUPER SECRET LEVEL! YOU'D BETTER\n" + + "BLAZE THROUGH THIS ONE!\n"); + + public static readonly DoomString P1TEXT = new DoomString( + "You gloat over the steaming carcass of the\n" + + "Guardian. With its death, you've wrested\n" + + "the Accelerator from the stinking claws\n" + + "of Hell. You relax and glance around the\n" + + "room. Damn! There was supposed to be at\n" + + "least one working prototype, but you can't\n" + + "see it. The demons must have taken it.\n" + + "\n" + + "You must find the prototype, or all your\n" + + "struggles will have been wasted. Keep\n" + + "moving, keep fighting, keep killing.\n" + + "Oh yes, keep living, too."); + + public static readonly DoomString P2TEXT = new DoomString( + "Even the deadly Arch-Vile labyrinth could\n" + + "not stop you, and you've gotten to the\n" + + "prototype Accelerator which is soon\n" + + "efficiently and permanently deactivated.\n" + + "\n" + + "You're good at that kind of thing."); + + public static readonly DoomString P3TEXT = new DoomString( + "You've bashed and battered your way into\n" + + "the heart of the devil-hive. Time for a\n" + + "Search-and-Destroy mission, aimed at the\n" + + "Gatekeeper, whose foul offspring is\n" + + "cascading to Earth. Yeah, he's bad. But\n" + + "you know who's worse!\n" + + "\n" + + "Grinning evilly, you check your gear, and\n" + + "get ready to give the bastard a little Hell\n" + + "of your own making!"); + + public static readonly DoomString P4TEXT = new DoomString( + "The Gatekeeper's evil face is splattered\n" + + "all over the place. As its tattered corpse\n" + + "collapses, an inverted Gate forms and\n" + + "sucks down the shards of the last\n" + + "prototype Accelerator, not to mention the\n" + + "few remaining demons. You're done. Hell\n" + + "has gone back to pounding bad dead folks \n" + + "instead of good live ones. Remember to\n" + + "tell your grandkids to put a rocket\n" + + "launcher in your coffin. If you go to Hell\n" + + "when you die, you'll need it for some\n" + + "final cleaning-up ..."); + + public static readonly DoomString P5TEXT = new DoomString( + "You've found the second-hardest level we\n" + + "got. Hope you have a saved game a level or\n" + + "two previous. If not, be prepared to die\n" + + "aplenty. For master marines only."); + + public static readonly DoomString P6TEXT = new DoomString( + "Betcha wondered just what WAS the hardest\n" + + "level we had ready for ya? Now you know.\n" + + "No one gets out alive."); + + public static readonly DoomString T1TEXT = new DoomString( + "You've fought your way out of the infested\n" + + "experimental labs. It seems that UAC has\n" + + "once again gulped it down. With their\n" + + "high turnover, it must be hard for poor\n" + + "old UAC to buy corporate health insurance\n" + + "nowadays..\n" + + "\n" + + "Ahead lies the military complex, now\n" + + "swarming with diseased horrors hot to get\n" + + "their teeth into you. With luck, the\n" + + "complex still has some warlike ordnance\n" + + "laying around."); + + public static readonly DoomString T2TEXT = new DoomString( + "You hear the grinding of heavy machinery\n" + + "ahead. You sure hope they're not stamping\n" + + "out new hellspawn, but you're ready to\n" + + "ream out a whole herd if you have to.\n" + + "They might be planning a blood feast, but\n" + + "you feel about as mean as two thousand\n" + + "maniacs packed into one mad killer.\n" + + "\n" + + "You don't plan to go down easy."); + + public static readonly DoomString T3TEXT = new DoomString( + "The vista opening ahead looks real damn\n" + + "familiar. Smells familiar, too -- like\n" + + "fried excrement. You didn't like this\n" + + "place before, and you sure as hell ain't\n" + + "planning to like it now. The more you\n" + + "brood on it, the madder you get.\n" + + "Hefting your gun, an evil grin trickles\n" + + "onto your face. Time to take some names."); + + public static readonly DoomString T4TEXT = new DoomString( + "Suddenly, all is silent, from one horizon\n" + + "to the other. The agonizing echo of Hell\n" + + "fades away, the nightmare sky turns to\n" + + "blue, the heaps of monster corpses start \n" + + "to evaporate along with the evil stench \n" + + "that filled the air. Jeeze, maybe you've\n" + + "done it. Have you really won?\n" + + "\n" + + "Something rumbles in the distance.\n" + + "A blue light begins to glow inside the\n" + + "ruined skull of the demon-spitter."); + + public static readonly DoomString T5TEXT = new DoomString( + "What now? Looks totally different. Kind\n" + + "of like King Tut's condo. Well,\n" + + "whatever's here can't be any worse\n" + + "than usual. Can it? Or maybe it's best\n" + + "to let sleeping gods lie.."); + + public static readonly DoomString T6TEXT = new DoomString( + "Time for a vacation. You've burst the\n" + + "bowels of hell and by golly you're ready\n" + + "for a break. You mutter to yourself,\n" + + "Maybe someone else can kick Hell's ass\n" + + "next time around. Ahead lies a quiet town,\n" + + "with peaceful flowing water, quaint\n" + + "buildings, and presumably no Hellspawn.\n" + + "\n" + + "As you step off the transport, you hear\n" + + "the stomp of a cyberdemon's iron shoe."); + + public static readonly DoomString CC_ZOMBIE = new DoomString("ZOMBIEMAN"); + public static readonly DoomString CC_SHOTGUN = new DoomString("SHOTGUN GUY"); + public static readonly DoomString CC_HEAVY = new DoomString("HEAVY WEAPON DUDE"); + public static readonly DoomString CC_IMP = new DoomString("IMP"); + public static readonly DoomString CC_DEMON = new DoomString("DEMON"); + public static readonly DoomString CC_LOST = new DoomString("LOST SOUL"); + public static readonly DoomString CC_CACO = new DoomString("CACODEMON"); + public static readonly DoomString CC_HELL = new DoomString("HELL KNIGHT"); + public static readonly DoomString CC_BARON = new DoomString("BARON OF HELL"); + public static readonly DoomString CC_ARACH = new DoomString("ARACHNOTRON"); + public static readonly DoomString CC_PAIN = new DoomString("PAIN ELEMENTAL"); + public static readonly DoomString CC_REVEN = new DoomString("REVENANT"); + public static readonly DoomString CC_MANCU = new DoomString("MANCUBUS"); + public static readonly DoomString CC_ARCH = new DoomString("ARCH-VILE"); + public static readonly DoomString CC_SPIDER = new DoomString("THE SPIDER MASTERMIND"); + public static readonly DoomString CC_CYBER = new DoomString("THE CYBERDEMON"); + public static readonly DoomString CC_HERO = new DoomString("OUR HERO"); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.SwitchNames.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.SwitchNames.cs new file mode 100644 index 00000000..a30d12a1 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.SwitchNames.cs @@ -0,0 +1,68 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static readonly Tuple[] SwitchNames = new Tuple[] + { + Tuple.Create(new DoomString("SW1BRCOM"), new DoomString("SW2BRCOM")), + Tuple.Create(new DoomString("SW1BRN1"), new DoomString("SW2BRN1")), + Tuple.Create(new DoomString("SW1BRN2"), new DoomString("SW2BRN2")), + Tuple.Create(new DoomString("SW1BRNGN"), new DoomString("SW2BRNGN")), + Tuple.Create(new DoomString("SW1BROWN"), new DoomString("SW2BROWN")), + Tuple.Create(new DoomString("SW1COMM"), new DoomString("SW2COMM")), + Tuple.Create(new DoomString("SW1COMP"), new DoomString("SW2COMP")), + Tuple.Create(new DoomString("SW1DIRT"), new DoomString("SW2DIRT")), + Tuple.Create(new DoomString("SW1EXIT"), new DoomString("SW2EXIT")), + Tuple.Create(new DoomString("SW1GRAY"), new DoomString("SW2GRAY")), + Tuple.Create(new DoomString("SW1GRAY1"), new DoomString("SW2GRAY1")), + Tuple.Create(new DoomString("SW1METAL"), new DoomString("SW2METAL")), + Tuple.Create(new DoomString("SW1PIPE"), new DoomString("SW2PIPE")), + Tuple.Create(new DoomString("SW1SLAD"), new DoomString("SW2SLAD")), + Tuple.Create(new DoomString("SW1STARG"), new DoomString("SW2STARG")), + Tuple.Create(new DoomString("SW1STON1"), new DoomString("SW2STON1")), + Tuple.Create(new DoomString("SW1STON2"), new DoomString("SW2STON2")), + Tuple.Create(new DoomString("SW1STONE"), new DoomString("SW2STONE")), + Tuple.Create(new DoomString("SW1STRTN"), new DoomString("SW2STRTN")), + Tuple.Create(new DoomString("SW1BLUE"), new DoomString("SW2BLUE")), + Tuple.Create(new DoomString("SW1CMT"), new DoomString("SW2CMT")), + Tuple.Create(new DoomString("SW1GARG"), new DoomString("SW2GARG")), + Tuple.Create(new DoomString("SW1GSTON"), new DoomString("SW2GSTON")), + Tuple.Create(new DoomString("SW1HOT"), new DoomString("SW2HOT")), + Tuple.Create(new DoomString("SW1LION"), new DoomString("SW2LION")), + Tuple.Create(new DoomString("SW1SATYR"), new DoomString("SW2SATYR")), + Tuple.Create(new DoomString("SW1SKIN"), new DoomString("SW2SKIN")), + Tuple.Create(new DoomString("SW1VINE"), new DoomString("SW2VINE")), + Tuple.Create(new DoomString("SW1WOOD"), new DoomString("SW2WOOD")), + Tuple.Create(new DoomString("SW1PANEL"), new DoomString("SW2PANEL")), + Tuple.Create(new DoomString("SW1ROCK"), new DoomString("SW2ROCK")), + Tuple.Create(new DoomString("SW1MET2"), new DoomString("SW2MET2")), + Tuple.Create(new DoomString("SW1WDMET"), new DoomString("SW2WDMET")), + Tuple.Create(new DoomString("SW1BRIK"), new DoomString("SW2BRIK")), + Tuple.Create(new DoomString("SW1MOD1"), new DoomString("SW2MOD1")), + Tuple.Create(new DoomString("SW1ZIM"), new DoomString("SW2ZIM")), + Tuple.Create(new DoomString("SW1STON6"), new DoomString("SW2STON6")), + Tuple.Create(new DoomString("SW1TEK"), new DoomString("SW2TEK")), + Tuple.Create(new DoomString("SW1MARB"), new DoomString("SW2MARB")), + Tuple.Create(new DoomString("SW1SKULL"), new DoomString("SW2SKULL")) + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.TextureAnimation.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.TextureAnimation.cs new file mode 100644 index 00000000..06dee610 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.TextureAnimation.cs @@ -0,0 +1,55 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static readonly AnimationDef[] TextureAnimation = new AnimationDef[] + { + new AnimationDef(false, "NUKAGE3", "NUKAGE1", 8), + new AnimationDef(false, "FWATER4", "FWATER1", 8), + new AnimationDef(false, "SWATER4", "SWATER1", 8), + new AnimationDef(false, "LAVA4", "LAVA1", 8), + new AnimationDef(false, "BLOOD3", "BLOOD1", 8), + + // DOOM II flat animations. + new AnimationDef(false, "RROCK08", "RROCK05", 8), + new AnimationDef(false, "SLIME04", "SLIME01", 8), + new AnimationDef(false, "SLIME08", "SLIME05", 8), + new AnimationDef(false, "SLIME12", "SLIME09", 8), + + new AnimationDef(true, "BLODGR4", "BLODGR1", 8), + new AnimationDef(true, "SLADRIP3", "SLADRIP1", 8), + + new AnimationDef(true, "BLODRIP4", "BLODRIP1", 8), + new AnimationDef(true, "FIREWALL", "FIREWALA", 8), + new AnimationDef(true, "GSTFONT3", "GSTFONT1", 8), + new AnimationDef(true, "FIRELAVA", "FIRELAV3", 8), + new AnimationDef(true, "FIREMAG3", "FIREMAG1", 8), + new AnimationDef(true, "FIREBLU2", "FIREBLU1", 8), + new AnimationDef(true, "ROCKRED3", "ROCKRED1", 8), + + new AnimationDef(true, "BFALL4", "BFALL1", 8), + new AnimationDef(true, "SFALL4", "SFALL1", 8), + new AnimationDef(true, "WFALL4", "WFALL1", 8), + new AnimationDef(true, "DBRAIN4", "DBRAIN1", 8) + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.WeaponInfos.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.WeaponInfos.cs new file mode 100644 index 00000000..b5faf0e0 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.WeaponInfos.cs @@ -0,0 +1,117 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + public static readonly WeaponInfo[] WeaponInfos = new WeaponInfo[] + { + // fist + new WeaponInfo( + AmmoType.NoAmmo, + MobjState.Punchup, + MobjState.Punchdown, + MobjState.Punch, + MobjState.Punch1, + MobjState.Null + ), + + // pistol + new WeaponInfo( + AmmoType.Clip, + MobjState.Pistolup, + MobjState.Pistoldown, + MobjState.Pistol, + MobjState.Pistol1, + MobjState.Pistolflash + ), + + // shotgun + new WeaponInfo( + AmmoType.Shell, + MobjState.Sgunup, + MobjState.Sgundown, + MobjState.Sgun, + MobjState.Sgun1, + MobjState.Sgunflash1 + ), + + // chaingun + new WeaponInfo( + AmmoType.Clip, + MobjState.Chainup, + MobjState.Chaindown, + MobjState.Chain, + MobjState.Chain1, + MobjState.Chainflash1 + ), + + // missile launcher + new WeaponInfo( + AmmoType.Missile, + MobjState.Missileup, + MobjState.Missiledown, + MobjState.Missile, + MobjState.Missile1, + MobjState.Missileflash1 + ), + + // plasma rifle + new WeaponInfo( + AmmoType.Cell, + MobjState.Plasmaup, + MobjState.Plasmadown, + MobjState.Plasma, + MobjState.Plasma1, + MobjState.Plasmaflash1 + ), + + // bfg 9000 + new WeaponInfo( + AmmoType.Cell, + MobjState.Bfgup, + MobjState.Bfgdown, + MobjState.Bfg, + MobjState.Bfg1, + MobjState.Bfgflash1 + ), + + // chainsaw + new WeaponInfo( + AmmoType.NoAmmo, + MobjState.Sawup, + MobjState.Sawdown, + MobjState.Saw, + MobjState.Saw1, + MobjState.Null + ), + + // // super shotgun + new WeaponInfo( + AmmoType.Shell, + MobjState.Dsgunup, + MobjState.Dsgundown, + MobjState.Dsgun, + MobjState.Dsgun1, + MobjState.Dsgunflash1 + ) + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.cs new file mode 100644 index 00000000..c7357429 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Info/DoomInfo.cs @@ -0,0 +1,30 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class DoomInfo + { + // This is to ensure the static members initialized. + public static void Initialize() + { + SpriteNames[0].Equals(null); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/Animation.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/Animation.cs new file mode 100644 index 00000000..7ebd4406 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/Animation.cs @@ -0,0 +1,135 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.IO; + +namespace ManagedDoom +{ + public sealed class Animation + { + private Intermission im; + private int number; + + private AnimationType type; + private int period; + private int frameCount; + private int locationX; + private int locationY; + private int data; + private string[] patches; + private int patchNumber; + private int nextTic; + + public Animation(Intermission intermission, AnimationInfo info, int number) + { + im = intermission; + this.number = number; + + type = info.Type; + period = info.Period; + frameCount = info.Count; + locationX = info.X; + locationY = info.Y; + data = info.Data; + + patches = new string[frameCount]; + for (var i = 0; i < frameCount; i++) + { + // MONDO HACK! + if (im.Info.Episode != 1 || number != 8) + { + patches[i] = "WIA" + im.Info.Episode + number.ToString("00") + i.ToString("00"); + } + else + { + // HACK ALERT! + patches[i] = "WIA104" + i.ToString("00"); + } + } + } + + public void Reset(int bgCount) + { + patchNumber = -1; + + // Specify the next time to draw it. + if (type == AnimationType.Always) + { + nextTic = bgCount + 1 + (im.Random.Next() % period); + } + else if (type == AnimationType.Random) + { + nextTic = bgCount + 1 + (im.Random.Next() % data); + } + else if (type == AnimationType.Level) + { + nextTic = bgCount + 1; + } + } + + public void Update(int bgCount) + { + if (bgCount == nextTic) + { + switch (type) + { + case AnimationType.Always: + if (++patchNumber >= frameCount) + { + patchNumber = 0; + } + nextTic = bgCount + period; + break; + + case AnimationType.Random: + patchNumber++; + if (patchNumber == frameCount) + { + patchNumber = -1; + nextTic = bgCount + (im.Random.Next() % data); + } + else + { + nextTic = bgCount + period; + } + break; + + case AnimationType.Level: + // Gawd-awful hack for level anims. + if (!(im.State == IntermissionState.StatCount && number == 7) && im.Info.NextLevel == Data) + { + patchNumber++; + if (patchNumber == frameCount) + { + patchNumber--; + } + nextTic = bgCount + period; + } + break; + } + } + } + + public int LocationX => locationX; + public int LocationY => locationY; + public int Data => data; + public IReadOnlyList Patches => patches; + public int PatchNumber => patchNumber; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/AnimationInfo.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/AnimationInfo.cs new file mode 100644 index 00000000..c2d402e1 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/AnimationInfo.cs @@ -0,0 +1,98 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class AnimationInfo + { + private AnimationType type; + private int period; + private int count; + private int x; + private int y; + private int data; + + public AnimationInfo(AnimationType type, int period, int count, int x, int y) + { + this.type = type; + this.period = period; + this.count = count; + this.x = x; + this.y = y; + } + + public AnimationInfo(AnimationType type, int period, int count, int x, int y, int data) + { + this.type = type; + this.period = period; + this.count = count; + this.x = x; + this.y = y; + this.data = data; + } + + public AnimationType Type => type; + public int Period => period; + public int Count => count; + public int X => x; + public int Y => y; + public int Data => data; + + public static readonly IReadOnlyList> Episodes = new AnimationInfo[][] + { + new AnimationInfo[] + { + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 224, 104), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 184, 160), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 112, 136), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 72, 112), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 88, 96), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 64, 48), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 192, 40), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 136, 16), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 80, 16), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 64, 24) + }, + + new AnimationInfo[] + { + new AnimationInfo(AnimationType.Level, GameConst.TicRate / 3, 1, 128, 136, 1), + new AnimationInfo(AnimationType.Level, GameConst.TicRate / 3, 1, 128, 136, 2), + new AnimationInfo(AnimationType.Level, GameConst.TicRate / 3, 1, 128, 136, 3), + new AnimationInfo(AnimationType.Level, GameConst.TicRate / 3, 1, 128, 136, 4), + new AnimationInfo(AnimationType.Level, GameConst.TicRate / 3, 1, 128, 136, 5), + new AnimationInfo(AnimationType.Level, GameConst.TicRate / 3, 1, 128, 136, 6), + new AnimationInfo(AnimationType.Level, GameConst.TicRate / 3, 1, 128, 136, 7), + new AnimationInfo(AnimationType.Level, GameConst.TicRate / 3, 3, 192, 144, 8), + new AnimationInfo(AnimationType.Level, GameConst.TicRate / 3, 1, 128, 136, 8) + }, + + new AnimationInfo[] + { + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 104, 168), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 40, 136), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 160, 96), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 104, 80), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 3, 3, 120, 32), + new AnimationInfo(AnimationType.Always, GameConst.TicRate / 4, 3, 40, 0) + } + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/AnimationType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/AnimationType.cs new file mode 100644 index 00000000..d9b89dfb --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/AnimationType.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum AnimationType + { + Always, + Random, + Level + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/Finale.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/Finale.cs new file mode 100644 index 00000000..8fd4a5f3 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/Finale.cs @@ -0,0 +1,566 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Finale + { + public static readonly int TextSpeed = 3; + public static readonly int TextWait = 250; + + private GameOptions options; + + // Stage of animation: + // 0 = text, 1 = art screen, 2 = character cast. + private int stage; + private int count; + + private string flat; + private string text; + + // For bunny scroll. + private int scrolled; + private bool showTheEnd; + private int theEndIndex; + + private UpdateResult updateResult; + + public Finale(GameOptions options) + { + this.options = options; + + string c1Text; + string c2Text; + string c3Text; + string c4Text; + string c5Text; + string c6Text; + switch (options.MissionPack) + { + case MissionPack.Plutonia: + c1Text = DoomInfo.Strings.P1TEXT; + c2Text = DoomInfo.Strings.P2TEXT; + c3Text = DoomInfo.Strings.P3TEXT; + c4Text = DoomInfo.Strings.P4TEXT; + c5Text = DoomInfo.Strings.P5TEXT; + c6Text = DoomInfo.Strings.P6TEXT; + break; + + case MissionPack.Tnt: + c1Text = DoomInfo.Strings.T1TEXT; + c2Text = DoomInfo.Strings.T2TEXT; + c3Text = DoomInfo.Strings.T3TEXT; + c4Text = DoomInfo.Strings.T4TEXT; + c5Text = DoomInfo.Strings.T5TEXT; + c6Text = DoomInfo.Strings.T6TEXT; + break; + + default: + c1Text = DoomInfo.Strings.C1TEXT; + c2Text = DoomInfo.Strings.C2TEXT; + c3Text = DoomInfo.Strings.C3TEXT; + c4Text = DoomInfo.Strings.C4TEXT; + c5Text = DoomInfo.Strings.C5TEXT; + c6Text = DoomInfo.Strings.C6TEXT; + break; + } + + switch (options.GameMode) + { + case GameMode.Shareware: + case GameMode.Registered: + case GameMode.Retail: + options.Music.StartMusic(Bgm.VICTOR, true); + switch (options.Episode) + { + case 1: + flat = "FLOOR4_8"; + text = DoomInfo.Strings.E1TEXT; + break; + + case 2: + flat = "SFLR6_1"; + text = DoomInfo.Strings.E2TEXT; + break; + + case 3: + flat = "MFLR8_4"; + text = DoomInfo.Strings.E3TEXT; + break; + + case 4: + flat = "MFLR8_3"; + text = DoomInfo.Strings.E4TEXT; + break; + + default: + break; + } + break; + + case GameMode.Commercial: + options.Music.StartMusic(Bgm.READ_M, true); + switch (options.Map) + { + case 6: + flat = "SLIME16"; + text = c1Text; + break; + + case 11: + flat = "RROCK14"; + text = c2Text; + break; + + case 20: + flat = "RROCK07"; + text = c3Text; + break; + + case 30: + flat = "RROCK17"; + text = c4Text; + break; + + case 15: + flat = "RROCK13"; + text = c5Text; + break; + + case 31: + flat = "RROCK19"; + text = c6Text; + break; + + default: + break; + } + break; + + default: + options.Music.StartMusic(Bgm.READ_M, true); + flat = "F_SKY1"; + text = DoomInfo.Strings.C1TEXT; + break; + } + + stage = 0; + count = 0; + + scrolled = 0; + showTheEnd = false; + theEndIndex = 0; + } + + public UpdateResult Update() + { + updateResult = UpdateResult.None; + + // Check for skipping. + if (options.GameMode == GameMode.Commercial && count > 50) + { + int i; + + // Go on to the next level. + for (i = 0; i < Player.MaxPlayerCount; i++) + { + if (options.Players[i].Cmd.Buttons != 0) + { + break; + } + } + + if (i < Player.MaxPlayerCount && stage != 2) + { + if (options.Map == 30) + { + StartCast(); + } + else + { + return UpdateResult.Completed; + } + } + } + + // Advance animation. + count++; + + if (stage == 2) + { + UpdateCast(); + return updateResult; + } + + if (options.GameMode == GameMode.Commercial) + { + return updateResult; + } + + if (stage == 0 && count > text.Length * TextSpeed + TextWait) + { + count = 0; + stage = 1; + updateResult = UpdateResult.NeedWipe; + if (options.Episode == 3) + { + options.Music.StartMusic(Bgm.BUNNY, true); + } + } + + if (stage == 1 && options.Episode == 3) + { + BunnyScroll(); + } + + return updateResult; + } + + private void BunnyScroll() + { + scrolled = 320 - (count - 230) / 2; + if (scrolled > 320) + { + scrolled = 320; + } + if (scrolled < 0) + { + scrolled = 0; + } + + if (count < 1130) + { + return; + } + + showTheEnd = true; + + if (count < 1180) + { + theEndIndex = 0; + return; + } + + var stage = (count - 1180) / 5; + if (stage > 6) + { + stage = 6; + } + if (stage > theEndIndex) + { + StartSound(Sfx.PISTOL); + theEndIndex = stage; + } + } + + + + private static readonly CastInfo[] castorder = new CastInfo[] + { + new CastInfo(DoomInfo.Strings.CC_ZOMBIE, MobjType.Possessed), + new CastInfo(DoomInfo.Strings.CC_SHOTGUN, MobjType.Shotguy), + new CastInfo(DoomInfo.Strings.CC_HEAVY, MobjType.Chainguy), + new CastInfo(DoomInfo.Strings.CC_IMP, MobjType.Troop), + new CastInfo(DoomInfo.Strings.CC_DEMON, MobjType.Sergeant), + new CastInfo(DoomInfo.Strings.CC_LOST, MobjType.Skull), + new CastInfo(DoomInfo.Strings.CC_CACO, MobjType.Head), + new CastInfo(DoomInfo.Strings.CC_HELL, MobjType.Knight), + new CastInfo(DoomInfo.Strings.CC_BARON, MobjType.Bruiser), + new CastInfo(DoomInfo.Strings.CC_ARACH, MobjType.Baby), + new CastInfo(DoomInfo.Strings.CC_PAIN, MobjType.Pain), + new CastInfo(DoomInfo.Strings.CC_REVEN, MobjType.Undead), + new CastInfo(DoomInfo.Strings.CC_MANCU, MobjType.Fatso), + new CastInfo(DoomInfo.Strings.CC_ARCH, MobjType.Vile), + new CastInfo(DoomInfo.Strings.CC_SPIDER, MobjType.Spider), + new CastInfo(DoomInfo.Strings.CC_CYBER, MobjType.Cyborg), + new CastInfo(DoomInfo.Strings.CC_HERO, MobjType.Player) + }; + + private int castNumber; + private MobjStateDef castState; + private int castTics; + private int castFrames; + private bool castDeath; + private bool castOnMelee; + private bool castAttacking; + + private void StartCast() + { + stage = 2; + + castNumber = 0; + castState = DoomInfo.States[(int)DoomInfo.MobjInfos[(int)castorder[castNumber].Type].SeeState]; + castTics = castState.Tics; + castFrames = 0; + castDeath = false; + castOnMelee = false; + castAttacking = false; + + updateResult = UpdateResult.NeedWipe; + + options.Music.StartMusic(Bgm.EVIL, true); + } + + private void UpdateCast() + { + if (--castTics > 0) + { + // Not time to change state yet. + return; + } + + if (castState.Tics == -1 || castState.Next == MobjState.Null) + { + // Switch from deathstate to next monster. + castNumber++; + castDeath = false; + if (castNumber == castorder.Length) + { + castNumber = 0; + } + if (DoomInfo.MobjInfos[(int)castorder[castNumber].Type].SeeSound != 0) + { + StartSound(DoomInfo.MobjInfos[(int)castorder[castNumber].Type].SeeSound); + } + castState = DoomInfo.States[(int)DoomInfo.MobjInfos[(int)castorder[castNumber].Type].SeeState]; + castFrames = 0; + } + else + { + // Just advance to next state in animation. + if (castState == DoomInfo.States[(int)MobjState.PlayAtk1]) + { + // Oh, gross hack! + castAttacking = false; + castState = DoomInfo.States[(int)DoomInfo.MobjInfos[(int)castorder[castNumber].Type].SeeState]; + castFrames = 0; + goto stopAttack; + } + var st = castState.Next; + castState = DoomInfo.States[(int)st]; + castFrames++; + + // Sound hacks.... + Sfx sfx; + switch (st) + { + case MobjState.PlayAtk1: + sfx = Sfx.DSHTGN; + break; + + case MobjState.PossAtk2: + sfx = Sfx.PISTOL; + break; + + case MobjState.SposAtk2: + sfx = Sfx.SHOTGN; + break; + + case MobjState.VileAtk2: + sfx = Sfx.VILATK; + break; + + case MobjState.SkelFist2: + sfx = Sfx.SKESWG; + break; + + case MobjState.SkelFist4: + sfx = Sfx.SKEPCH; + break; + + case MobjState.SkelMiss2: + sfx = Sfx.SKEATK; + break; + + case MobjState.FattAtk8: + case MobjState.FattAtk5: + case MobjState.FattAtk2: + sfx = Sfx.FIRSHT; + break; + + case MobjState.CposAtk2: + case MobjState.CposAtk3: + case MobjState.CposAtk4: + sfx = Sfx.SHOTGN; + break; + + case MobjState.TrooAtk3: + sfx = Sfx.CLAW; + break; + + case MobjState.SargAtk2: + sfx = Sfx.SGTATK; + break; + + case MobjState.BossAtk2: + case MobjState.Bos2Atk2: + case MobjState.HeadAtk2: + sfx = Sfx.FIRSHT; + break; + + case MobjState.SkullAtk2: + sfx = Sfx.SKLATK; + break; + + case MobjState.SpidAtk2: + case MobjState.SpidAtk3: + sfx = Sfx.SHOTGN; + break; + + case MobjState.BspiAtk2: + sfx = Sfx.PLASMA; + break; + + case MobjState.CyberAtk2: + case MobjState.CyberAtk4: + case MobjState.CyberAtk6: + sfx = Sfx.RLAUNC; + break; + + case MobjState.PainAtk3: + sfx = Sfx.SKLATK; + break; + + default: + sfx = 0; + break; + } + + if (sfx != 0) + { + StartSound(sfx); + } + } + + if (castFrames == 12) + { + // Go into attack frame. + castAttacking = true; + if (castOnMelee) + { + castState = DoomInfo.States[(int)DoomInfo.MobjInfos[(int)castorder[castNumber].Type].MeleeState]; + } + else + { + castState = DoomInfo.States[(int)DoomInfo.MobjInfos[(int)castorder[castNumber].Type].MissileState]; + } + + castOnMelee = !castOnMelee; + if (castState == DoomInfo.States[(int)MobjState.Null]) + { + if (castOnMelee) + { + castState = DoomInfo.States[(int)DoomInfo.MobjInfos[(int)castorder[castNumber].Type].MeleeState]; + } + else + { + castState = DoomInfo.States[(int)DoomInfo.MobjInfos[(int)castorder[castNumber].Type].MissileState]; + } + } + } + + if (castAttacking) + { + if (castFrames == 24 || + castState == DoomInfo.States[(int)DoomInfo.MobjInfos[(int)castorder[castNumber].Type].SeeState]) + { + castAttacking = false; + castState = DoomInfo.States[(int)DoomInfo.MobjInfos[(int)castorder[castNumber].Type].SeeState]; + castFrames = 0; + } + } + + stopAttack: + + castTics = castState.Tics; + if (castTics == -1) + { + castTics = 15; + } + } + + public bool DoEvent(DoomEvent e) + { + if (stage != 2) + { + return false; + } + + if (e.Type == EventType.KeyDown) + { + if (castDeath) + { + // Already in dying frames. + return true; + } + + // Go into death frame. + castDeath = true; + castState = DoomInfo.States[(int)DoomInfo.MobjInfos[(int)castorder[castNumber].Type].DeathState]; + castTics = castState.Tics; + castFrames = 0; + castAttacking = false; + if (DoomInfo.MobjInfos[(int)castorder[castNumber].Type].DeathSound != 0) + { + StartSound(DoomInfo.MobjInfos[(int)castorder[castNumber].Type].DeathSound); + } + + return true; + } + + return false; + } + + private void StartSound(Sfx sfx) + { + options.Sound.StartSound(sfx); + } + + + + public GameOptions Options => options; + public string Flat => flat; + public string Text => text; + public int Count => count; + public int Stage => stage; + + // For cast. + public string CastName => castorder[castNumber].Name; + public MobjStateDef CastState => castState; + + // For bunny scroll. + public int Scrolled => scrolled; + public int TheEndIndex => theEndIndex; + public bool ShowTheEnd => showTheEnd; + + + + private class CastInfo + { + public string Name; + public MobjType Type; + + public CastInfo(string name, MobjType type) + { + Name = name; + Type = type; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/Intermission.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/Intermission.cs new file mode 100644 index 00000000..af9bb5aa --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/Intermission.cs @@ -0,0 +1,858 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class Intermission + { + private GameOptions options; + + // Contains information passed into intermission. + private IntermissionInfo info; + private PlayerScores[] scores; + + // Used to accelerate or skip a stage. + private bool accelerateStage; + + // Specifies current state. + private IntermissionState state; + + private int[] killCount; + private int[] itemCount; + private int[] secretCount; + private int[] fragCount; + private int timeCount; + private int parCount; + private int pauseCount; + + private int spState; + + private int ngState; + private bool doFrags; + + private int dmState; + private int[][] dmFragCount; + private int[] dmTotalCount; + + private DoomRandom random; + private Animation[] animations; + private bool showYouAreHere; + + // Used for general timing. + private int count; + + // Used for timing of background animation. + private int bgCount; + + private bool completed; + + public Intermission(GameOptions options, IntermissionInfo info) + { + this.options = options; + this.info = info; + + scores = info.Players; + + killCount = new int[Player.MaxPlayerCount]; + itemCount = new int[Player.MaxPlayerCount]; + secretCount = new int[Player.MaxPlayerCount]; + fragCount = new int[Player.MaxPlayerCount]; + + dmFragCount = new int[Player.MaxPlayerCount][]; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + dmFragCount[i] = new int[Player.MaxPlayerCount]; + } + dmTotalCount = new int[Player.MaxPlayerCount]; + + if (options.Deathmatch != 0) + { + InitDeathmatchStats(); + } + else if (options.NetGame) + { + InitNetGameStats(); + } + else + { + InitSinglePLayerStats(); + } + + completed = false; + } + + + + //////////////////////////////////////////////////////////// + // Initialization + //////////////////////////////////////////////////////////// + + private void InitSinglePLayerStats() + { + state = IntermissionState.StatCount; + accelerateStage = false; + spState = 1; + killCount[0] = itemCount[0] = secretCount[0] = -1; + timeCount = parCount = -1; + pauseCount = GameConst.TicRate; + + InitAnimatedBack(); + } + + + private void InitNetGameStats() + { + state = IntermissionState.StatCount; + accelerateStage = false; + ngState = 1; + pauseCount = GameConst.TicRate; + + var frags = 0; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (!options.Players[i].InGame) + { + continue; + } + + killCount[i] = itemCount[i] = secretCount[i] = fragCount[i] = 0; + + frags += GetFragSum(i); + } + doFrags = frags > 0; + + InitAnimatedBack(); + } + + + private void InitDeathmatchStats() + { + state = IntermissionState.StatCount; + accelerateStage = false; + dmState = 1; + pauseCount = GameConst.TicRate; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (options.Players[i].InGame) + { + for (var j = 0; j < Player.MaxPlayerCount; j++) + { + if (options.Players[j].InGame) + { + dmFragCount[i][j] = 0; + } + } + dmTotalCount[i] = 0; + } + } + + InitAnimatedBack(); + } + + + private void InitNoState() + { + state = IntermissionState.NoState; + accelerateStage = false; + count = 10; + } + + + private static readonly int showNextLocDelay = 4; + + private void InitShowNextLoc() + { + state = IntermissionState.ShowNextLoc; + accelerateStage = false; + count = showNextLocDelay * GameConst.TicRate; + + InitAnimatedBack(); + } + + + private void InitAnimatedBack() + { + if (options.GameMode == GameMode.Commercial) + { + return; + } + + if (info.Episode > 2) + { + return; + } + + if (animations == null) + { + animations = new Animation[AnimationInfo.Episodes[info.Episode].Count]; + for (var i = 0; i < animations.Length; i++) + { + animations[i] = new Animation(this, AnimationInfo.Episodes[info.Episode][i], i); + } + + random = new DoomRandom(); + } + + foreach (var animation in animations) + { + animation.Reset(bgCount); + } + } + + + + //////////////////////////////////////////////////////////// + // Update + //////////////////////////////////////////////////////////// + + public UpdateResult Update() + { + // Counter for general background animation. + bgCount++; + + CheckForAccelerate(); + + if (bgCount == 1) + { + // intermission music + if (options.GameMode == GameMode.Commercial) + { + options.Music.StartMusic(Bgm.DM2INT, true); + } + else + { + options.Music.StartMusic(Bgm.INTER, true); + } + } + + switch (state) + { + case IntermissionState.StatCount: + if (options.Deathmatch != 0) + { + UpdateDeathmatchStats(); + } + else if (options.NetGame) + { + UpdateNetGameStats(); + } + else + { + UpdateSinglePlayerStats(); + } + break; + + case IntermissionState.ShowNextLoc: + UpdateShowNextLoc(); + break; + + case IntermissionState.NoState: + UpdateNoState(); + break; + } + + if (completed) + { + return UpdateResult.Completed; + } + else + { + if (bgCount == 1) + { + return UpdateResult.NeedWipe; + } + else + { + return UpdateResult.None; + } + } + } + + + private void UpdateSinglePlayerStats() + { + UpdateAnimatedBack(); + + if (accelerateStage && spState != 10) + { + accelerateStage = false; + killCount[0] = (scores[0].KillCount * 100) / info.MaxKillCount; + itemCount[0] = (scores[0].ItemCount * 100) / info.MaxItemCount; + secretCount[0] = (scores[0].SecretCount * 100) / info.MaxSecretCount; + timeCount = scores[0].Time / GameConst.TicRate; + parCount = info.ParTime / GameConst.TicRate; + StartSound(Sfx.BAREXP); + spState = 10; + } + + if (spState == 2) + { + killCount[0] += 2; + + if ((bgCount & 3) == 0) + { + StartSound(Sfx.PISTOL); + } + + if (killCount[0] >= (scores[0].KillCount * 100) / info.MaxKillCount) + { + killCount[0] = (scores[0].KillCount * 100) / info.MaxKillCount; + StartSound(Sfx.BAREXP); + spState++; + } + } + else if (spState == 4) + { + itemCount[0] += 2; + + if ((bgCount & 3) == 0) + { + StartSound(Sfx.PISTOL); + } + + if (itemCount[0] >= (scores[0].ItemCount * 100) / info.MaxItemCount) + { + itemCount[0] = (scores[0].ItemCount * 100) / info.MaxItemCount; + StartSound(Sfx.BAREXP); + spState++; + } + } + else if (spState == 6) + { + secretCount[0] += 2; + + if ((bgCount & 3) == 0) + { + StartSound(Sfx.PISTOL); + } + + if (secretCount[0] >= (scores[0].SecretCount * 100) / info.MaxSecretCount) + { + secretCount[0] = (scores[0].SecretCount * 100) / info.MaxSecretCount; + StartSound(Sfx.BAREXP); + spState++; + } + } + + else if (spState == 8) + { + if ((bgCount & 3) == 0) + { + StartSound(Sfx.PISTOL); + } + + timeCount += 3; + + if (timeCount >= scores[0].Time / GameConst.TicRate) + { + timeCount = scores[0].Time / GameConst.TicRate; + } + + parCount += 3; + + if (parCount >= info.ParTime / GameConst.TicRate) + { + parCount = info.ParTime / GameConst.TicRate; + + if (timeCount >= scores[0].Time / GameConst.TicRate) + { + StartSound(Sfx.BAREXP); + spState++; + } + } + } + else if (spState == 10) + { + if (accelerateStage) + { + StartSound(Sfx.SGCOCK); + + if (options.GameMode == GameMode.Commercial) + { + InitNoState(); + } + else + { + InitShowNextLoc(); + } + } + } + else if ((spState & 1) != 0) + { + if (--pauseCount == 0) + { + spState++; + pauseCount = GameConst.TicRate; + } + } + } + + + private void UpdateNetGameStats() + { + UpdateAnimatedBack(); + + bool stillTicking; + + if (accelerateStage && ngState != 10) + { + accelerateStage = false; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (!options.Players[i].InGame) + { + continue; + } + + killCount[i] = (scores[i].KillCount * 100) / info.MaxKillCount; + itemCount[i] = (scores[i].ItemCount * 100) / info.MaxItemCount; + secretCount[i] = (scores[i].SecretCount * 100) / info.MaxSecretCount; + } + + StartSound(Sfx.BAREXP); + + ngState = 10; + } + + if (ngState == 2) + { + if ((bgCount & 3) == 0) + { + StartSound(Sfx.PISTOL); + } + + stillTicking = false; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (!options.Players[i].InGame) + { + continue; + } + + killCount[i] += 2; + if (killCount[i] >= (scores[i].KillCount * 100) / info.MaxKillCount) + { + killCount[i] = (scores[i].KillCount * 100) / info.MaxKillCount; + } + else + { + stillTicking = true; + } + } + + if (!stillTicking) + { + StartSound(Sfx.BAREXP); + ngState++; + } + } + else if (ngState == 4) + { + if ((bgCount & 3) == 0) + { + StartSound(Sfx.PISTOL); + } + + stillTicking = false; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (!options.Players[i].InGame) + { + continue; + } + + itemCount[i] += 2; + if (itemCount[i] >= (scores[i].ItemCount * 100) / info.MaxItemCount) + { + itemCount[i] = (scores[i].ItemCount * 100) / info.MaxItemCount; + } + else + { + stillTicking = true; + } + } + + if (!stillTicking) + { + StartSound(Sfx.BAREXP); + ngState++; + } + } + else if (ngState == 6) + { + if ((bgCount & 3) == 0) + { + StartSound(Sfx.PISTOL); + } + + stillTicking = false; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (!options.Players[i].InGame) + { + continue; + } + + secretCount[i] += 2; + if (secretCount[i] >= (scores[i].SecretCount * 100) / info.MaxSecretCount) + { + secretCount[i] = (scores[i].SecretCount * 100) / info.MaxSecretCount; + } + else + { + stillTicking = true; + } + } + + if (!stillTicking) + { + StartSound(Sfx.BAREXP); + if (doFrags) + { + ngState++; + } + else + { + ngState += 3; + } + } + } + else if (ngState == 8) + { + if ((bgCount & 3) == 0) + { + StartSound(Sfx.PISTOL); + } + + stillTicking = false; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (!options.Players[i].InGame) + { + continue; + } + + fragCount[i] += 1; + var sum = GetFragSum(i); + if (fragCount[i] >= sum) + { + fragCount[i] = sum; + } + else + { + stillTicking = true; + } + } + + if (!stillTicking) + { + StartSound(Sfx.PLDETH); + ngState++; + } + } + else if (ngState == 10) + { + if (accelerateStage) + { + StartSound(Sfx.SGCOCK); + + if (options.GameMode == GameMode.Commercial) + { + InitNoState(); + } + else + { + InitShowNextLoc(); + } + } + } + else if ((ngState & 1) != 0) + { + if (--pauseCount == 0) + { + ngState++; + pauseCount = GameConst.TicRate; + } + } + } + + + private void UpdateDeathmatchStats() + { + UpdateAnimatedBack(); + + bool stillticking; + + if (accelerateStage && dmState != 4) + { + accelerateStage = false; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (options.Players[i].InGame) + { + for (var j = 0; j < Player.MaxPlayerCount; j++) + { + if (options.Players[j].InGame) + { + dmFragCount[i][j] = scores[i].Frags[j]; + } + } + + dmTotalCount[i] = GetFragSum(i); + } + } + + StartSound(Sfx.BAREXP); + + dmState = 4; + } + + if (dmState == 2) + { + if ((bgCount & 3) == 0) + { + StartSound(Sfx.PISTOL); + } + + stillticking = false; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (options.Players[i].InGame) + { + for (var j = 0; j < Player.MaxPlayerCount; j++) + { + if (options.Players[j].InGame && dmFragCount[i][j] != scores[i].Frags[j]) + { + if (scores[i].Frags[j] < 0) + { + dmFragCount[i][j]--; + } + else + { + dmFragCount[i][j]++; + } + + if (dmFragCount[i][j] > 99) + { + dmFragCount[i][j] = 99; + } + + if (dmFragCount[i][j] < -99) + { + dmFragCount[i][j] = -99; + } + + stillticking = true; + } + } + + dmTotalCount[i] = GetFragSum(i); + + if (dmTotalCount[i] > 99) + { + dmTotalCount[i] = 99; + } + + if (dmTotalCount[i] < -99) + { + dmTotalCount[i] = -99; + } + } + + } + + if (!stillticking) + { + StartSound(Sfx.BAREXP); + dmState++; + } + + } + else if (dmState == 4) + { + if (accelerateStage) + { + StartSound(Sfx.SLOP); + + if (options.GameMode == GameMode.Commercial) + { + InitNoState(); + } + else + { + InitShowNextLoc(); + } + } + } + else if ((dmState & 1) != 0) + { + if (--pauseCount == 0) + { + dmState++; + pauseCount = GameConst.TicRate; + } + } + } + + + private void UpdateShowNextLoc() + { + UpdateAnimatedBack(); + + if (--count == 0 || accelerateStage) + { + InitNoState(); + } + else + { + showYouAreHere = (count & 31) < 20; + } + } + + + private void UpdateNoState() + { + + UpdateAnimatedBack(); + + if (--count == 0) + { + completed = true; + } + } + + + private void UpdateAnimatedBack() + { + if (options.GameMode == GameMode.Commercial) + { + return; + } + + if (info.Episode > 2) + { + return; + } + + foreach (var a in animations) + { + a.Update(bgCount); + } + } + + + + //////////////////////////////////////////////////////////// + // Check for button press + //////////////////////////////////////////////////////////// + + private void CheckForAccelerate() + { + // Check for button presses to skip delays. + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + var player = options.Players[i]; + if (player.InGame) + { + if ((player.Cmd.Buttons & TicCmdButtons.Attack) != 0) + { + if (!player.AttackDown) + { + accelerateStage = true; + } + player.AttackDown = true; + } + else + { + player.AttackDown = false; + } + + if ((player.Cmd.Buttons & TicCmdButtons.Use) != 0) + { + if (!player.UseDown) + { + accelerateStage = true; + } + player.UseDown = true; + } + else + { + player.UseDown = false; + } + } + } + } + + + + //////////////////////////////////////////////////////////// + // Miscellaneous functions + //////////////////////////////////////////////////////////// + + private int GetFragSum(int playerNumber) + { + var frags = 0; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (options.Players[i].InGame && i != playerNumber) + { + frags += scores[playerNumber].Frags[i]; + } + } + + frags -= scores[playerNumber].Frags[playerNumber]; + + return frags; + } + + + private void StartSound(Sfx sfx) + { + options.Sound.StartSound(sfx); + } + + + + public GameOptions Options => options; + public IntermissionInfo Info => info; + public IntermissionState State => state; + public IReadOnlyList KillCount => killCount; + public IReadOnlyList ItemCount => itemCount; + public IReadOnlyList SecretCount => secretCount; + public IReadOnlyList FragCount => fragCount; + public int TimeCount => timeCount; + public int ParCount => parCount; + public int[][] DeathmatchFrags => dmFragCount; + public int[] DeathmatchTotals => dmTotalCount; + public bool DoFrags => doFrags; + public DoomRandom Random => random; + public Animation[] Animations => animations; + public bool ShowYouAreHere => showYouAreHere; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/IntermissionInfo.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/IntermissionInfo.cs new file mode 100644 index 00000000..9be3a218 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/IntermissionInfo.cs @@ -0,0 +1,112 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public class IntermissionInfo + { + // Episode number (0-2). + private int episode; + + // If true, splash the secret level. + private bool didSecret; + + // Previous and next levels, origin 0. + private int lastLevel; + private int nextLevel; + + private int maxKillCount; + private int maxItemCount; + private int maxSecretCount; + private int totalFrags; + + // The par time. + private int parTime; + + private PlayerScores[] players; + + public IntermissionInfo() + { + players = new PlayerScores[Player.MaxPlayerCount]; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + players[i] = new PlayerScores(); + } + } + + public int Episode + { + get => episode; + set => episode = value; + } + + public bool DidSecret + { + get => didSecret; + set => didSecret = value; + } + + public int LastLevel + { + get => lastLevel; + set => lastLevel = value; + } + + public int NextLevel + { + get => nextLevel; + set => nextLevel = value; + } + + public int MaxKillCount + { + get => Math.Max(maxKillCount, 1); + set => maxKillCount = value; + } + + public int MaxItemCount + { + get => Math.Max(maxItemCount, 1); + set => maxItemCount = value; + } + + public int MaxSecretCount + { + get => Math.Max(maxSecretCount, 1); + set => maxSecretCount = value; + } + + public int TotalFrags + { + get => Math.Max(totalFrags, 1); + set => totalFrags = value; + } + + public int ParTime + { + get => parTime; + set => parTime = value; + } + + public PlayerScores[] Players + { + get => players; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/IntermissionState.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/IntermissionState.cs new file mode 100644 index 00000000..8d3370ab --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/IntermissionState.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum IntermissionState + { + NoState = -1, + StatCount, + ShowNextLoc + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/PlayerScores.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/PlayerScores.cs new file mode 100644 index 00000000..6fb9d081 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/PlayerScores.cs @@ -0,0 +1,75 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public class PlayerScores + { + // Whether the player is in game. + private bool inGame; + + // Player stats, kills, collected items etc. + private int killCount; + private int itemCount; + private int secretCount; + private int time; + private int[] frags; + + public PlayerScores() + { + frags = new int[Player.MaxPlayerCount]; + } + + public bool InGame + { + get => inGame; + set => inGame = value; + } + + public int KillCount + { + get => killCount; + set => killCount = value; + } + + public int ItemCount + { + get => itemCount; + set => itemCount = value; + } + + public int SecretCount + { + get => secretCount; + set => secretCount = value; + } + + public int Time + { + get => time; + set => time = value; + } + + public int[] Frags + { + get => frags; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/WorldMap.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/WorldMap.cs new file mode 100644 index 00000000..7875be61 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Intermission/WorldMap.cs @@ -0,0 +1,87 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public static class WorldMap + { + public static readonly IReadOnlyList> Locations = new Point[][] + { + // Episode 0 world map. + new Point[] + { + new Point(185, 164), // location of level 0 (CJ) + new Point(148, 143), // location of level 1 (CJ) + new Point(69, 122), // location of level 2 (CJ) + new Point(209, 102), // location of level 3 (CJ) + new Point(116, 89), // location of level 4 (CJ) + new Point(166, 55), // location of level 5 (CJ) + new Point(71, 56), // location of level 6 (CJ) + new Point(135, 29), // location of level 7 (CJ) + new Point(71, 24) // location of level 8 (CJ) + }, + + // Episode 1 world map should go here. + new Point[] + { + new Point(254, 25), // location of level 0 (CJ) + new Point(97, 50), // location of level 1 (CJ) + new Point(188, 64), // location of level 2 (CJ) + new Point(128, 78), // location of level 3 (CJ) + new Point(214, 92), // location of level 4 (CJ) + new Point(133, 130), // location of level 5 (CJ) + new Point(208, 136), // location of level 6 (CJ) + new Point(148, 140), // location of level 7 (CJ) + new Point(235, 158) // location of level 8 (CJ) + }, + + // Episode 2 world map should go here. + new Point[] + { + new Point(156, 168), // location of level 0 (CJ) + new Point(48, 154), // location of level 1 (CJ) + new Point(174, 95), // location of level 2 (CJ) + new Point(265, 75), // location of level 3 (CJ) + new Point(130, 48), // location of level 4 (CJ) + new Point(279, 23), // location of level 5 (CJ) + new Point(198, 48), // location of level 6 (CJ) + new Point(140, 25), // location of level 7 (CJ) + new Point(281, 136) // location of level 8 (CJ) + } + }; + + + + public class Point + { + private int x; + private int y; + + public Point(int x, int y) + { + this.x = x; + this.y = y; + } + + public int X => x; + public int Y => y; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/BlockMap.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/BlockMap.cs new file mode 100644 index 00000000..d58ecf74 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/BlockMap.cs @@ -0,0 +1,169 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class BlockMap + { + public static readonly int IntBlockSize = 128; + public static readonly Fixed BlockSize = Fixed.FromInt(IntBlockSize); + public static readonly int BlockMask = BlockSize.Data - 1; + public static readonly int FracToBlockShift = Fixed.FracBits + 7; + public static readonly int BlockToFracShift = FracToBlockShift - Fixed.FracBits; + + private Fixed originX; + private Fixed originY; + + private int width; + private int height; + + private short[] table; + + private LineDef[] lines; + + private Mobj[] thingLists; + + private BlockMap( + Fixed originX, + Fixed originY, + int width, + int height, + short[] table, + LineDef[] lines) + { + this.originX = originX; + this.originY = originY; + this.width = width; + this.height = height; + this.table = table; + this.lines = lines; + + thingLists = new Mobj[width * height]; + } + + public static BlockMap FromWad(Wad wad, int lump, LineDef[] lines) + { + var data = wad.ReadLump(lump); + + var table = new short[data.Length / 2]; + for (var i = 0; i < table.Length; i++) + { + var offset = 2 * i; + table[i] = BitConverter.ToInt16(data, offset); + } + + var originX = Fixed.FromInt(table[0]); + var originY = Fixed.FromInt(table[1]); + var width = table[2]; + var height = table[3]; + + return new BlockMap( + originX, + originY, + width, + height, + table, + lines); + } + + public int GetBlockX(Fixed x) + { + return (x - originX).Data >> FracToBlockShift; + } + + public int GetBlockY(Fixed y) + { + return (y - originY).Data >> FracToBlockShift; + } + + public int GetIndex(int blockX, int blockY) + { + if (0 <= blockX && blockX < width && 0 <= blockY && blockY < height) + { + return width * blockY + blockX; + } + else + { + return -1; + } + } + + public int GetIndex(Fixed x, Fixed y) + { + var blockX = GetBlockX(x); + var blockY = GetBlockY(y); + return GetIndex(blockX, blockY); + } + + public bool IterateLines(int blockX, int blockY, Func func, int validCount) + { + var index = GetIndex(blockX, blockY); + + if (index == -1) + { + return true; + } + + for (var offset = table[4 + index]; table[offset] != -1; offset++) + { + var line = lines[table[offset]]; + + if (line.ValidCount == validCount) + { + continue; + } + + line.ValidCount = validCount; + + if (!func(line)) + { + return false; + } + } + + return true; + } + + public bool IterateThings(int blockX, int blockY, Func func) + { + var index = GetIndex(blockX, blockY); + + if (index == -1) + { + return true; + } + + for (var mobj = thingLists[index]; mobj != null; mobj = mobj.BlockNext) + { + if (!func(mobj)) + { + return false; + } + } + + return true; + } + + public Fixed OriginX => originX; + public Fixed OriginY => originY; + public int Width => width; + public int Height => height; + public Mobj[] ThingLists => thingLists; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/LineDef.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/LineDef.cs new file mode 100644 index 00000000..771edb7d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/LineDef.cs @@ -0,0 +1,195 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class LineDef + { + private static readonly int dataSize = 14; + + private Vertex vertex1; + private Vertex vertex2; + + private Fixed dx; + private Fixed dy; + + private LineFlags flags; + private LineSpecial special; + private short tag; + + private SideDef frontSide; + private SideDef backSide; + + private Fixed[] boundingBox; + + private SlopeType slopeType; + + private Sector frontSector; + private Sector backSector; + + private int validCount; + + private Thinker specialData; + + private Mobj soundOrigin; + + public LineDef( + Vertex vertex1, + Vertex vertex2, + LineFlags flags, + LineSpecial special, + short tag, + SideDef frontSide, + SideDef backSide) + { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.flags = flags; + this.special = special; + this.tag = tag; + this.frontSide = frontSide; + this.backSide = backSide; + + dx = vertex2.X - vertex1.X; + dy = vertex2.Y - vertex1.Y; + + if (dx == Fixed.Zero) + { + slopeType = SlopeType.Vertical; + } + else if (dy == Fixed.Zero) + { + slopeType = SlopeType.Horizontal; + } + else + { + if (dy / dx > Fixed.Zero) + { + slopeType = SlopeType.Positive; + } + else + { + slopeType = SlopeType.Negative; + } + } + + boundingBox = new Fixed[4]; + boundingBox[Box.Top] = Fixed.Max(vertex1.Y, vertex2.Y); + boundingBox[Box.Bottom] = Fixed.Min(vertex1.Y, vertex2.Y); + boundingBox[Box.Left] = Fixed.Min(vertex1.X, vertex2.X); + boundingBox[Box.Right] = Fixed.Max(vertex1.X, vertex2.X); + + frontSector = frontSide?.Sector; + backSector = backSide?.Sector; + } + + public static LineDef FromData(byte[] data, int offset, Vertex[] vertices, SideDef[] sides) + { + var vertex1Number = BitConverter.ToInt16(data, offset); + var vertex2Number = BitConverter.ToInt16(data, offset + 2); + var flags = BitConverter.ToInt16(data, offset + 4); + var special = BitConverter.ToInt16(data, offset + 6); + var tag = BitConverter.ToInt16(data, offset + 8); + var side0Number = BitConverter.ToInt16(data, offset + 10); + var side1Number = BitConverter.ToInt16(data, offset + 12); + + return new LineDef( + vertices[vertex1Number], + vertices[vertex2Number], + (LineFlags)flags, + (LineSpecial)special, + tag, + sides[side0Number], + side1Number != -1 ? sides[side1Number] : null); + } + + public static LineDef[] FromWad(Wad wad, int lump, Vertex[] vertices, SideDef[] sides) + { + var length = wad.GetLumpSize(lump); + if (length % dataSize != 0) + { + throw new Exception(); + } + + var data = wad.ReadLump(lump); + var count = length / dataSize; + var lines = new LineDef[count]; ; + + for (var i = 0; i < count; i++) + { + var offset = 14 * i; + lines[i] = FromData(data, offset, vertices, sides); + } + + return lines; + } + + public Vertex Vertex1 => vertex1; + public Vertex Vertex2 => vertex2; + + public Fixed Dx => dx; + public Fixed Dy => dy; + + public LineFlags Flags + { + get => flags; + set => flags = value; + } + + public LineSpecial Special + { + get => special; + set => special = value; + } + + public short Tag + { + get => tag; + set => tag = value; + } + + public SideDef FrontSide => frontSide; + public SideDef BackSide => backSide; + + public Fixed[] BoundingBox => boundingBox; + + public SlopeType SlopeType => slopeType; + + public Sector FrontSector => frontSector; + public Sector BackSector => backSector; + + public int ValidCount + { + get => validCount; + set => validCount = value; + } + + public Thinker SpecialData + { + get => specialData; + set => specialData = value; + } + + public Mobj SoundOrigin + { + get => soundOrigin; + set => soundOrigin = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/LineFlags.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/LineFlags.cs new file mode 100644 index 00000000..a1015f41 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/LineFlags.cs @@ -0,0 +1,35 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + [Flags] + public enum LineFlags + { + Blocking = 1, + BlockMonsters = 2, + TwoSided = 4, + DontPegTop = 8, + DontPegBottom = 16, + Secret = 32, + SoundBlock = 64, + DontDraw = 128, + Mapped = 256 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/LineSpecial.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/LineSpecial.cs new file mode 100644 index 00000000..db48842f --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/LineSpecial.cs @@ -0,0 +1,26 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum LineSpecial + { + Normal = 0 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Map.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Map.cs new file mode 100644 index 00000000..7ac8b141 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Map.cs @@ -0,0 +1,275 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.Runtime.ExceptionServices; + +namespace ManagedDoom +{ + public sealed class Map + { + private TextureLookup textures; + private FlatLookup flats; + private TextureAnimation animation; + + private World world; + + private Vertex[] vertices; + private Sector[] sectors; + private SideDef[] sides; + private LineDef[] lines; + private Seg[] segs; + private Subsector[] subsectors; + private Node[] nodes; + private MapThing[] things; + private BlockMap blockMap; + private Reject reject; + + private Texture skyTexture; + + private string title; + + public Map(CommonResource resorces, World world) + : this(resorces.Wad, resorces.Textures, resorces.Flats, resorces.Animation, world) + { + } + + public Map(Wad wad, TextureLookup textures, FlatLookup flats, TextureAnimation animation, World world) + { + try + { + this.textures = textures; + this.flats = flats; + this.animation = animation; + this.world = world; + + var options = world.Options; + + string name; + if (wad.GameMode == GameMode.Commercial) + { + name = "MAP" + options.Map.ToString("00"); + } + else + { + name = "E" + options.Episode + "M" + options.Map; + } + + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Load map '" + name + "': "); + + var map = wad.GetLumpNumber(name); + + if (map == -1) + { + throw new Exception("Map '" + name + "' was not found!"); + } + + vertices = Vertex.FromWad(wad, map + 4); + sectors = Sector.FromWad(wad, map + 8, flats); + sides = SideDef.FromWad(wad, map + 3, textures, sectors); + lines = LineDef.FromWad(wad, map + 2, vertices, sides); + segs = Seg.FromWad(wad, map + 5, vertices, lines); + subsectors = Subsector.FromWad(wad, map + 6, segs); + nodes = Node.FromWad(wad, map + 7, subsectors); + things = MapThing.FromWad(wad, map + 1); + blockMap = BlockMap.FromWad(wad, map + 10, lines); + reject = Reject.FromWad(wad, map + 9, sectors); + + GroupLines(); + + skyTexture = GetSkyTextureByMapName(name); + + if (options.GameMode == GameMode.Commercial) + { + switch (options.MissionPack) + { + case MissionPack.Plutonia: + title = DoomInfo.MapTitles.Plutonia[options.Map - 1]; + break; + case MissionPack.Tnt: + title = DoomInfo.MapTitles.Tnt[options.Map - 1]; + break; + default: + title = DoomInfo.MapTitles.Doom2[options.Map - 1]; + break; + } + } + else + { + title = DoomInfo.MapTitles.Doom[options.Episode - 1][options.Map - 1]; + } + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + ExceptionDispatchInfo.Throw(e); + } + } + + private void GroupLines() + { + var sectorLines = new List(); + var boundingBox = new Fixed[4]; + + foreach (var line in lines) + { + if (line.Special != 0) + { + var so = new Mobj(world); + so.X = (line.Vertex1.X + line.Vertex2.X) / 2; + so.Y = (line.Vertex1.Y + line.Vertex2.Y) / 2; + line.SoundOrigin = so; + } + } + + foreach (var sector in sectors) + { + sectorLines.Clear(); + Box.Clear(boundingBox); + + foreach (var line in lines) + { + if (line.FrontSector == sector || line.BackSector == sector) + { + sectorLines.Add(line); + Box.AddPoint(boundingBox, line.Vertex1.X, line.Vertex1.Y); + Box.AddPoint(boundingBox, line.Vertex2.X, line.Vertex2.Y); + } + } + + sector.Lines = sectorLines.ToArray(); + + // Set the degenmobj_t to the middle of the bounding box. + sector.SoundOrigin = new Mobj(world); + sector.SoundOrigin.X = (boundingBox[Box.Right] + boundingBox[Box.Left]) / 2; + sector.SoundOrigin.Y = (boundingBox[Box.Top] + boundingBox[Box.Bottom]) / 2; + + sector.BlockBox = new int[4]; + int block; + + // Adjust bounding box to map blocks. + block = (boundingBox[Box.Top] - blockMap.OriginY + GameConst.MaxThingRadius).Data >> BlockMap.FracToBlockShift; + block = block >= blockMap.Height ? blockMap.Height - 1 : block; + sector.BlockBox[Box.Top] = block; + + block = (boundingBox[Box.Bottom] - blockMap.OriginY - GameConst.MaxThingRadius).Data >> BlockMap.FracToBlockShift; + block = block < 0 ? 0 : block; + sector.BlockBox[Box.Bottom] = block; + + block = (boundingBox[Box.Right] - blockMap.OriginX + GameConst.MaxThingRadius).Data >> BlockMap.FracToBlockShift; + block = block >= blockMap.Width ? blockMap.Width - 1 : block; + sector.BlockBox[Box.Right] = block; + + block = (boundingBox[Box.Left] - blockMap.OriginX - GameConst.MaxThingRadius).Data >> BlockMap.FracToBlockShift; + block = block < 0 ? 0 : block; + sector.BlockBox[Box.Left] = block; + } + } + + private Texture GetSkyTextureByMapName(string name) + { + if (name.Length == 4) + { + switch (name[1]) + { + case '1': + return textures["SKY1"]; + case '2': + return textures["SKY2"]; + case '3': + return textures["SKY3"]; + default: + return textures["SKY4"]; + } + } + else + { + var number = int.Parse(name.Substring(3)); + if (number <= 11) + { + return textures["SKY1"]; + } + else if (number <= 21) + { + return textures["SKY2"]; + } + else + { + return textures["SKY3"]; + } + } + } + + public TextureLookup Textures => textures; + public FlatLookup Flats => flats; + public TextureAnimation Animation => animation; + + public Vertex[] Vertices => vertices; + public Sector[] Sectors => sectors; + public SideDef[] Sides => sides; + public LineDef[] Lines => lines; + public Seg[] Segs => segs; + public Subsector[] Subsectors => subsectors; + public Node[] Nodes => nodes; + public MapThing[] Things => things; + public BlockMap BlockMap => blockMap; + public Reject Reject => reject; + public Texture SkyTexture => skyTexture; + public int SkyFlatNumber => flats.SkyFlatNumber; + public string Title => title; + + + + private static readonly Bgm[] e4BgmList = new Bgm[] + { + Bgm.E3M4, // American e4m1 + Bgm.E3M2, // Romero e4m2 + Bgm.E3M3, // Shawn e4m3 + Bgm.E1M5, // American e4m4 + Bgm.E2M7, // Tim e4m5 + Bgm.E2M4, // Romero e4m6 + Bgm.E2M6, // J.Anderson e4m7 CHIRON.WAD + Bgm.E2M5, // Shawn e4m8 + Bgm.E1M9 // Tim e4m9 + }; + + public static Bgm GetMapBgm(GameOptions options) + { + Bgm bgm; + if (options.GameMode == GameMode.Commercial) + { + bgm = Bgm.RUNNIN + options.Map - 1; + } + else + { + if (options.Episode < 4) + { + bgm = Bgm.E1M1 + (options.Episode - 1) * 9 + options.Map - 1; + } + else + { + bgm = e4BgmList[options.Map - 1]; + } + } + + return bgm; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/MapThing.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/MapThing.cs new file mode 100644 index 00000000..f0bb5003 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/MapThing.cs @@ -0,0 +1,102 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class MapThing + { + private static readonly int dataSize = 10; + + public static MapThing Empty = new MapThing( + Fixed.Zero, + Fixed.Zero, + Angle.Ang0, + 0, + 0); + + private Fixed x; + private Fixed y; + private Angle angle; + private int type; + private ThingFlags flags; + + public MapThing( + Fixed x, + Fixed y, + Angle angle, + int type, + ThingFlags flags) + { + this.x = x; + this.y = y; + this.angle = angle; + this.type = type; + this.flags = flags; + } + + public static MapThing FromData(byte[] data, int offset) + { + var x = BitConverter.ToInt16(data, offset); + var y = BitConverter.ToInt16(data, offset + 2); + var angle = BitConverter.ToInt16(data, offset + 4); + var type = BitConverter.ToInt16(data, offset + 6); + var flags = BitConverter.ToInt16(data, offset + 8); + + return new MapThing( + Fixed.FromInt(x), + Fixed.FromInt(y), + new Angle(ManagedDoom.Angle.Ang45.Data * (uint)(angle / 45)), + type, + (ThingFlags)flags); + } + + public static MapThing[] FromWad(Wad wad, int lump) + { + var length = wad.GetLumpSize(lump); + if (length % dataSize != 0) + { + throw new Exception(); + } + + var data = wad.ReadLump(lump); + var count = length / dataSize; + var things = new MapThing[count]; + + for (var i = 0; i < count; i++) + { + var offset = dataSize * i; + things[i] = FromData(data, offset); + } + + return things; + } + + public Fixed X => x; + public Fixed Y => y; + public Angle Angle => angle; + + public int Type + { + get => type; + set => type = value; + } + + public ThingFlags Flags => flags; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Node.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Node.cs new file mode 100644 index 00000000..36d4c027 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Node.cs @@ -0,0 +1,157 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Node + { + private static readonly int dataSize = 28; + + private Fixed x; + private Fixed y; + private Fixed dx; + private Fixed dy; + + private Fixed[][] boundingBox; + + private int[] children; + + public Node( + Fixed x, + Fixed y, + Fixed dx, + Fixed dy, + Fixed frontBoundingBoxTop, + Fixed frontBoundingBoxBottom, + Fixed frontBoundingBoxLeft, + Fixed frontBoundingBoxRight, + Fixed backBoundingBoxTop, + Fixed backBoundingBoxBottom, + Fixed backBoundingBoxLeft, + Fixed backBoundingBoxRight, + int frontChild, + int backChild) + { + this.x = x; + this.y = y; + this.dx = dx; + this.dy = dy; + + var frontBoundingBox = new Fixed[4] + { + frontBoundingBoxTop, + frontBoundingBoxBottom, + frontBoundingBoxLeft, + frontBoundingBoxRight + }; + + var backBoundingBox = new Fixed[4] + { + backBoundingBoxTop, + backBoundingBoxBottom, + backBoundingBoxLeft, + backBoundingBoxRight + }; + + boundingBox = new Fixed[][] + { + frontBoundingBox, + backBoundingBox + }; + + children = new int[] + { + frontChild, + backChild + }; + } + + public static Node FromData(byte[] data, int offset) + { + var x = BitConverter.ToInt16(data, offset); + var y = BitConverter.ToInt16(data, offset + 2); + var dx = BitConverter.ToInt16(data, offset + 4); + var dy = BitConverter.ToInt16(data, offset + 6); + var frontBoundingBoxTop = BitConverter.ToInt16(data, offset + 8); + var frontBoundingBoxBottom = BitConverter.ToInt16(data, offset + 10); + var frontBoundingBoxLeft = BitConverter.ToInt16(data, offset + 12); + var frontBoundingBoxRight = BitConverter.ToInt16(data, offset + 14); + var backBoundingBoxTop = BitConverter.ToInt16(data, offset + 16); + var backBoundingBoxBottom = BitConverter.ToInt16(data, offset + 18); + var backBoundingBoxLeft = BitConverter.ToInt16(data, offset + 20); + var backBoundingBoxRight = BitConverter.ToInt16(data, offset + 22); + var frontChild = BitConverter.ToInt16(data, offset + 24); + var backChild = BitConverter.ToInt16(data, offset + 26); + + return new Node( + Fixed.FromInt(x), + Fixed.FromInt(y), + Fixed.FromInt(dx), + Fixed.FromInt(dy), + Fixed.FromInt(frontBoundingBoxTop), + Fixed.FromInt(frontBoundingBoxBottom), + Fixed.FromInt(frontBoundingBoxLeft), + Fixed.FromInt(frontBoundingBoxRight), + Fixed.FromInt(backBoundingBoxTop), + Fixed.FromInt(backBoundingBoxBottom), + Fixed.FromInt(backBoundingBoxLeft), + Fixed.FromInt(backBoundingBoxRight), + frontChild, + backChild); + } + + public static Node[] FromWad(Wad wad, int lump, Subsector[] subsectors) + { + var length = wad.GetLumpSize(lump); + if (length % Node.dataSize != 0) + { + throw new Exception(); + } + + var data = wad.ReadLump(lump); + var count = length / Node.dataSize; + var nodes = new Node[count]; + + for (var i = 0; i < count; i++) + { + var offset = Node.dataSize * i; + nodes[i] = Node.FromData(data, offset); + } + + return nodes; + } + + public static bool IsSubsector(int node) + { + return (node & unchecked((int)0xFFFF8000)) != 0; + } + + public static int GetSubsector(int node) + { + return node ^ unchecked((int)0xFFFF8000); + } + + public Fixed X => x; + public Fixed Y => y; + public Fixed Dx => dx; + public Fixed Dy => dy; + public Fixed[][] BoundingBox => boundingBox; + public int[] Children => children; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Reject.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Reject.cs new file mode 100644 index 00000000..3a3de856 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Reject.cs @@ -0,0 +1,58 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Reject + { + private byte[] data; + private int sectorCount; + + private Reject(byte[] data, int sectorCount) + { + // If the reject table is too small, expand it to avoid crash. + // https://doomwiki.org/wiki/Reject#Reject_Overflow + var expectedLength = (sectorCount * sectorCount + 7) / 8; + if (data.Length < expectedLength) + { + Array.Resize(ref data, expectedLength); + } + + this.data = data; + this.sectorCount = sectorCount; + } + + public static Reject FromWad(Wad wad, int lump, Sector[] sectors) + { + return new Reject(wad.ReadLump(lump), sectors.Length); + } + + public bool Check(Sector sector1, Sector sector2) + { + var s1 = sector1.Number; + var s2 = sector2.Number; + + var p = s1 * sectorCount + s2; + var byteIndex = p >> 3; + var bitIndex = 1 << (p & 7); + + return (data[byteIndex] & bitIndex) != 0; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Sector.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Sector.cs new file mode 100644 index 00000000..e2e59405 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Sector.cs @@ -0,0 +1,264 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class Sector + { + private static readonly int dataSize = 26; + + private int number; + private Fixed floorHeight; + private Fixed ceilingHeight; + private int floorFlat; + private int ceilingFlat; + private int lightLevel; + private SectorSpecial special; + private int tag; + + // 0 = untraversed, 1, 2 = sndlines - 1. + private int soundTraversed; + + // Thing that made a sound (or null). + private Mobj soundTarget; + + // Mapblock bounding box for height changes. + private int[] blockBox; + + // Origin for any sounds played by the sector. + private Mobj soundOrigin; + + // If == validcount, already checked. + private int validCount; + + // List of mobjs in sector. + private Mobj thingList; + + // Thinker for reversable actions. + private Thinker specialData; + + private LineDef[] lines; + + public Sector( + int number, + Fixed floorHeight, + Fixed ceilingHeight, + int floorFlat, + int ceilingFlat, + int lightLevel, + SectorSpecial special, + int tag) + { + this.number = number; + this.floorHeight = floorHeight; + this.ceilingHeight = ceilingHeight; + this.floorFlat = floorFlat; + this.ceilingFlat = ceilingFlat; + this.lightLevel = lightLevel; + this.special = special; + this.tag = tag; + } + + public static Sector FromData(byte[] data, int offset, int number, FlatLookup flats) + { + var floorHeight = BitConverter.ToInt16(data, offset); + var ceilingHeight = BitConverter.ToInt16(data, offset + 2); + var floorFlatName = DoomInterop.ToString(data, offset + 4, 8); + var ceilingFlatName = DoomInterop.ToString(data, offset + 12, 8); + var lightLevel = BitConverter.ToInt16(data, offset + 20); + var special = BitConverter.ToInt16(data, offset + 22); + var tag = BitConverter.ToInt16(data, offset + 24); + + return new Sector( + number, + Fixed.FromInt(floorHeight), + Fixed.FromInt(ceilingHeight), + flats.GetNumber(floorFlatName), + flats.GetNumber(ceilingFlatName), + lightLevel, + (SectorSpecial)special, + tag); + } + + public static Sector[] FromWad(Wad wad, int lump, FlatLookup flats) + { + var length = wad.GetLumpSize(lump); + if (length % dataSize != 0) + { + throw new Exception(); + } + + var data = wad.ReadLump(lump); + var count = length / dataSize; + var sectors = new Sector[count]; ; + + for (var i = 0; i < count; i++) + { + var offset = dataSize * i; + sectors[i] = FromData(data, offset, i, flats); + } + + return sectors; + } + + public ThingEnumerator GetEnumerator() + { + return new ThingEnumerator(this); + } + + + + public struct ThingEnumerator : IEnumerator + { + private Sector sector; + private Mobj thing; + private Mobj current; + + public ThingEnumerator(Sector sector) + { + this.sector = sector; + thing = sector.thingList; + current = null; + } + + public bool MoveNext() + { + if (thing != null) + { + current = thing; + thing = thing.SectorNext; + return true; + } + else + { + current = null; + return false; + } + } + + public void Reset() + { + thing = sector.thingList; + current = null; + } + + public void Dispose() + { + } + + public Mobj Current => current; + + object IEnumerator.Current => throw new NotImplementedException(); + } + + public int Number => number; + + public Fixed FloorHeight + { + get => floorHeight; + set => floorHeight = value; + } + + public Fixed CeilingHeight + { + get => ceilingHeight; + set => ceilingHeight = value; + } + + public int FloorFlat + { + get => floorFlat; + set => floorFlat = value; + } + + public int CeilingFlat + { + get => ceilingFlat; + set => ceilingFlat = value; + } + + public int LightLevel + { + get => lightLevel; + set => lightLevel = value; + } + + public SectorSpecial Special + { + get => special; + set => special = value; + } + + public int Tag + { + get => tag; + set => tag = value; + } + + public int SoundTraversed + { + get => soundTraversed; + set => soundTraversed = value; + } + + public Mobj SoundTarget + { + get => soundTarget; + set => soundTarget = value; + } + + public int[] BlockBox + { + get => blockBox; + set => blockBox = value; + } + + public Mobj SoundOrigin + { + get => soundOrigin; + set => soundOrigin = value; + } + + public int ValidCount + { + get => validCount; + set => validCount = value; + } + + public Mobj ThingList + { + get => thingList; + set => thingList = value; + } + + public Thinker SpecialData + { + get => specialData; + set => specialData = value; + } + + public LineDef[] Lines + { + get => lines; + set => lines = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/SectorSpecial.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/SectorSpecial.cs new file mode 100644 index 00000000..3b2c8e25 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/SectorSpecial.cs @@ -0,0 +1,26 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum SectorSpecial + { + Normal = 0 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Seg.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Seg.cs new file mode 100644 index 00000000..f560d727 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Seg.cs @@ -0,0 +1,109 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Seg + { + private static readonly int dataSize = 12; + + private Vertex vertex1; + private Vertex vertex2; + private Fixed offset; + private Angle angle; + private SideDef sideDef; + private LineDef lineDef; + private Sector frontSector; + private Sector backSector; + + public Seg( + Vertex vertex1, + Vertex vertex2, + Fixed offset, + Angle angle, + SideDef sideDef, + LineDef lineDef, + Sector frontSector, + Sector backSector) + { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.offset = offset; + this.angle = angle; + this.sideDef = sideDef; + this.lineDef = lineDef; + this.frontSector = frontSector; + this.backSector = backSector; + } + + public static Seg FromData(byte[] data, int offset, Vertex[] vertices, LineDef[] lines) + { + var vertex1Number = BitConverter.ToInt16(data, offset); + var vertex2Number = BitConverter.ToInt16(data, offset + 2); + var angle = BitConverter.ToInt16(data, offset + 4); + var lineNumber = BitConverter.ToInt16(data, offset + 6); + var side = BitConverter.ToInt16(data, offset + 8); + var segOffset = BitConverter.ToInt16(data, offset + 10); + + var lineDef = lines[lineNumber]; + var frontSide = side == 0 ? lineDef.FrontSide : lineDef.BackSide; + var backSide = side == 0 ? lineDef.BackSide : lineDef.FrontSide; + + return new Seg( + vertices[vertex1Number], + vertices[vertex2Number], + Fixed.FromInt(segOffset), + new Angle((uint)angle << 16), + frontSide, + lineDef, + frontSide.Sector, + (lineDef.Flags & LineFlags.TwoSided) != 0 ? backSide?.Sector : null); + } + + public static Seg[] FromWad(Wad wad, int lump, Vertex[] vertices, LineDef[] lines) + { + var length = wad.GetLumpSize(lump); + if (length % Seg.dataSize != 0) + { + throw new Exception(); + } + + var data = wad.ReadLump(lump); + var count = length / Seg.dataSize; + var segs = new Seg[count]; ; + + for (var i = 0; i < count; i++) + { + var offset = Seg.dataSize * i; + segs[i] = Seg.FromData(data, offset, vertices, lines); + } + + return segs; + } + + public Vertex Vertex1 => vertex1; + public Vertex Vertex2 => vertex2; + public Fixed Offset => offset; + public Angle Angle => angle; + public SideDef SideDef => sideDef; + public LineDef LineDef => lineDef; + public Sector FrontSector => frontSector; + public Sector BackSector => backSector; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/SideDef.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/SideDef.cs new file mode 100644 index 00000000..ec39d842 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/SideDef.cs @@ -0,0 +1,120 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class SideDef + { + private static readonly int dataSize = 30; + + private Fixed textureOffset; + private Fixed rowOffset; + private int topTexture; + private int bottomTexture; + private int middleTexture; + private Sector sector; + + public SideDef( + Fixed textureOffset, + Fixed rowOffset, + int topTexture, + int bottomTexture, + int middleTexture, + Sector sector) + { + this.textureOffset = textureOffset; + this.rowOffset = rowOffset; + this.topTexture = topTexture; + this.bottomTexture = bottomTexture; + this.middleTexture = middleTexture; + this.sector = sector; + } + + public static SideDef FromData(byte[] data, int offset, TextureLookup textures, Sector[] sectors) + { + var textureOffset = BitConverter.ToInt16(data, offset); + var rowOffset = BitConverter.ToInt16(data, offset + 2); + var topTextureName = DoomInterop.ToString(data, offset + 4, 8); + var bottomTextureName = DoomInterop.ToString(data, offset + 12, 8); + var middleTextureName = DoomInterop.ToString(data, offset + 20, 8); + var sectorNum = BitConverter.ToInt16(data, offset + 28); + + return new SideDef( + Fixed.FromInt(textureOffset), + Fixed.FromInt(rowOffset), + textures.GetNumber(topTextureName), + textures.GetNumber(bottomTextureName), + textures.GetNumber(middleTextureName), + sectorNum != -1 ? sectors[sectorNum] : null); + } + + public static SideDef[] FromWad(Wad wad, int lump, TextureLookup textures, Sector[] sectors) + { + var length = wad.GetLumpSize(lump); + if (length % dataSize != 0) + { + throw new Exception(); + } + + var data = wad.ReadLump(lump); + var count = length / dataSize; + var sides = new SideDef[count]; ; + + for (var i = 0; i < count; i++) + { + var offset = dataSize * i; + sides[i] = FromData(data, offset, textures, sectors); + } + + return sides; + } + + public Fixed TextureOffset + { + get => textureOffset; + set => textureOffset = value; + } + + public Fixed RowOffset + { + get => rowOffset; + set => rowOffset = value; + } + + public int TopTexture + { + get => topTexture; + set => topTexture = value; + } + + public int BottomTexture + { + get => bottomTexture; + set => bottomTexture = value; + } + + public int MiddleTexture + { + get => middleTexture; + set => middleTexture = value; + } + + public Sector Sector => sector; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/SlopeType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/SlopeType.cs new file mode 100644 index 00000000..a05de526 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/SlopeType.cs @@ -0,0 +1,29 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum SlopeType + { + Horizontal, + Vertical, + Positive, + Negative + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Subsector.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Subsector.cs new file mode 100644 index 00000000..6656df69 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Subsector.cs @@ -0,0 +1,73 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Subsector + { + private static readonly int dataSize = 4; + + private Sector sector; + private int segCount; + private int firstSeg; + + public Subsector(Sector sector, int segCount, int firstSeg) + { + this.sector = sector; + this.segCount = segCount; + this.firstSeg = firstSeg; + } + + public static Subsector FromData(byte[] data, int offset, Seg[] segs) + { + var segCount = BitConverter.ToInt16(data, offset); + var firstSegNumber = BitConverter.ToInt16(data, offset + 2); + + return new Subsector( + segs[firstSegNumber].SideDef.Sector, + segCount, + firstSegNumber); + } + + public static Subsector[] FromWad(Wad wad, int lump, Seg[] segs) + { + var length = wad.GetLumpSize(lump); + if (length % Subsector.dataSize != 0) + { + throw new Exception(); + } + + var data = wad.ReadLump(lump); + var count = length / Subsector.dataSize; + var subsectors = new Subsector[count]; + + for (var i = 0; i < count; i++) + { + var offset = Subsector.dataSize * i; + subsectors[i] = Subsector.FromData(data, offset, segs); + } + + return subsectors; + } + + public Sector Sector => sector; + public int SegCount => segCount; + public int FirstSeg => firstSeg; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/ThingFlags.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/ThingFlags.cs new file mode 100644 index 00000000..8daeb346 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/ThingFlags.cs @@ -0,0 +1,30 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + [Flags] + public enum ThingFlags + { + Easy = 1, + Normal = 2, + Hard = 4, + Ambush = 8 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Vertex.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Vertex.cs new file mode 100644 index 00000000..4929e800 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Map/Vertex.cs @@ -0,0 +1,67 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Vertex + { + private static readonly int dataSize = 4; + + private Fixed x; + private Fixed y; + + public Vertex(Fixed x, Fixed y) + { + this.x = x; + this.y = y; + } + + public static Vertex FromData(byte[] data, int offset) + { + var x = BitConverter.ToInt16(data, offset); + var y = BitConverter.ToInt16(data, offset + 2); + + return new Vertex(Fixed.FromInt(x), Fixed.FromInt(y)); + } + + public static Vertex[] FromWad(Wad wad, int lump) + { + var length = wad.GetLumpSize(lump); + if (length % dataSize != 0) + { + throw new Exception(); + } + + var data = wad.ReadLump(lump); + var count = length / dataSize; + var vertices = new Vertex[count]; ; + + for (var i = 0; i < count; i++) + { + var offset = dataSize * i; + vertices[i] = FromData(data, offset); + } + + return vertices; + } + + public Fixed X => x; + public Fixed Y => y; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Angle.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Angle.cs new file mode 100644 index 00000000..f16c4d43 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Angle.cs @@ -0,0 +1,180 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Runtime.CompilerServices; + +namespace ManagedDoom +{ + public struct Angle + { + public static readonly Angle Ang0 = new Angle(0x00000000); + public static readonly Angle Ang45 = new Angle(0x20000000); + public static readonly Angle Ang90 = new Angle(0x40000000); + public static readonly Angle Ang180 = new Angle(0x80000000); + public static readonly Angle Ang270 = new Angle(0xC0000000); + + private uint data; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Angle(uint data) + { + this.data = data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Angle(int data) + { + this.data = (uint)data; + } + + public static Angle FromRadian(double radian) + { + var data = Math.Round(0x100000000 * (radian / (2 * Math.PI))); + return new Angle((uint)data); + } + + public static Angle FromDegree(double degree) + { + var data = Math.Round(0x100000000 * (degree / 360)); + return new Angle((uint)data); + } + + public double ToRadian() + { + return 2 * Math.PI * ((double)data / 0x100000000); + } + + public double ToDegree() + { + return 360 * ((double)data / 0x100000000); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Angle Abs(Angle angle) + { + var data = (int)angle.data; + if (data < 0) + { + return new Angle((uint)-data); + } + else + { + return angle; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Angle operator +(Angle a) + { + return a; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Angle operator -(Angle a) + { + return new Angle((uint)-(int)a.data); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Angle operator +(Angle a, Angle b) + { + return new Angle(a.data + b.data); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Angle operator -(Angle a, Angle b) + { + return new Angle(a.data - b.data); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Angle operator *(uint a, Angle b) + { + return new Angle(a * b.data); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Angle operator *(Angle a, uint b) + { + return new Angle(a.data * b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Angle operator /(Angle a, uint b) + { + return new Angle(a.data / b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Angle a, Angle b) + { + return a.data == b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Angle a, Angle b) + { + return a.data != b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <(Angle a, Angle b) + { + return a.data < b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(Angle a, Angle b) + { + return a.data > b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <=(Angle a, Angle b) + { + return a.data <= b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >=(Angle a, Angle b) + { + return a.data >= b.data; + } + + public override bool Equals(object obj) + { + throw new NotSupportedException(); + } + + public override int GetHashCode() + { + return data.GetHashCode(); + } + + public override string ToString() + { + return ToDegree().ToString(); + } + + public uint Data + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => data; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Fixed.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Fixed.cs new file mode 100644 index 00000000..87ac9006 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Fixed.cs @@ -0,0 +1,272 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Runtime.CompilerServices; + +namespace ManagedDoom +{ + public struct Fixed + { + public const int FracBits = 16; + public const int FracUnit = 1 << FracBits; + + public static readonly Fixed Zero = new Fixed(0); + public static readonly Fixed One = new Fixed(FracUnit); + + public static readonly Fixed MaxValue = new Fixed(int.MaxValue); + public static readonly Fixed MinValue = new Fixed(int.MinValue); + + public static readonly Fixed Epsilon = new Fixed(1); + public static readonly Fixed OnePlusEpsilon = new Fixed(FracUnit + 1); + public static readonly Fixed OneMinusEpsilon = new Fixed(FracUnit - 1); + + private int data; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Fixed(int data) + { + this.data = data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed FromInt(int value) + { + return new Fixed(value << FracBits); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed FromFloat(float value) + { + return new Fixed((int)(FracUnit * value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed FromDouble(double value) + { + return new Fixed((int)(FracUnit * value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float ToFloat() + { + return (float)data / FracUnit; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double ToDouble() + { + return (double)data / FracUnit; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Abs(Fixed a) + { + if (a.data < 0) + { + return new Fixed(-a.data); + } + else + { + return a; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator +(Fixed a) + { + return a; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator -(Fixed a) + { + return new Fixed(-a.data); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator +(Fixed a, Fixed b) + { + return new Fixed(a.data + b.data); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator -(Fixed a, Fixed b) + { + return new Fixed(a.data - b.data); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator *(Fixed a, Fixed b) + { + return new Fixed((int)(((long)a.data * (long)b.data) >> FracBits)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator *(int a, Fixed b) + { + return new Fixed(a * b.data); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator *(Fixed a, int b) + { + return new Fixed(a.data * b); + } + + public static Fixed operator /(Fixed a, Fixed b) + { + if ((Math.Abs(a.data) >> 14) >= Math.Abs(b.data)) + { + return new Fixed((a.data ^ b.data) < 0 ? int.MinValue : int.MaxValue); + } + + return FixedDiv2(a, b); + } + + private static Fixed FixedDiv2(Fixed a, Fixed b) + { + var c = ((double)a.data) / ((double)b.data) * FracUnit; + + if (c >= 2147483648.0 || c < -2147483648.0) + { + throw new DivideByZeroException(); + } + + return new Fixed((int)c); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator /(int a, Fixed b) + { + return Fixed.FromInt(a) / b; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator /(Fixed a, int b) + { + return new Fixed(a.data / b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator <<(Fixed a, int b) + { + return new Fixed(a.data << b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed operator >>(Fixed a, int b) + { + return new Fixed(a.data >> b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Fixed a, Fixed b) + { + return a.data == b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Fixed a, Fixed b) + { + return a.data != b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <(Fixed a, Fixed b) + { + return a.data < b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(Fixed a, Fixed b) + { + return a.data > b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <=(Fixed a, Fixed b) + { + return a.data <= b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >=(Fixed a, Fixed b) + { + return a.data >= b.data; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Min(Fixed a, Fixed b) + { + if (a < b) + { + return a; + } + else + { + return b; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Max(Fixed a, Fixed b) + { + if (a < b) + { + return b; + } + else + { + return a; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ToIntFloor() + { + return data >> FracBits; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ToIntCeiling() + { + return (data + FracUnit - 1) >> FracBits; + } + + public override bool Equals(object obj) + { + throw new NotSupportedException(); + } + + public override int GetHashCode() + { + return data.GetHashCode(); + } + + public override string ToString() + { + return ((double)data / FracUnit).ToString(); + } + + public int Data + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => data; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Geometry.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Geometry.cs new file mode 100644 index 00000000..1ada7d7c --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Geometry.cs @@ -0,0 +1,625 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static class Geometry + { + private const int slopeRange = 2048; + private const int slopeBits = 11; + private const int fracToSlopeShift = Fixed.FracBits - slopeBits; + + private static uint SlopeDiv(Fixed num, Fixed den) + { + if ((uint)den.Data < 512) + { + return slopeRange; + } + + var ans = ((uint)num.Data << 3) / ((uint)den.Data >> 8); + + return ans <= slopeRange ? ans : slopeRange; + } + + /// + /// Calculate the distance between the two points. + /// + public static Fixed PointToDist(Fixed fromX, Fixed fromY, Fixed toX, Fixed toY) + { + var dx = Fixed.Abs(toX - fromX); + var dy = Fixed.Abs(toY - fromY); + + if (dy > dx) + { + var temp = dx; + dx = dy; + dy = temp; + } + + // The code below to avoid division by zero is based on Chocolate Doom's implementation. + Fixed frac; + if (dx != Fixed.Zero) + { + frac = dy / dx; + } + else + { + frac = Fixed.Zero; + } + + var angle = (Trig.TanToAngle((uint)frac.Data >> fracToSlopeShift) + Angle.Ang90); + + // Use as cosine. + var dist = dx / Trig.Sin(angle); + + return dist; + } + + /// + /// Calculate on which side of the node the point is. + /// + /// + /// 0 (front) or 1 (back). + /// + public static int PointOnSide(Fixed x, Fixed y, Node node) + { + if (node.Dx == Fixed.Zero) + { + if (x <= node.X) + { + return node.Dy > Fixed.Zero ? 1 : 0; + } + else + { + return node.Dy < Fixed.Zero ? 1 : 0; + } + } + + if (node.Dy == Fixed.Zero) + { + if (y <= node.Y) + { + return node.Dx < Fixed.Zero ? 1 : 0; + } + else + { + return node.Dx > Fixed.Zero ? 1 : 0; + } + } + + var dx = (x - node.X); + var dy = (y - node.Y); + + // Try to quickly decide by looking at sign bits. + if (((node.Dy.Data ^ node.Dx.Data ^ dx.Data ^ dy.Data) & 0x80000000) != 0) + { + if (((node.Dy.Data ^ dx.Data) & 0x80000000) != 0) + { + // Left is negative. + return 1; + } + + return 0; + } + + var left = new Fixed(node.Dy.Data >> Fixed.FracBits) * dx; + var right = dy * new Fixed(node.Dx.Data >> Fixed.FracBits); + + if (right < left) + { + // Front side. + return 0; + } + else + { + // Back side. + return 1; + } + } + + /// + /// Calculate the angle of the line passing through the two points. + /// + public static Angle PointToAngle(Fixed fromX, Fixed fromY, Fixed toX, Fixed toY) + { + var x = toX - fromX; + var y = toY - fromY; + + if (x == Fixed.Zero && y == Fixed.Zero) + { + return Angle.Ang0; + } + + if (x >= Fixed.Zero) + { + // x >= 0 + if (y >= Fixed.Zero) + { + // y >= 0 + if (x > y) + { + // octant 0 + return Trig.TanToAngle(SlopeDiv(y, x)); + } + else + { + // octant 1 + return new Angle(Angle.Ang90.Data - 1) - Trig.TanToAngle(SlopeDiv(x, y)); + } + } + else + { + // y < 0 + y = -y; + + if (x > y) + { + // octant 8 + return -Trig.TanToAngle(SlopeDiv(y, x)); + } + else + { + // octant 7 + return Angle.Ang270 + Trig.TanToAngle(SlopeDiv(x, y)); + } + } + } + else + { + // x < 0 + x = -x; + + if (y >= Fixed.Zero) + { + // y >= 0 + if (x > y) + { + // octant 3 + return new Angle(Angle.Ang180.Data - 1) - Trig.TanToAngle(SlopeDiv(y, x)); + } + else + { + // octant 2 + return Angle.Ang90 + Trig.TanToAngle(SlopeDiv(x, y)); + } + } + else + { + // y < 0 + y = -y; + + if (x > y) + { + // octant 4 + return Angle.Ang180 + Trig.TanToAngle(SlopeDiv(y, x)); + } + else + { + // octant 5 + return new Angle(Angle.Ang270.Data - 1) - Trig.TanToAngle(SlopeDiv(x, y)); + } + } + } + } + + /// + /// Get the subsector which contains the point. + /// + public static Subsector PointInSubsector(Fixed x, Fixed y, Map map) + { + // Single subsector is a special case. + if (map.Nodes.Length == 0) + { + return map.Subsectors[0]; + } + + var nodeNumber = map.Nodes.Length - 1; + + while (!Node.IsSubsector(nodeNumber)) + { + var node = map.Nodes[nodeNumber]; + var side = PointOnSide(x, y, node); + nodeNumber = node.Children[side]; + } + + return map.Subsectors[Node.GetSubsector(nodeNumber)]; + } + + /// + /// Calculate on which side of the line the point is. + /// + /// + /// 0 (front) or 1 (back). + /// + public static int PointOnSegSide(Fixed x, Fixed y, Seg line) + { + var lx = line.Vertex1.X; + var ly = line.Vertex1.Y; + + var ldx = line.Vertex2.X - lx; + var ldy = line.Vertex2.Y - ly; + + if (ldx == Fixed.Zero) + { + if (x <= lx) + { + return ldy > Fixed.Zero ? 1 : 0; + } + else + { + return ldy < Fixed.Zero ? 1 : 0; + } + } + + if (ldy == Fixed.Zero) + { + if (y <= ly) + { + return ldx < Fixed.Zero ? 1 : 0; + } + else + { + return ldx > Fixed.Zero ? 1 : 0; + } + } + + var dx = (x - lx); + var dy = (y - ly); + + // Try to quickly decide by looking at sign bits. + if (((ldy.Data ^ ldx.Data ^ dx.Data ^ dy.Data) & 0x80000000) != 0) + { + if (((ldy.Data ^ dx.Data) & 0x80000000) != 0) + { + // Left is negative. + return 1; + } + else + { + return 0; + } + } + + var left = new Fixed(ldy.Data >> Fixed.FracBits) * dx; + var right = dy * new Fixed(ldx.Data >> Fixed.FracBits); + + if (right < left) + { + // Front side. + return 0; + } + else + { + // Back side. + return 1; + } + } + + /// + /// Calculate on which side of the line the point is. + /// + /// + /// 0 (front) or 1 (back). + /// + public static int PointOnLineSide(Fixed x, Fixed y, LineDef line) + { + if (line.Dx == Fixed.Zero) + { + if (x <= line.Vertex1.X) + { + return line.Dy > Fixed.Zero ? 1 : 0; + } + else + { + return line.Dy < Fixed.Zero ? 1 : 0; + } + } + + if (line.Dy == Fixed.Zero) + { + if (y <= line.Vertex1.Y) + { + return line.Dx < Fixed.Zero ? 1 : 0; + } + else + { + return line.Dx > Fixed.Zero ? 1 : 0; + } + } + + var dx = (x - line.Vertex1.X); + var dy = (y - line.Vertex1.Y); + + var left = new Fixed(line.Dy.Data >> Fixed.FracBits) * dx; + var right = dy * new Fixed(line.Dx.Data >> Fixed.FracBits); + + if (right < left) + { + // Front side. + return 0; + } + else + { + // Back side. + return 1; + } + } + + /// + /// Calculate on which side of the line the box is. + /// + /// + /// 0 (front), 1 (back), or -1 if the box crosses the line. + /// + public static int BoxOnLineSide(Fixed[] box, LineDef line) + { + int p1; + int p2; + + switch (line.SlopeType) + { + case SlopeType.Horizontal: + p1 = box[Box.Top] > line.Vertex1.Y ? 1 : 0; + p2 = box[Box.Bottom] > line.Vertex1.Y ? 1 : 0; + if (line.Dx < Fixed.Zero) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case SlopeType.Vertical: + p1 = box[Box.Right] < line.Vertex1.X ? 1 : 0; + p2 = box[Box.Left] < line.Vertex1.X ? 1 : 0; + if (line.Dy < Fixed.Zero) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case SlopeType.Positive: + p1 = PointOnLineSide(box[Box.Left], box[Box.Top], line); + p2 = PointOnLineSide(box[Box.Right], box[Box.Bottom], line); + break; + + case SlopeType.Negative: + p1 = PointOnLineSide(box[Box.Right], box[Box.Top], line); + p2 = PointOnLineSide(box[Box.Left], box[Box.Bottom], line); + break; + + default: + throw new Exception("Invalid SlopeType."); + } + + if (p1 == p2) + { + return p1; + } + else + { + return -1; + } + } + + /// + /// Calculate on which side of the line the point is. + /// + /// + /// 0 (front) or 1 (back). + /// + public static int PointOnDivLineSide(Fixed x, Fixed y, DivLine line) + { + if (line.Dx == Fixed.Zero) + { + if (x <= line.X) + { + return line.Dy > Fixed.Zero ? 1 : 0; + } + else + { + return line.Dy < Fixed.Zero ? 1 : 0; + } + } + + if (line.Dy == Fixed.Zero) + { + if (y <= line.Y) + { + return line.Dx < Fixed.Zero ? 1 : 0; + } + else + { + return line.Dx > Fixed.Zero ? 1 : 0; + } + } + + var dx = (x - line.X); + var dy = (y - line.Y); + + // Try to quickly decide by looking at sign bits. + if (((line.Dy.Data ^ line.Dx.Data ^ dx.Data ^ dy.Data) & 0x80000000) != 0) + { + if (((line.Dy.Data ^ dx.Data) & 0x80000000) != 0) + { + // Left is negative. + return 1; + } + else + { + return 0; + } + } + + var left = new Fixed(line.Dy.Data >> 8) * new Fixed(dx.Data >> 8); + var right = new Fixed(dy.Data >> 8) * new Fixed(line.Dx.Data >> 8); + + if (right < left) + { + // Front side. + return 0; + } + else + { + // Back side. + return 1; + } + } + + /// + /// Gives an estimation of distance (not exact). + /// + public static Fixed AproxDistance(Fixed dx, Fixed dy) + { + dx = Fixed.Abs(dx); + dy = Fixed.Abs(dy); + + if (dx < dy) + { + return dx + dy - (dx >> 1); + } + else + { + return dx + dy - (dy >> 1); + } + } + + /// + /// Calculate on which side of the line the point is. + /// + /// + /// 0 (front) or 1 (back), or 2 if the box crosses the line. + /// + public static int DivLineSide(Fixed x, Fixed y, DivLine line) + { + if (line.Dx == Fixed.Zero) + { + if (x == line.X) + { + return 2; + } + + if (x <= line.X) + { + return line.Dy > Fixed.Zero ? 1 : 0; + } + + return line.Dy < Fixed.Zero ? 1 : 0; + } + + if (line.Dy == Fixed.Zero) + { + if (x == line.Y) + { + return 2; + } + + if (y <= line.Y) + { + return line.Dx < Fixed.Zero ? 1 : 0; + } + + return line.Dx > Fixed.Zero ? 1 : 0; + } + + var dx = (x - line.X); + var dy = (y - line.Y); + + var left = new Fixed((line.Dy.Data >> Fixed.FracBits) * (dx.Data >> Fixed.FracBits)); + var right = new Fixed((dy.Data >> Fixed.FracBits) * (line.Dx.Data >> Fixed.FracBits)); + + if (right < left) + { + // Front side. + return 0; + } + + if (left == right) + { + return 2; + } + else + { + // Back side. + return 1; + } + } + + /// + /// Calculate on which side of the line the point is. + /// + /// + /// 0 (front) or 1 (back), or 2 if the box crosses the line. + /// + public static int DivLineSide(Fixed x, Fixed y, Node node) + { + if (node.Dx == Fixed.Zero) + { + if (x == node.X) + { + return 2; + } + + if (x <= node.X) + { + return node.Dy > Fixed.Zero ? 1 : 0; + } + + return node.Dy < Fixed.Zero ? 1 : 0; + } + + if (node.Dy == Fixed.Zero) + { + if (x == node.Y) + { + return 2; + } + + if (y <= node.Y) + { + return node.Dx < Fixed.Zero ? 1 : 0; + } + + return node.Dx > Fixed.Zero ? 1 : 0; + } + + var dx = (x - node.X); + var dy = (y - node.Y); + + var left = new Fixed((node.Dy.Data >> Fixed.FracBits) * (dx.Data >> Fixed.FracBits)); + var right = new Fixed((dy.Data >> Fixed.FracBits) * (node.Dx.Data >> Fixed.FracBits)); + + if (right < left) + { + // Front side. + return 0; + } + + if (left == right) + { + return 2; + } + else + { + // Back side. + return 1; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Trig.Tables.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Trig.Tables.cs new file mode 100644 index 00000000..0ea91974 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Trig.Tables.cs @@ -0,0 +1,2085 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static partial class Trig + { + private static readonly int[] fineTangent = + { + -170910304, -56965752, -34178904, -24413316, -18988036, -15535599, -13145455, -11392683, + -10052327, -8994149, -8137527, -7429880, -6835455, -6329090, -5892567, -5512368, + -5178251, -4882318, -4618375, -4381502, -4167737, -3973855, -3797206, -3635590, + -3487165, -3350381, -3223918, -3106651, -2997613, -2895966, -2800983, -2712030, + -2628549, -2550052, -2476104, -2406322, -2340362, -2277919, -2218719, -2162516, + -2109087, -2058233, -2009771, -1963536, -1919378, -1877161, -1836758, -1798063, + -1760956, -1725348, -1691149, -1658278, -1626658, -1596220, -1566898, -1538632, + -1511367, -1485049, -1459630, -1435065, -1411312, -1388330, -1366084, -1344537, + -1323658, -1303416, -1283783, -1264730, -1246234, -1228269, -1210813, -1193846, + -1177345, -1161294, -1145673, -1130465, -1115654, -1101225, -1087164, -1073455, + -1060087, -1047046, -1034322, -1021901, -1009774, -997931, -986361, -975054, + -964003, -953199, -942633, -932298, -922186, -912289, -902602, -893117, + -883829, -874730, -865817, -857081, -848520, -840127, -831898, -823827, + -815910, -808143, -800521, -793041, -785699, -778490, -771411, -764460, + -757631, -750922, -744331, -737853, -731486, -725227, -719074, -713023, + -707072, -701219, -695462, -689797, -684223, -678737, -673338, -668024, + -662792, -657640, -652568, -647572, -642651, -637803, -633028, -628323, + -623686, -619117, -614613, -610174, -605798, -601483, -597229, -593033, + -588896, -584815, -580789, -576818, -572901, -569035, -565221, -561456, + -557741, -554074, -550455, -546881, -543354, -539870, -536431, -533034, + -529680, -526366, -523094, -519861, -516667, -513512, -510394, -507313, + -504269, -501261, -498287, -495348, -492443, -489571, -486732, -483925, + -481150, -478406, -475692, -473009, -470355, -467730, -465133, -462565, + -460024, -457511, -455024, -452564, -450129, -447720, -445337, -442978, + -440643, -438332, -436045, -433781, -431540, -429321, -427125, -424951, + -422798, -420666, -418555, -416465, -414395, -412344, -410314, -408303, + -406311, -404338, -402384, -400448, -398530, -396630, -394747, -392882, + -391034, -389202, -387387, -385589, -383807, -382040, -380290, -378555, + -376835, -375130, -373440, -371765, -370105, -368459, -366826, -365208, + -363604, -362013, -360436, -358872, -357321, -355783, -354257, -352744, + -351244, -349756, -348280, -346816, -345364, -343924, -342495, -341078, + -339671, -338276, -336892, -335519, -334157, -332805, -331464, -330133, + -328812, -327502, -326201, -324910, -323629, -322358, -321097, -319844, + -318601, -317368, -316143, -314928, -313721, -312524, -311335, -310154, + -308983, -307819, -306664, -305517, -304379, -303248, -302126, -301011, + -299904, -298805, -297714, -296630, -295554, -294485, -293423, -292369, + -291322, -290282, -289249, -288223, -287204, -286192, -285186, -284188, + -283195, -282210, -281231, -280258, -279292, -278332, -277378, -276430, + -275489, -274553, -273624, -272700, -271782, -270871, -269965, -269064, + -268169, -267280, -266397, -265519, -264646, -263779, -262917, -262060, + -261209, -260363, -259522, -258686, -257855, -257029, -256208, -255392, + -254581, -253774, -252973, -252176, -251384, -250596, -249813, -249035, + -248261, -247492, -246727, -245966, -245210, -244458, -243711, -242967, + -242228, -241493, -240763, -240036, -239314, -238595, -237881, -237170, + -236463, -235761, -235062, -234367, -233676, -232988, -232304, -231624, + -230948, -230275, -229606, -228941, -228279, -227621, -226966, -226314, + -225666, -225022, -224381, -223743, -223108, -222477, -221849, -221225, + -220603, -219985, -219370, -218758, -218149, -217544, -216941, -216341, + -215745, -215151, -214561, -213973, -213389, -212807, -212228, -211652, + -211079, -210509, -209941, -209376, -208815, -208255, -207699, -207145, + -206594, -206045, -205500, -204956, -204416, -203878, -203342, -202809, + -202279, -201751, -201226, -200703, -200182, -199664, -199149, -198636, + -198125, -197616, -197110, -196606, -196105, -195606, -195109, -194614, + -194122, -193631, -193143, -192658, -192174, -191693, -191213, -190736, + -190261, -189789, -189318, -188849, -188382, -187918, -187455, -186995, + -186536, -186080, -185625, -185173, -184722, -184274, -183827, -183382, + -182939, -182498, -182059, -181622, -181186, -180753, -180321, -179891, + -179463, -179037, -178612, -178190, -177769, -177349, -176932, -176516, + -176102, -175690, -175279, -174870, -174463, -174057, -173653, -173251, + -172850, -172451, -172053, -171657, -171263, -170870, -170479, -170089, + -169701, -169315, -168930, -168546, -168164, -167784, -167405, -167027, + -166651, -166277, -165904, -165532, -165162, -164793, -164426, -164060, + -163695, -163332, -162970, -162610, -162251, -161893, -161537, -161182, + -160828, -160476, -160125, -159775, -159427, -159079, -158734, -158389, + -158046, -157704, -157363, -157024, -156686, -156349, -156013, -155678, + -155345, -155013, -154682, -154352, -154024, -153697, -153370, -153045, + -152722, -152399, -152077, -151757, -151438, -151120, -150803, -150487, + -150172, -149859, -149546, -149235, -148924, -148615, -148307, -148000, + -147693, -147388, -147084, -146782, -146480, -146179, -145879, -145580, + -145282, -144986, -144690, -144395, -144101, -143808, -143517, -143226, + -142936, -142647, -142359, -142072, -141786, -141501, -141217, -140934, + -140651, -140370, -140090, -139810, -139532, -139254, -138977, -138701, + -138426, -138152, -137879, -137607, -137335, -137065, -136795, -136526, + -136258, -135991, -135725, -135459, -135195, -134931, -134668, -134406, + -134145, -133884, -133625, -133366, -133108, -132851, -132594, -132339, + -132084, -131830, -131576, -131324, -131072, -130821, -130571, -130322, + -130073, -129825, -129578, -129332, -129086, -128841, -128597, -128353, + -128111, -127869, -127627, -127387, -127147, -126908, -126669, -126432, + -126195, -125959, -125723, -125488, -125254, -125020, -124787, -124555, + -124324, -124093, -123863, -123633, -123404, -123176, -122949, -122722, + -122496, -122270, -122045, -121821, -121597, -121374, -121152, -120930, + -120709, -120489, -120269, -120050, -119831, -119613, -119396, -119179, + -118963, -118747, -118532, -118318, -118104, -117891, -117678, -117466, + -117254, -117044, -116833, -116623, -116414, -116206, -115998, -115790, + -115583, -115377, -115171, -114966, -114761, -114557, -114354, -114151, + -113948, -113746, -113545, -113344, -113143, -112944, -112744, -112546, + -112347, -112150, -111952, -111756, -111560, -111364, -111169, -110974, + -110780, -110586, -110393, -110200, -110008, -109817, -109626, -109435, + -109245, -109055, -108866, -108677, -108489, -108301, -108114, -107927, + -107741, -107555, -107369, -107184, -107000, -106816, -106632, -106449, + -106266, -106084, -105902, -105721, -105540, -105360, -105180, -105000, + -104821, -104643, -104465, -104287, -104109, -103933, -103756, -103580, + -103404, -103229, -103054, -102880, -102706, -102533, -102360, -102187, + -102015, -101843, -101671, -101500, -101330, -101159, -100990, -100820, + -100651, -100482, -100314, -100146, -99979, -99812, -99645, -99479, + -99313, -99148, -98982, -98818, -98653, -98489, -98326, -98163, + -98000, -97837, -97675, -97513, -97352, -97191, -97030, -96870, + -96710, -96551, -96391, -96233, -96074, -95916, -95758, -95601, + -95444, -95287, -95131, -94975, -94819, -94664, -94509, -94354, + -94200, -94046, -93892, -93739, -93586, -93434, -93281, -93129, + -92978, -92826, -92675, -92525, -92375, -92225, -92075, -91926, + -91777, -91628, -91480, -91332, -91184, -91036, -90889, -90742, + -90596, -90450, -90304, -90158, -90013, -89868, -89724, -89579, + -89435, -89292, -89148, -89005, -88862, -88720, -88577, -88435, + -88294, -88152, -88011, -87871, -87730, -87590, -87450, -87310, + -87171, -87032, -86893, -86755, -86616, -86479, -86341, -86204, + -86066, -85930, -85793, -85657, -85521, -85385, -85250, -85114, + -84980, -84845, -84710, -84576, -84443, -84309, -84176, -84043, + -83910, -83777, -83645, -83513, -83381, -83250, -83118, -82987, + -82857, -82726, -82596, -82466, -82336, -82207, -82078, -81949, + -81820, -81691, -81563, -81435, -81307, -81180, -81053, -80925, + -80799, -80672, -80546, -80420, -80294, -80168, -80043, -79918, + -79793, -79668, -79544, -79420, -79296, -79172, -79048, -78925, + -78802, -78679, -78557, -78434, -78312, -78190, -78068, -77947, + -77826, -77705, -77584, -77463, -77343, -77223, -77103, -76983, + -76864, -76744, -76625, -76506, -76388, -76269, -76151, -76033, + -75915, -75797, -75680, -75563, -75446, -75329, -75213, -75096, + -74980, -74864, -74748, -74633, -74517, -74402, -74287, -74172, + -74058, -73944, -73829, -73715, -73602, -73488, -73375, -73262, + -73149, -73036, -72923, -72811, -72699, -72587, -72475, -72363, + -72252, -72140, -72029, -71918, -71808, -71697, -71587, -71477, + -71367, -71257, -71147, -71038, -70929, -70820, -70711, -70602, + -70494, -70385, -70277, -70169, -70061, -69954, -69846, -69739, + -69632, -69525, -69418, -69312, -69205, -69099, -68993, -68887, + -68781, -68676, -68570, -68465, -68360, -68255, -68151, -68046, + -67942, -67837, -67733, -67629, -67526, -67422, -67319, -67216, + -67113, -67010, -66907, -66804, -66702, -66600, -66498, -66396, + -66294, -66192, -66091, -65989, -65888, -65787, -65686, -65586, + -65485, -65385, -65285, -65185, -65085, -64985, -64885, -64786, + -64687, -64587, -64488, -64389, -64291, -64192, -64094, -63996, + -63897, -63799, -63702, -63604, -63506, -63409, -63312, -63215, + -63118, -63021, -62924, -62828, -62731, -62635, -62539, -62443, + -62347, -62251, -62156, -62060, -61965, -61870, -61775, -61680, + -61585, -61491, -61396, -61302, -61208, -61114, -61020, -60926, + -60833, -60739, -60646, -60552, -60459, -60366, -60273, -60181, + -60088, -59996, -59903, -59811, -59719, -59627, -59535, -59444, + -59352, -59261, -59169, -59078, -58987, -58896, -58805, -58715, + -58624, -58534, -58443, -58353, -58263, -58173, -58083, -57994, + -57904, -57815, -57725, -57636, -57547, -57458, -57369, -57281, + -57192, -57104, -57015, -56927, -56839, -56751, -56663, -56575, + -56487, -56400, -56312, -56225, -56138, -56051, -55964, -55877, + -55790, -55704, -55617, -55531, -55444, -55358, -55272, -55186, + -55100, -55015, -54929, -54843, -54758, -54673, -54587, -54502, + -54417, -54333, -54248, -54163, -54079, -53994, -53910, -53826, + -53741, -53657, -53574, -53490, -53406, -53322, -53239, -53156, + -53072, -52989, -52906, -52823, -52740, -52657, -52575, -52492, + -52410, -52327, -52245, -52163, -52081, -51999, -51917, -51835, + -51754, -51672, -51591, -51509, -51428, -51347, -51266, -51185, + -51104, -51023, -50942, -50862, -50781, -50701, -50621, -50540, + -50460, -50380, -50300, -50221, -50141, -50061, -49982, -49902, + -49823, -49744, -49664, -49585, -49506, -49427, -49349, -49270, + -49191, -49113, -49034, -48956, -48878, -48799, -48721, -48643, + -48565, -48488, -48410, -48332, -48255, -48177, -48100, -48022, + -47945, -47868, -47791, -47714, -47637, -47560, -47484, -47407, + -47331, -47254, -47178, -47102, -47025, -46949, -46873, -46797, + -46721, -46646, -46570, -46494, -46419, -46343, -46268, -46193, + -46118, -46042, -45967, -45892, -45818, -45743, -45668, -45593, + -45519, -45444, -45370, -45296, -45221, -45147, -45073, -44999, + -44925, -44851, -44778, -44704, -44630, -44557, -44483, -44410, + -44337, -44263, -44190, -44117, -44044, -43971, -43898, -43826, + -43753, -43680, -43608, -43535, -43463, -43390, -43318, -43246, + -43174, -43102, -43030, -42958, -42886, -42814, -42743, -42671, + -42600, -42528, -42457, -42385, -42314, -42243, -42172, -42101, + -42030, -41959, -41888, -41817, -41747, -41676, -41605, -41535, + -41465, -41394, -41324, -41254, -41184, -41113, -41043, -40973, + -40904, -40834, -40764, -40694, -40625, -40555, -40486, -40416, + -40347, -40278, -40208, -40139, -40070, -40001, -39932, -39863, + -39794, -39726, -39657, -39588, -39520, -39451, -39383, -39314, + -39246, -39178, -39110, -39042, -38973, -38905, -38837, -38770, + -38702, -38634, -38566, -38499, -38431, -38364, -38296, -38229, + -38161, -38094, -38027, -37960, -37893, -37826, -37759, -37692, + -37625, -37558, -37491, -37425, -37358, -37291, -37225, -37158, + -37092, -37026, -36959, -36893, -36827, -36761, -36695, -36629, + -36563, -36497, -36431, -36365, -36300, -36234, -36168, -36103, + -36037, -35972, -35907, -35841, -35776, -35711, -35646, -35580, + -35515, -35450, -35385, -35321, -35256, -35191, -35126, -35062, + -34997, -34932, -34868, -34803, -34739, -34675, -34610, -34546, + -34482, -34418, -34354, -34289, -34225, -34162, -34098, -34034, + -33970, -33906, -33843, -33779, -33715, -33652, -33588, -33525, + -33461, -33398, -33335, -33272, -33208, -33145, -33082, -33019, + -32956, -32893, -32830, -32767, -32705, -32642, -32579, -32516, + -32454, -32391, -32329, -32266, -32204, -32141, -32079, -32017, + -31955, -31892, -31830, -31768, -31706, -31644, -31582, -31520, + -31458, -31396, -31335, -31273, -31211, -31150, -31088, -31026, + -30965, -30904, -30842, -30781, -30719, -30658, -30597, -30536, + -30474, -30413, -30352, -30291, -30230, -30169, -30108, -30048, + -29987, -29926, -29865, -29805, -29744, -29683, -29623, -29562, + -29502, -29441, -29381, -29321, -29260, -29200, -29140, -29080, + -29020, -28959, -28899, -28839, -28779, -28719, -28660, -28600, + -28540, -28480, -28420, -28361, -28301, -28241, -28182, -28122, + -28063, -28003, -27944, -27884, -27825, -27766, -27707, -27647, + -27588, -27529, -27470, -27411, -27352, -27293, -27234, -27175, + -27116, -27057, -26998, -26940, -26881, -26822, -26763, -26705, + -26646, -26588, -26529, -26471, -26412, -26354, -26295, -26237, + -26179, -26120, -26062, -26004, -25946, -25888, -25830, -25772, + -25714, -25656, -25598, -25540, -25482, -25424, -25366, -25308, + -25251, -25193, -25135, -25078, -25020, -24962, -24905, -24847, + -24790, -24732, -24675, -24618, -24560, -24503, -24446, -24389, + -24331, -24274, -24217, -24160, -24103, -24046, -23989, -23932, + -23875, -23818, -23761, -23704, -23647, -23591, -23534, -23477, + -23420, -23364, -23307, -23250, -23194, -23137, -23081, -23024, + -22968, -22911, -22855, -22799, -22742, -22686, -22630, -22573, + -22517, -22461, -22405, -22349, -22293, -22237, -22181, -22125, + -22069, -22013, -21957, -21901, -21845, -21789, -21733, -21678, + -21622, -21566, -21510, -21455, -21399, -21343, -21288, -21232, + -21177, -21121, -21066, -21010, -20955, -20900, -20844, -20789, + -20734, -20678, -20623, -20568, -20513, -20457, -20402, -20347, + -20292, -20237, -20182, -20127, -20072, -20017, -19962, -19907, + -19852, -19797, -19742, -19688, -19633, -19578, -19523, -19469, + -19414, -19359, -19305, -19250, -19195, -19141, -19086, -19032, + -18977, -18923, -18868, -18814, -18760, -18705, -18651, -18597, + -18542, -18488, -18434, -18380, -18325, -18271, -18217, -18163, + -18109, -18055, -18001, -17946, -17892, -17838, -17784, -17731, + -17677, -17623, -17569, -17515, -17461, -17407, -17353, -17300, + -17246, -17192, -17138, -17085, -17031, -16977, -16924, -16870, + -16817, -16763, -16710, -16656, -16603, -16549, -16496, -16442, + -16389, -16335, -16282, -16229, -16175, -16122, -16069, -16015, + -15962, -15909, -15856, -15802, -15749, -15696, -15643, -15590, + -15537, -15484, -15431, -15378, -15325, -15272, -15219, -15166, + -15113, -15060, -15007, -14954, -14901, -14848, -14795, -14743, + -14690, -14637, -14584, -14531, -14479, -14426, -14373, -14321, + -14268, -14215, -14163, -14110, -14057, -14005, -13952, -13900, + -13847, -13795, -13742, -13690, -13637, -13585, -13533, -13480, + -13428, -13375, -13323, -13271, -13218, -13166, -13114, -13062, + -13009, -12957, -12905, -12853, -12800, -12748, -12696, -12644, + -12592, -12540, -12488, -12436, -12383, -12331, -12279, -12227, + -12175, -12123, -12071, -12019, -11967, -11916, -11864, -11812, + -11760, -11708, -11656, -11604, -11552, -11501, -11449, -11397, + -11345, -11293, -11242, -11190, -11138, -11086, -11035, -10983, + -10931, -10880, -10828, -10777, -10725, -10673, -10622, -10570, + -10519, -10467, -10415, -10364, -10312, -10261, -10209, -10158, + -10106, -10055, -10004, -9952, -9901, -9849, -9798, -9747, + -9695, -9644, -9592, -9541, -9490, -9438, -9387, -9336, + -9285, -9233, -9182, -9131, -9080, -9028, -8977, -8926, + -8875, -8824, -8772, -8721, -8670, -8619, -8568, -8517, + -8466, -8414, -8363, -8312, -8261, -8210, -8159, -8108, + -8057, -8006, -7955, -7904, -7853, -7802, -7751, -7700, + -7649, -7598, -7547, -7496, -7445, -7395, -7344, -7293, + -7242, -7191, -7140, -7089, -7038, -6988, -6937, -6886, + -6835, -6784, -6733, -6683, -6632, -6581, -6530, -6480, + -6429, -6378, -6327, -6277, -6226, -6175, -6124, -6074, + -6023, -5972, -5922, -5871, -5820, -5770, -5719, -5668, + -5618, -5567, -5517, -5466, -5415, -5365, -5314, -5264, + -5213, -5162, -5112, -5061, -5011, -4960, -4910, -4859, + -4808, -4758, -4707, -4657, -4606, -4556, -4505, -4455, + -4404, -4354, -4303, -4253, -4202, -4152, -4101, -4051, + -4001, -3950, -3900, -3849, -3799, -3748, -3698, -3648, + -3597, -3547, -3496, -3446, -3395, -3345, -3295, -3244, + -3194, -3144, -3093, -3043, -2992, -2942, -2892, -2841, + -2791, -2741, -2690, -2640, -2590, -2539, -2489, -2439, + -2388, -2338, -2288, -2237, -2187, -2137, -2086, -2036, + -1986, -1935, -1885, -1835, -1784, -1734, -1684, -1633, + -1583, -1533, -1483, -1432, -1382, -1332, -1281, -1231, + -1181, -1131, -1080, -1030, -980, -929, -879, -829, + -779, -728, -678, -628, -578, -527, -477, -427, + -376, -326, -276, -226, -175, -125, -75, -25, + 25, 75, 125, 175, 226, 276, 326, 376, + 427, 477, 527, 578, 628, 678, 728, 779, + 829, 879, 929, 980, 1030, 1080, 1131, 1181, + 1231, 1281, 1332, 1382, 1432, 1483, 1533, 1583, + 1633, 1684, 1734, 1784, 1835, 1885, 1935, 1986, + 2036, 2086, 2137, 2187, 2237, 2288, 2338, 2388, + 2439, 2489, 2539, 2590, 2640, 2690, 2741, 2791, + 2841, 2892, 2942, 2992, 3043, 3093, 3144, 3194, + 3244, 3295, 3345, 3395, 3446, 3496, 3547, 3597, + 3648, 3698, 3748, 3799, 3849, 3900, 3950, 4001, + 4051, 4101, 4152, 4202, 4253, 4303, 4354, 4404, + 4455, 4505, 4556, 4606, 4657, 4707, 4758, 4808, + 4859, 4910, 4960, 5011, 5061, 5112, 5162, 5213, + 5264, 5314, 5365, 5415, 5466, 5517, 5567, 5618, + 5668, 5719, 5770, 5820, 5871, 5922, 5972, 6023, + 6074, 6124, 6175, 6226, 6277, 6327, 6378, 6429, + 6480, 6530, 6581, 6632, 6683, 6733, 6784, 6835, + 6886, 6937, 6988, 7038, 7089, 7140, 7191, 7242, + 7293, 7344, 7395, 7445, 7496, 7547, 7598, 7649, + 7700, 7751, 7802, 7853, 7904, 7955, 8006, 8057, + 8108, 8159, 8210, 8261, 8312, 8363, 8414, 8466, + 8517, 8568, 8619, 8670, 8721, 8772, 8824, 8875, + 8926, 8977, 9028, 9080, 9131, 9182, 9233, 9285, + 9336, 9387, 9438, 9490, 9541, 9592, 9644, 9695, + 9747, 9798, 9849, 9901, 9952, 10004, 10055, 10106, + 10158, 10209, 10261, 10312, 10364, 10415, 10467, 10519, + 10570, 10622, 10673, 10725, 10777, 10828, 10880, 10931, + 10983, 11035, 11086, 11138, 11190, 11242, 11293, 11345, + 11397, 11449, 11501, 11552, 11604, 11656, 11708, 11760, + 11812, 11864, 11916, 11967, 12019, 12071, 12123, 12175, + 12227, 12279, 12331, 12383, 12436, 12488, 12540, 12592, + 12644, 12696, 12748, 12800, 12853, 12905, 12957, 13009, + 13062, 13114, 13166, 13218, 13271, 13323, 13375, 13428, + 13480, 13533, 13585, 13637, 13690, 13742, 13795, 13847, + 13900, 13952, 14005, 14057, 14110, 14163, 14215, 14268, + 14321, 14373, 14426, 14479, 14531, 14584, 14637, 14690, + 14743, 14795, 14848, 14901, 14954, 15007, 15060, 15113, + 15166, 15219, 15272, 15325, 15378, 15431, 15484, 15537, + 15590, 15643, 15696, 15749, 15802, 15856, 15909, 15962, + 16015, 16069, 16122, 16175, 16229, 16282, 16335, 16389, + 16442, 16496, 16549, 16603, 16656, 16710, 16763, 16817, + 16870, 16924, 16977, 17031, 17085, 17138, 17192, 17246, + 17300, 17353, 17407, 17461, 17515, 17569, 17623, 17677, + 17731, 17784, 17838, 17892, 17946, 18001, 18055, 18109, + 18163, 18217, 18271, 18325, 18380, 18434, 18488, 18542, + 18597, 18651, 18705, 18760, 18814, 18868, 18923, 18977, + 19032, 19086, 19141, 19195, 19250, 19305, 19359, 19414, + 19469, 19523, 19578, 19633, 19688, 19742, 19797, 19852, + 19907, 19962, 20017, 20072, 20127, 20182, 20237, 20292, + 20347, 20402, 20457, 20513, 20568, 20623, 20678, 20734, + 20789, 20844, 20900, 20955, 21010, 21066, 21121, 21177, + 21232, 21288, 21343, 21399, 21455, 21510, 21566, 21622, + 21678, 21733, 21789, 21845, 21901, 21957, 22013, 22069, + 22125, 22181, 22237, 22293, 22349, 22405, 22461, 22517, + 22573, 22630, 22686, 22742, 22799, 22855, 22911, 22968, + 23024, 23081, 23137, 23194, 23250, 23307, 23364, 23420, + 23477, 23534, 23591, 23647, 23704, 23761, 23818, 23875, + 23932, 23989, 24046, 24103, 24160, 24217, 24274, 24331, + 24389, 24446, 24503, 24560, 24618, 24675, 24732, 24790, + 24847, 24905, 24962, 25020, 25078, 25135, 25193, 25251, + 25308, 25366, 25424, 25482, 25540, 25598, 25656, 25714, + 25772, 25830, 25888, 25946, 26004, 26062, 26120, 26179, + 26237, 26295, 26354, 26412, 26471, 26529, 26588, 26646, + 26705, 26763, 26822, 26881, 26940, 26998, 27057, 27116, + 27175, 27234, 27293, 27352, 27411, 27470, 27529, 27588, + 27647, 27707, 27766, 27825, 27884, 27944, 28003, 28063, + 28122, 28182, 28241, 28301, 28361, 28420, 28480, 28540, + 28600, 28660, 28719, 28779, 28839, 28899, 28959, 29020, + 29080, 29140, 29200, 29260, 29321, 29381, 29441, 29502, + 29562, 29623, 29683, 29744, 29805, 29865, 29926, 29987, + 30048, 30108, 30169, 30230, 30291, 30352, 30413, 30474, + 30536, 30597, 30658, 30719, 30781, 30842, 30904, 30965, + 31026, 31088, 31150, 31211, 31273, 31335, 31396, 31458, + 31520, 31582, 31644, 31706, 31768, 31830, 31892, 31955, + 32017, 32079, 32141, 32204, 32266, 32329, 32391, 32454, + 32516, 32579, 32642, 32705, 32767, 32830, 32893, 32956, + 33019, 33082, 33145, 33208, 33272, 33335, 33398, 33461, + 33525, 33588, 33652, 33715, 33779, 33843, 33906, 33970, + 34034, 34098, 34162, 34225, 34289, 34354, 34418, 34482, + 34546, 34610, 34675, 34739, 34803, 34868, 34932, 34997, + 35062, 35126, 35191, 35256, 35321, 35385, 35450, 35515, + 35580, 35646, 35711, 35776, 35841, 35907, 35972, 36037, + 36103, 36168, 36234, 36300, 36365, 36431, 36497, 36563, + 36629, 36695, 36761, 36827, 36893, 36959, 37026, 37092, + 37158, 37225, 37291, 37358, 37425, 37491, 37558, 37625, + 37692, 37759, 37826, 37893, 37960, 38027, 38094, 38161, + 38229, 38296, 38364, 38431, 38499, 38566, 38634, 38702, + 38770, 38837, 38905, 38973, 39042, 39110, 39178, 39246, + 39314, 39383, 39451, 39520, 39588, 39657, 39726, 39794, + 39863, 39932, 40001, 40070, 40139, 40208, 40278, 40347, + 40416, 40486, 40555, 40625, 40694, 40764, 40834, 40904, + 40973, 41043, 41113, 41184, 41254, 41324, 41394, 41465, + 41535, 41605, 41676, 41747, 41817, 41888, 41959, 42030, + 42101, 42172, 42243, 42314, 42385, 42457, 42528, 42600, + 42671, 42743, 42814, 42886, 42958, 43030, 43102, 43174, + 43246, 43318, 43390, 43463, 43535, 43608, 43680, 43753, + 43826, 43898, 43971, 44044, 44117, 44190, 44263, 44337, + 44410, 44483, 44557, 44630, 44704, 44778, 44851, 44925, + 44999, 45073, 45147, 45221, 45296, 45370, 45444, 45519, + 45593, 45668, 45743, 45818, 45892, 45967, 46042, 46118, + 46193, 46268, 46343, 46419, 46494, 46570, 46646, 46721, + 46797, 46873, 46949, 47025, 47102, 47178, 47254, 47331, + 47407, 47484, 47560, 47637, 47714, 47791, 47868, 47945, + 48022, 48100, 48177, 48255, 48332, 48410, 48488, 48565, + 48643, 48721, 48799, 48878, 48956, 49034, 49113, 49191, + 49270, 49349, 49427, 49506, 49585, 49664, 49744, 49823, + 49902, 49982, 50061, 50141, 50221, 50300, 50380, 50460, + 50540, 50621, 50701, 50781, 50862, 50942, 51023, 51104, + 51185, 51266, 51347, 51428, 51509, 51591, 51672, 51754, + 51835, 51917, 51999, 52081, 52163, 52245, 52327, 52410, + 52492, 52575, 52657, 52740, 52823, 52906, 52989, 53072, + 53156, 53239, 53322, 53406, 53490, 53574, 53657, 53741, + 53826, 53910, 53994, 54079, 54163, 54248, 54333, 54417, + 54502, 54587, 54673, 54758, 54843, 54929, 55015, 55100, + 55186, 55272, 55358, 55444, 55531, 55617, 55704, 55790, + 55877, 55964, 56051, 56138, 56225, 56312, 56400, 56487, + 56575, 56663, 56751, 56839, 56927, 57015, 57104, 57192, + 57281, 57369, 57458, 57547, 57636, 57725, 57815, 57904, + 57994, 58083, 58173, 58263, 58353, 58443, 58534, 58624, + 58715, 58805, 58896, 58987, 59078, 59169, 59261, 59352, + 59444, 59535, 59627, 59719, 59811, 59903, 59996, 60088, + 60181, 60273, 60366, 60459, 60552, 60646, 60739, 60833, + 60926, 61020, 61114, 61208, 61302, 61396, 61491, 61585, + 61680, 61775, 61870, 61965, 62060, 62156, 62251, 62347, + 62443, 62539, 62635, 62731, 62828, 62924, 63021, 63118, + 63215, 63312, 63409, 63506, 63604, 63702, 63799, 63897, + 63996, 64094, 64192, 64291, 64389, 64488, 64587, 64687, + 64786, 64885, 64985, 65085, 65185, 65285, 65385, 65485, + 65586, 65686, 65787, 65888, 65989, 66091, 66192, 66294, + 66396, 66498, 66600, 66702, 66804, 66907, 67010, 67113, + 67216, 67319, 67422, 67526, 67629, 67733, 67837, 67942, + 68046, 68151, 68255, 68360, 68465, 68570, 68676, 68781, + 68887, 68993, 69099, 69205, 69312, 69418, 69525, 69632, + 69739, 69846, 69954, 70061, 70169, 70277, 70385, 70494, + 70602, 70711, 70820, 70929, 71038, 71147, 71257, 71367, + 71477, 71587, 71697, 71808, 71918, 72029, 72140, 72252, + 72363, 72475, 72587, 72699, 72811, 72923, 73036, 73149, + 73262, 73375, 73488, 73602, 73715, 73829, 73944, 74058, + 74172, 74287, 74402, 74517, 74633, 74748, 74864, 74980, + 75096, 75213, 75329, 75446, 75563, 75680, 75797, 75915, + 76033, 76151, 76269, 76388, 76506, 76625, 76744, 76864, + 76983, 77103, 77223, 77343, 77463, 77584, 77705, 77826, + 77947, 78068, 78190, 78312, 78434, 78557, 78679, 78802, + 78925, 79048, 79172, 79296, 79420, 79544, 79668, 79793, + 79918, 80043, 80168, 80294, 80420, 80546, 80672, 80799, + 80925, 81053, 81180, 81307, 81435, 81563, 81691, 81820, + 81949, 82078, 82207, 82336, 82466, 82596, 82726, 82857, + 82987, 83118, 83250, 83381, 83513, 83645, 83777, 83910, + 84043, 84176, 84309, 84443, 84576, 84710, 84845, 84980, + 85114, 85250, 85385, 85521, 85657, 85793, 85930, 86066, + 86204, 86341, 86479, 86616, 86755, 86893, 87032, 87171, + 87310, 87450, 87590, 87730, 87871, 88011, 88152, 88294, + 88435, 88577, 88720, 88862, 89005, 89148, 89292, 89435, + 89579, 89724, 89868, 90013, 90158, 90304, 90450, 90596, + 90742, 90889, 91036, 91184, 91332, 91480, 91628, 91777, + 91926, 92075, 92225, 92375, 92525, 92675, 92826, 92978, + 93129, 93281, 93434, 93586, 93739, 93892, 94046, 94200, + 94354, 94509, 94664, 94819, 94975, 95131, 95287, 95444, + 95601, 95758, 95916, 96074, 96233, 96391, 96551, 96710, + 96870, 97030, 97191, 97352, 97513, 97675, 97837, 98000, + 98163, 98326, 98489, 98653, 98818, 98982, 99148, 99313, + 99479, 99645, 99812, 99979, 100146, 100314, 100482, 100651, + 100820, 100990, 101159, 101330, 101500, 101671, 101843, 102015, + 102187, 102360, 102533, 102706, 102880, 103054, 103229, 103404, + 103580, 103756, 103933, 104109, 104287, 104465, 104643, 104821, + 105000, 105180, 105360, 105540, 105721, 105902, 106084, 106266, + 106449, 106632, 106816, 107000, 107184, 107369, 107555, 107741, + 107927, 108114, 108301, 108489, 108677, 108866, 109055, 109245, + 109435, 109626, 109817, 110008, 110200, 110393, 110586, 110780, + 110974, 111169, 111364, 111560, 111756, 111952, 112150, 112347, + 112546, 112744, 112944, 113143, 113344, 113545, 113746, 113948, + 114151, 114354, 114557, 114761, 114966, 115171, 115377, 115583, + 115790, 115998, 116206, 116414, 116623, 116833, 117044, 117254, + 117466, 117678, 117891, 118104, 118318, 118532, 118747, 118963, + 119179, 119396, 119613, 119831, 120050, 120269, 120489, 120709, + 120930, 121152, 121374, 121597, 121821, 122045, 122270, 122496, + 122722, 122949, 123176, 123404, 123633, 123863, 124093, 124324, + 124555, 124787, 125020, 125254, 125488, 125723, 125959, 126195, + 126432, 126669, 126908, 127147, 127387, 127627, 127869, 128111, + 128353, 128597, 128841, 129086, 129332, 129578, 129825, 130073, + 130322, 130571, 130821, 131072, 131324, 131576, 131830, 132084, + 132339, 132594, 132851, 133108, 133366, 133625, 133884, 134145, + 134406, 134668, 134931, 135195, 135459, 135725, 135991, 136258, + 136526, 136795, 137065, 137335, 137607, 137879, 138152, 138426, + 138701, 138977, 139254, 139532, 139810, 140090, 140370, 140651, + 140934, 141217, 141501, 141786, 142072, 142359, 142647, 142936, + 143226, 143517, 143808, 144101, 144395, 144690, 144986, 145282, + 145580, 145879, 146179, 146480, 146782, 147084, 147388, 147693, + 148000, 148307, 148615, 148924, 149235, 149546, 149859, 150172, + 150487, 150803, 151120, 151438, 151757, 152077, 152399, 152722, + 153045, 153370, 153697, 154024, 154352, 154682, 155013, 155345, + 155678, 156013, 156349, 156686, 157024, 157363, 157704, 158046, + 158389, 158734, 159079, 159427, 159775, 160125, 160476, 160828, + 161182, 161537, 161893, 162251, 162610, 162970, 163332, 163695, + 164060, 164426, 164793, 165162, 165532, 165904, 166277, 166651, + 167027, 167405, 167784, 168164, 168546, 168930, 169315, 169701, + 170089, 170479, 170870, 171263, 171657, 172053, 172451, 172850, + 173251, 173653, 174057, 174463, 174870, 175279, 175690, 176102, + 176516, 176932, 177349, 177769, 178190, 178612, 179037, 179463, + 179891, 180321, 180753, 181186, 181622, 182059, 182498, 182939, + 183382, 183827, 184274, 184722, 185173, 185625, 186080, 186536, + 186995, 187455, 187918, 188382, 188849, 189318, 189789, 190261, + 190736, 191213, 191693, 192174, 192658, 193143, 193631, 194122, + 194614, 195109, 195606, 196105, 196606, 197110, 197616, 198125, + 198636, 199149, 199664, 200182, 200703, 201226, 201751, 202279, + 202809, 203342, 203878, 204416, 204956, 205500, 206045, 206594, + 207145, 207699, 208255, 208815, 209376, 209941, 210509, 211079, + 211652, 212228, 212807, 213389, 213973, 214561, 215151, 215745, + 216341, 216941, 217544, 218149, 218758, 219370, 219985, 220603, + 221225, 221849, 222477, 223108, 223743, 224381, 225022, 225666, + 226314, 226966, 227621, 228279, 228941, 229606, 230275, 230948, + 231624, 232304, 232988, 233676, 234367, 235062, 235761, 236463, + 237170, 237881, 238595, 239314, 240036, 240763, 241493, 242228, + 242967, 243711, 244458, 245210, 245966, 246727, 247492, 248261, + 249035, 249813, 250596, 251384, 252176, 252973, 253774, 254581, + 255392, 256208, 257029, 257855, 258686, 259522, 260363, 261209, + 262060, 262917, 263779, 264646, 265519, 266397, 267280, 268169, + 269064, 269965, 270871, 271782, 272700, 273624, 274553, 275489, + 276430, 277378, 278332, 279292, 280258, 281231, 282210, 283195, + 284188, 285186, 286192, 287204, 288223, 289249, 290282, 291322, + 292369, 293423, 294485, 295554, 296630, 297714, 298805, 299904, + 301011, 302126, 303248, 304379, 305517, 306664, 307819, 308983, + 310154, 311335, 312524, 313721, 314928, 316143, 317368, 318601, + 319844, 321097, 322358, 323629, 324910, 326201, 327502, 328812, + 330133, 331464, 332805, 334157, 335519, 336892, 338276, 339671, + 341078, 342495, 343924, 345364, 346816, 348280, 349756, 351244, + 352744, 354257, 355783, 357321, 358872, 360436, 362013, 363604, + 365208, 366826, 368459, 370105, 371765, 373440, 375130, 376835, + 378555, 380290, 382040, 383807, 385589, 387387, 389202, 391034, + 392882, 394747, 396630, 398530, 400448, 402384, 404338, 406311, + 408303, 410314, 412344, 414395, 416465, 418555, 420666, 422798, + 424951, 427125, 429321, 431540, 433781, 436045, 438332, 440643, + 442978, 445337, 447720, 450129, 452564, 455024, 457511, 460024, + 462565, 465133, 467730, 470355, 473009, 475692, 478406, 481150, + 483925, 486732, 489571, 492443, 495348, 498287, 501261, 504269, + 507313, 510394, 513512, 516667, 519861, 523094, 526366, 529680, + 533034, 536431, 539870, 543354, 546881, 550455, 554074, 557741, + 561456, 565221, 569035, 572901, 576818, 580789, 584815, 588896, + 593033, 597229, 601483, 605798, 610174, 614613, 619117, 623686, + 628323, 633028, 637803, 642651, 647572, 652568, 657640, 662792, + 668024, 673338, 678737, 684223, 689797, 695462, 701219, 707072, + 713023, 719074, 725227, 731486, 737853, 744331, 750922, 757631, + 764460, 771411, 778490, 785699, 793041, 800521, 808143, 815910, + 823827, 831898, 840127, 848520, 857081, 865817, 874730, 883829, + 893117, 902602, 912289, 922186, 932298, 942633, 953199, 964003, + 975054, 986361, 997931, 1009774, 1021901, 1034322, 1047046, 1060087, + 1073455, 1087164, 1101225, 1115654, 1130465, 1145673, 1161294, 1177345, + 1193846, 1210813, 1228269, 1246234, 1264730, 1283783, 1303416, 1323658, + 1344537, 1366084, 1388330, 1411312, 1435065, 1459630, 1485049, 1511367, + 1538632, 1566898, 1596220, 1626658, 1658278, 1691149, 1725348, 1760956, + 1798063, 1836758, 1877161, 1919378, 1963536, 2009771, 2058233, 2109087, + 2162516, 2218719, 2277919, 2340362, 2406322, 2476104, 2550052, 2628549, + 2712030, 2800983, 2895966, 2997613, 3106651, 3223918, 3350381, 3487165, + 3635590, 3797206, 3973855, 4167737, 4381502, 4618375, 4882318, 5178251, + 5512368, 5892567, 6329090, 6835455, 7429880, 8137527, 8994149, 10052327, + 11392683, 13145455, 15535599, 18988036, 24413316, 34178904, 56965752, 170910304 + }; + + private static readonly int[] fineSine = + { + 25, 75, 125, 175, 226, 276, 326, 376, + 427, 477, 527, 578, 628, 678, 728, 779, + 829, 879, 929, 980, 1030, 1080, 1130, 1181, + 1231, 1281, 1331, 1382, 1432, 1482, 1532, 1583, + 1633, 1683, 1733, 1784, 1834, 1884, 1934, 1985, + 2035, 2085, 2135, 2186, 2236, 2286, 2336, 2387, + 2437, 2487, 2537, 2587, 2638, 2688, 2738, 2788, + 2839, 2889, 2939, 2989, 3039, 3090, 3140, 3190, + 3240, 3291, 3341, 3391, 3441, 3491, 3541, 3592, + 3642, 3692, 3742, 3792, 3843, 3893, 3943, 3993, + 4043, 4093, 4144, 4194, 4244, 4294, 4344, 4394, + 4445, 4495, 4545, 4595, 4645, 4695, 4745, 4796, + 4846, 4896, 4946, 4996, 5046, 5096, 5146, 5197, + 5247, 5297, 5347, 5397, 5447, 5497, 5547, 5597, + 5647, 5697, 5748, 5798, 5848, 5898, 5948, 5998, + 6048, 6098, 6148, 6198, 6248, 6298, 6348, 6398, + 6448, 6498, 6548, 6598, 6648, 6698, 6748, 6798, + 6848, 6898, 6948, 6998, 7048, 7098, 7148, 7198, + 7248, 7298, 7348, 7398, 7448, 7498, 7548, 7598, + 7648, 7697, 7747, 7797, 7847, 7897, 7947, 7997, + 8047, 8097, 8147, 8196, 8246, 8296, 8346, 8396, + 8446, 8496, 8545, 8595, 8645, 8695, 8745, 8794, + 8844, 8894, 8944, 8994, 9043, 9093, 9143, 9193, + 9243, 9292, 9342, 9392, 9442, 9491, 9541, 9591, + 9640, 9690, 9740, 9790, 9839, 9889, 9939, 9988, + 10038, 10088, 10137, 10187, 10237, 10286, 10336, 10386, + 10435, 10485, 10534, 10584, 10634, 10683, 10733, 10782, + 10832, 10882, 10931, 10981, 11030, 11080, 11129, 11179, + 11228, 11278, 11327, 11377, 11426, 11476, 11525, 11575, + 11624, 11674, 11723, 11773, 11822, 11872, 11921, 11970, + 12020, 12069, 12119, 12168, 12218, 12267, 12316, 12366, + 12415, 12464, 12514, 12563, 12612, 12662, 12711, 12760, + 12810, 12859, 12908, 12957, 13007, 13056, 13105, 13154, + 13204, 13253, 13302, 13351, 13401, 13450, 13499, 13548, + 13597, 13647, 13696, 13745, 13794, 13843, 13892, 13941, + 13990, 14040, 14089, 14138, 14187, 14236, 14285, 14334, + 14383, 14432, 14481, 14530, 14579, 14628, 14677, 14726, + 14775, 14824, 14873, 14922, 14971, 15020, 15069, 15118, + 15167, 15215, 15264, 15313, 15362, 15411, 15460, 15509, + 15557, 15606, 15655, 15704, 15753, 15802, 15850, 15899, + 15948, 15997, 16045, 16094, 16143, 16191, 16240, 16289, + 16338, 16386, 16435, 16484, 16532, 16581, 16629, 16678, + 16727, 16775, 16824, 16872, 16921, 16970, 17018, 17067, + 17115, 17164, 17212, 17261, 17309, 17358, 17406, 17455, + 17503, 17551, 17600, 17648, 17697, 17745, 17793, 17842, + 17890, 17939, 17987, 18035, 18084, 18132, 18180, 18228, + 18277, 18325, 18373, 18421, 18470, 18518, 18566, 18614, + 18663, 18711, 18759, 18807, 18855, 18903, 18951, 19000, + 19048, 19096, 19144, 19192, 19240, 19288, 19336, 19384, + 19432, 19480, 19528, 19576, 19624, 19672, 19720, 19768, + 19816, 19864, 19912, 19959, 20007, 20055, 20103, 20151, + 20199, 20246, 20294, 20342, 20390, 20438, 20485, 20533, + 20581, 20629, 20676, 20724, 20772, 20819, 20867, 20915, + 20962, 21010, 21057, 21105, 21153, 21200, 21248, 21295, + 21343, 21390, 21438, 21485, 21533, 21580, 21628, 21675, + 21723, 21770, 21817, 21865, 21912, 21960, 22007, 22054, + 22102, 22149, 22196, 22243, 22291, 22338, 22385, 22433, + 22480, 22527, 22574, 22621, 22668, 22716, 22763, 22810, + 22857, 22904, 22951, 22998, 23045, 23092, 23139, 23186, + 23233, 23280, 23327, 23374, 23421, 23468, 23515, 23562, + 23609, 23656, 23703, 23750, 23796, 23843, 23890, 23937, + 23984, 24030, 24077, 24124, 24171, 24217, 24264, 24311, + 24357, 24404, 24451, 24497, 24544, 24591, 24637, 24684, + 24730, 24777, 24823, 24870, 24916, 24963, 25009, 25056, + 25102, 25149, 25195, 25241, 25288, 25334, 25381, 25427, + 25473, 25520, 25566, 25612, 25658, 25705, 25751, 25797, + 25843, 25889, 25936, 25982, 26028, 26074, 26120, 26166, + 26212, 26258, 26304, 26350, 26396, 26442, 26488, 26534, + 26580, 26626, 26672, 26718, 26764, 26810, 26856, 26902, + 26947, 26993, 27039, 27085, 27131, 27176, 27222, 27268, + 27313, 27359, 27405, 27450, 27496, 27542, 27587, 27633, + 27678, 27724, 27770, 27815, 27861, 27906, 27952, 27997, + 28042, 28088, 28133, 28179, 28224, 28269, 28315, 28360, + 28405, 28451, 28496, 28541, 28586, 28632, 28677, 28722, + 28767, 28812, 28858, 28903, 28948, 28993, 29038, 29083, + 29128, 29173, 29218, 29263, 29308, 29353, 29398, 29443, + 29488, 29533, 29577, 29622, 29667, 29712, 29757, 29801, + 29846, 29891, 29936, 29980, 30025, 30070, 30114, 30159, + 30204, 30248, 30293, 30337, 30382, 30426, 30471, 30515, + 30560, 30604, 30649, 30693, 30738, 30782, 30826, 30871, + 30915, 30959, 31004, 31048, 31092, 31136, 31181, 31225, + 31269, 31313, 31357, 31402, 31446, 31490, 31534, 31578, + 31622, 31666, 31710, 31754, 31798, 31842, 31886, 31930, + 31974, 32017, 32061, 32105, 32149, 32193, 32236, 32280, + 32324, 32368, 32411, 32455, 32499, 32542, 32586, 32630, + 32673, 32717, 32760, 32804, 32847, 32891, 32934, 32978, + 33021, 33065, 33108, 33151, 33195, 33238, 33281, 33325, + 33368, 33411, 33454, 33498, 33541, 33584, 33627, 33670, + 33713, 33756, 33799, 33843, 33886, 33929, 33972, 34015, + 34057, 34100, 34143, 34186, 34229, 34272, 34315, 34358, + 34400, 34443, 34486, 34529, 34571, 34614, 34657, 34699, + 34742, 34785, 34827, 34870, 34912, 34955, 34997, 35040, + 35082, 35125, 35167, 35210, 35252, 35294, 35337, 35379, + 35421, 35464, 35506, 35548, 35590, 35633, 35675, 35717, + 35759, 35801, 35843, 35885, 35927, 35969, 36011, 36053, + 36095, 36137, 36179, 36221, 36263, 36305, 36347, 36388, + 36430, 36472, 36514, 36555, 36597, 36639, 36681, 36722, + 36764, 36805, 36847, 36889, 36930, 36972, 37013, 37055, + 37096, 37137, 37179, 37220, 37262, 37303, 37344, 37386, + 37427, 37468, 37509, 37551, 37592, 37633, 37674, 37715, + 37756, 37797, 37838, 37879, 37920, 37961, 38002, 38043, + 38084, 38125, 38166, 38207, 38248, 38288, 38329, 38370, + 38411, 38451, 38492, 38533, 38573, 38614, 38655, 38695, + 38736, 38776, 38817, 38857, 38898, 38938, 38979, 39019, + 39059, 39100, 39140, 39180, 39221, 39261, 39301, 39341, + 39382, 39422, 39462, 39502, 39542, 39582, 39622, 39662, + 39702, 39742, 39782, 39822, 39862, 39902, 39942, 39982, + 40021, 40061, 40101, 40141, 40180, 40220, 40260, 40300, + 40339, 40379, 40418, 40458, 40497, 40537, 40576, 40616, + 40655, 40695, 40734, 40773, 40813, 40852, 40891, 40931, + 40970, 41009, 41048, 41087, 41127, 41166, 41205, 41244, + 41283, 41322, 41361, 41400, 41439, 41478, 41517, 41556, + 41595, 41633, 41672, 41711, 41750, 41788, 41827, 41866, + 41904, 41943, 41982, 42020, 42059, 42097, 42136, 42174, + 42213, 42251, 42290, 42328, 42366, 42405, 42443, 42481, + 42520, 42558, 42596, 42634, 42672, 42711, 42749, 42787, + 42825, 42863, 42901, 42939, 42977, 43015, 43053, 43091, + 43128, 43166, 43204, 43242, 43280, 43317, 43355, 43393, + 43430, 43468, 43506, 43543, 43581, 43618, 43656, 43693, + 43731, 43768, 43806, 43843, 43880, 43918, 43955, 43992, + 44029, 44067, 44104, 44141, 44178, 44215, 44252, 44289, + 44326, 44363, 44400, 44437, 44474, 44511, 44548, 44585, + 44622, 44659, 44695, 44732, 44769, 44806, 44842, 44879, + 44915, 44952, 44989, 45025, 45062, 45098, 45135, 45171, + 45207, 45244, 45280, 45316, 45353, 45389, 45425, 45462, + 45498, 45534, 45570, 45606, 45642, 45678, 45714, 45750, + 45786, 45822, 45858, 45894, 45930, 45966, 46002, 46037, + 46073, 46109, 46145, 46180, 46216, 46252, 46287, 46323, + 46358, 46394, 46429, 46465, 46500, 46536, 46571, 46606, + 46642, 46677, 46712, 46747, 46783, 46818, 46853, 46888, + 46923, 46958, 46993, 47028, 47063, 47098, 47133, 47168, + 47203, 47238, 47273, 47308, 47342, 47377, 47412, 47446, + 47481, 47516, 47550, 47585, 47619, 47654, 47688, 47723, + 47757, 47792, 47826, 47860, 47895, 47929, 47963, 47998, + 48032, 48066, 48100, 48134, 48168, 48202, 48237, 48271, + 48305, 48338, 48372, 48406, 48440, 48474, 48508, 48542, + 48575, 48609, 48643, 48676, 48710, 48744, 48777, 48811, + 48844, 48878, 48911, 48945, 48978, 49012, 49045, 49078, + 49112, 49145, 49178, 49211, 49244, 49278, 49311, 49344, + 49377, 49410, 49443, 49476, 49509, 49542, 49575, 49608, + 49640, 49673, 49706, 49739, 49771, 49804, 49837, 49869, + 49902, 49935, 49967, 50000, 50032, 50065, 50097, 50129, + 50162, 50194, 50226, 50259, 50291, 50323, 50355, 50387, + 50420, 50452, 50484, 50516, 50548, 50580, 50612, 50644, + 50675, 50707, 50739, 50771, 50803, 50834, 50866, 50898, + 50929, 50961, 50993, 51024, 51056, 51087, 51119, 51150, + 51182, 51213, 51244, 51276, 51307, 51338, 51369, 51401, + 51432, 51463, 51494, 51525, 51556, 51587, 51618, 51649, + 51680, 51711, 51742, 51773, 51803, 51834, 51865, 51896, + 51926, 51957, 51988, 52018, 52049, 52079, 52110, 52140, + 52171, 52201, 52231, 52262, 52292, 52322, 52353, 52383, + 52413, 52443, 52473, 52503, 52534, 52564, 52594, 52624, + 52653, 52683, 52713, 52743, 52773, 52803, 52832, 52862, + 52892, 52922, 52951, 52981, 53010, 53040, 53069, 53099, + 53128, 53158, 53187, 53216, 53246, 53275, 53304, 53334, + 53363, 53392, 53421, 53450, 53479, 53508, 53537, 53566, + 53595, 53624, 53653, 53682, 53711, 53739, 53768, 53797, + 53826, 53854, 53883, 53911, 53940, 53969, 53997, 54026, + 54054, 54082, 54111, 54139, 54167, 54196, 54224, 54252, + 54280, 54308, 54337, 54365, 54393, 54421, 54449, 54477, + 54505, 54533, 54560, 54588, 54616, 54644, 54672, 54699, + 54727, 54755, 54782, 54810, 54837, 54865, 54892, 54920, + 54947, 54974, 55002, 55029, 55056, 55084, 55111, 55138, + 55165, 55192, 55219, 55246, 55274, 55300, 55327, 55354, + 55381, 55408, 55435, 55462, 55489, 55515, 55542, 55569, + 55595, 55622, 55648, 55675, 55701, 55728, 55754, 55781, + 55807, 55833, 55860, 55886, 55912, 55938, 55965, 55991, + 56017, 56043, 56069, 56095, 56121, 56147, 56173, 56199, + 56225, 56250, 56276, 56302, 56328, 56353, 56379, 56404, + 56430, 56456, 56481, 56507, 56532, 56557, 56583, 56608, + 56633, 56659, 56684, 56709, 56734, 56760, 56785, 56810, + 56835, 56860, 56885, 56910, 56935, 56959, 56984, 57009, + 57034, 57059, 57083, 57108, 57133, 57157, 57182, 57206, + 57231, 57255, 57280, 57304, 57329, 57353, 57377, 57402, + 57426, 57450, 57474, 57498, 57522, 57546, 57570, 57594, + 57618, 57642, 57666, 57690, 57714, 57738, 57762, 57785, + 57809, 57833, 57856, 57880, 57903, 57927, 57950, 57974, + 57997, 58021, 58044, 58067, 58091, 58114, 58137, 58160, + 58183, 58207, 58230, 58253, 58276, 58299, 58322, 58345, + 58367, 58390, 58413, 58436, 58459, 58481, 58504, 58527, + 58549, 58572, 58594, 58617, 58639, 58662, 58684, 58706, + 58729, 58751, 58773, 58795, 58818, 58840, 58862, 58884, + 58906, 58928, 58950, 58972, 58994, 59016, 59038, 59059, + 59081, 59103, 59125, 59146, 59168, 59190, 59211, 59233, + 59254, 59276, 59297, 59318, 59340, 59361, 59382, 59404, + 59425, 59446, 59467, 59488, 59509, 59530, 59551, 59572, + 59593, 59614, 59635, 59656, 59677, 59697, 59718, 59739, + 59759, 59780, 59801, 59821, 59842, 59862, 59883, 59903, + 59923, 59944, 59964, 59984, 60004, 60025, 60045, 60065, + 60085, 60105, 60125, 60145, 60165, 60185, 60205, 60225, + 60244, 60264, 60284, 60304, 60323, 60343, 60363, 60382, + 60402, 60421, 60441, 60460, 60479, 60499, 60518, 60537, + 60556, 60576, 60595, 60614, 60633, 60652, 60671, 60690, + 60709, 60728, 60747, 60766, 60785, 60803, 60822, 60841, + 60859, 60878, 60897, 60915, 60934, 60952, 60971, 60989, + 61007, 61026, 61044, 61062, 61081, 61099, 61117, 61135, + 61153, 61171, 61189, 61207, 61225, 61243, 61261, 61279, + 61297, 61314, 61332, 61350, 61367, 61385, 61403, 61420, + 61438, 61455, 61473, 61490, 61507, 61525, 61542, 61559, + 61577, 61594, 61611, 61628, 61645, 61662, 61679, 61696, + 61713, 61730, 61747, 61764, 61780, 61797, 61814, 61831, + 61847, 61864, 61880, 61897, 61913, 61930, 61946, 61963, + 61979, 61995, 62012, 62028, 62044, 62060, 62076, 62092, + 62108, 62125, 62141, 62156, 62172, 62188, 62204, 62220, + 62236, 62251, 62267, 62283, 62298, 62314, 62329, 62345, + 62360, 62376, 62391, 62407, 62422, 62437, 62453, 62468, + 62483, 62498, 62513, 62528, 62543, 62558, 62573, 62588, + 62603, 62618, 62633, 62648, 62662, 62677, 62692, 62706, + 62721, 62735, 62750, 62764, 62779, 62793, 62808, 62822, + 62836, 62850, 62865, 62879, 62893, 62907, 62921, 62935, + 62949, 62963, 62977, 62991, 63005, 63019, 63032, 63046, + 63060, 63074, 63087, 63101, 63114, 63128, 63141, 63155, + 63168, 63182, 63195, 63208, 63221, 63235, 63248, 63261, + 63274, 63287, 63300, 63313, 63326, 63339, 63352, 63365, + 63378, 63390, 63403, 63416, 63429, 63441, 63454, 63466, + 63479, 63491, 63504, 63516, 63528, 63541, 63553, 63565, + 63578, 63590, 63602, 63614, 63626, 63638, 63650, 63662, + 63674, 63686, 63698, 63709, 63721, 63733, 63745, 63756, + 63768, 63779, 63791, 63803, 63814, 63825, 63837, 63848, + 63859, 63871, 63882, 63893, 63904, 63915, 63927, 63938, + 63949, 63960, 63971, 63981, 63992, 64003, 64014, 64025, + 64035, 64046, 64057, 64067, 64078, 64088, 64099, 64109, + 64120, 64130, 64140, 64151, 64161, 64171, 64181, 64192, + 64202, 64212, 64222, 64232, 64242, 64252, 64261, 64271, + 64281, 64291, 64301, 64310, 64320, 64330, 64339, 64349, + 64358, 64368, 64377, 64387, 64396, 64405, 64414, 64424, + 64433, 64442, 64451, 64460, 64469, 64478, 64487, 64496, + 64505, 64514, 64523, 64532, 64540, 64549, 64558, 64566, + 64575, 64584, 64592, 64601, 64609, 64617, 64626, 64634, + 64642, 64651, 64659, 64667, 64675, 64683, 64691, 64699, + 64707, 64715, 64723, 64731, 64739, 64747, 64754, 64762, + 64770, 64777, 64785, 64793, 64800, 64808, 64815, 64822, + 64830, 64837, 64844, 64852, 64859, 64866, 64873, 64880, + 64887, 64895, 64902, 64908, 64915, 64922, 64929, 64936, + 64943, 64949, 64956, 64963, 64969, 64976, 64982, 64989, + 64995, 65002, 65008, 65015, 65021, 65027, 65033, 65040, + 65046, 65052, 65058, 65064, 65070, 65076, 65082, 65088, + 65094, 65099, 65105, 65111, 65117, 65122, 65128, 65133, + 65139, 65144, 65150, 65155, 65161, 65166, 65171, 65177, + 65182, 65187, 65192, 65197, 65202, 65207, 65212, 65217, + 65222, 65227, 65232, 65237, 65242, 65246, 65251, 65256, + 65260, 65265, 65270, 65274, 65279, 65283, 65287, 65292, + 65296, 65300, 65305, 65309, 65313, 65317, 65321, 65325, + 65329, 65333, 65337, 65341, 65345, 65349, 65352, 65356, + 65360, 65363, 65367, 65371, 65374, 65378, 65381, 65385, + 65388, 65391, 65395, 65398, 65401, 65404, 65408, 65411, + 65414, 65417, 65420, 65423, 65426, 65429, 65431, 65434, + 65437, 65440, 65442, 65445, 65448, 65450, 65453, 65455, + 65458, 65460, 65463, 65465, 65467, 65470, 65472, 65474, + 65476, 65478, 65480, 65482, 65484, 65486, 65488, 65490, + 65492, 65494, 65496, 65497, 65499, 65501, 65502, 65504, + 65505, 65507, 65508, 65510, 65511, 65513, 65514, 65515, + 65516, 65518, 65519, 65520, 65521, 65522, 65523, 65524, + 65525, 65526, 65527, 65527, 65528, 65529, 65530, 65530, + 65531, 65531, 65532, 65532, 65533, 65533, 65534, 65534, + 65534, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65534, + 65534, 65534, 65533, 65533, 65532, 65532, 65531, 65531, + 65530, 65530, 65529, 65528, 65527, 65527, 65526, 65525, + 65524, 65523, 65522, 65521, 65520, 65519, 65518, 65516, + 65515, 65514, 65513, 65511, 65510, 65508, 65507, 65505, + 65504, 65502, 65501, 65499, 65497, 65496, 65494, 65492, + 65490, 65488, 65486, 65484, 65482, 65480, 65478, 65476, + 65474, 65472, 65470, 65467, 65465, 65463, 65460, 65458, + 65455, 65453, 65450, 65448, 65445, 65442, 65440, 65437, + 65434, 65431, 65429, 65426, 65423, 65420, 65417, 65414, + 65411, 65408, 65404, 65401, 65398, 65395, 65391, 65388, + 65385, 65381, 65378, 65374, 65371, 65367, 65363, 65360, + 65356, 65352, 65349, 65345, 65341, 65337, 65333, 65329, + 65325, 65321, 65317, 65313, 65309, 65305, 65300, 65296, + 65292, 65287, 65283, 65279, 65274, 65270, 65265, 65260, + 65256, 65251, 65246, 65242, 65237, 65232, 65227, 65222, + 65217, 65212, 65207, 65202, 65197, 65192, 65187, 65182, + 65177, 65171, 65166, 65161, 65155, 65150, 65144, 65139, + 65133, 65128, 65122, 65117, 65111, 65105, 65099, 65094, + 65088, 65082, 65076, 65070, 65064, 65058, 65052, 65046, + 65040, 65033, 65027, 65021, 65015, 65008, 65002, 64995, + 64989, 64982, 64976, 64969, 64963, 64956, 64949, 64943, + 64936, 64929, 64922, 64915, 64908, 64902, 64895, 64887, + 64880, 64873, 64866, 64859, 64852, 64844, 64837, 64830, + 64822, 64815, 64808, 64800, 64793, 64785, 64777, 64770, + 64762, 64754, 64747, 64739, 64731, 64723, 64715, 64707, + 64699, 64691, 64683, 64675, 64667, 64659, 64651, 64642, + 64634, 64626, 64617, 64609, 64600, 64592, 64584, 64575, + 64566, 64558, 64549, 64540, 64532, 64523, 64514, 64505, + 64496, 64487, 64478, 64469, 64460, 64451, 64442, 64433, + 64424, 64414, 64405, 64396, 64387, 64377, 64368, 64358, + 64349, 64339, 64330, 64320, 64310, 64301, 64291, 64281, + 64271, 64261, 64252, 64242, 64232, 64222, 64212, 64202, + 64192, 64181, 64171, 64161, 64151, 64140, 64130, 64120, + 64109, 64099, 64088, 64078, 64067, 64057, 64046, 64035, + 64025, 64014, 64003, 63992, 63981, 63971, 63960, 63949, + 63938, 63927, 63915, 63904, 63893, 63882, 63871, 63859, + 63848, 63837, 63825, 63814, 63803, 63791, 63779, 63768, + 63756, 63745, 63733, 63721, 63709, 63698, 63686, 63674, + 63662, 63650, 63638, 63626, 63614, 63602, 63590, 63578, + 63565, 63553, 63541, 63528, 63516, 63504, 63491, 63479, + 63466, 63454, 63441, 63429, 63416, 63403, 63390, 63378, + 63365, 63352, 63339, 63326, 63313, 63300, 63287, 63274, + 63261, 63248, 63235, 63221, 63208, 63195, 63182, 63168, + 63155, 63141, 63128, 63114, 63101, 63087, 63074, 63060, + 63046, 63032, 63019, 63005, 62991, 62977, 62963, 62949, + 62935, 62921, 62907, 62893, 62879, 62865, 62850, 62836, + 62822, 62808, 62793, 62779, 62764, 62750, 62735, 62721, + 62706, 62692, 62677, 62662, 62648, 62633, 62618, 62603, + 62588, 62573, 62558, 62543, 62528, 62513, 62498, 62483, + 62468, 62453, 62437, 62422, 62407, 62391, 62376, 62360, + 62345, 62329, 62314, 62298, 62283, 62267, 62251, 62236, + 62220, 62204, 62188, 62172, 62156, 62141, 62125, 62108, + 62092, 62076, 62060, 62044, 62028, 62012, 61995, 61979, + 61963, 61946, 61930, 61913, 61897, 61880, 61864, 61847, + 61831, 61814, 61797, 61780, 61764, 61747, 61730, 61713, + 61696, 61679, 61662, 61645, 61628, 61611, 61594, 61577, + 61559, 61542, 61525, 61507, 61490, 61473, 61455, 61438, + 61420, 61403, 61385, 61367, 61350, 61332, 61314, 61297, + 61279, 61261, 61243, 61225, 61207, 61189, 61171, 61153, + 61135, 61117, 61099, 61081, 61062, 61044, 61026, 61007, + 60989, 60971, 60952, 60934, 60915, 60897, 60878, 60859, + 60841, 60822, 60803, 60785, 60766, 60747, 60728, 60709, + 60690, 60671, 60652, 60633, 60614, 60595, 60576, 60556, + 60537, 60518, 60499, 60479, 60460, 60441, 60421, 60402, + 60382, 60363, 60343, 60323, 60304, 60284, 60264, 60244, + 60225, 60205, 60185, 60165, 60145, 60125, 60105, 60085, + 60065, 60045, 60025, 60004, 59984, 59964, 59944, 59923, + 59903, 59883, 59862, 59842, 59821, 59801, 59780, 59759, + 59739, 59718, 59697, 59677, 59656, 59635, 59614, 59593, + 59572, 59551, 59530, 59509, 59488, 59467, 59446, 59425, + 59404, 59382, 59361, 59340, 59318, 59297, 59276, 59254, + 59233, 59211, 59190, 59168, 59146, 59125, 59103, 59081, + 59059, 59038, 59016, 58994, 58972, 58950, 58928, 58906, + 58884, 58862, 58840, 58818, 58795, 58773, 58751, 58729, + 58706, 58684, 58662, 58639, 58617, 58594, 58572, 58549, + 58527, 58504, 58481, 58459, 58436, 58413, 58390, 58367, + 58345, 58322, 58299, 58276, 58253, 58230, 58207, 58183, + 58160, 58137, 58114, 58091, 58067, 58044, 58021, 57997, + 57974, 57950, 57927, 57903, 57880, 57856, 57833, 57809, + 57785, 57762, 57738, 57714, 57690, 57666, 57642, 57618, + 57594, 57570, 57546, 57522, 57498, 57474, 57450, 57426, + 57402, 57377, 57353, 57329, 57304, 57280, 57255, 57231, + 57206, 57182, 57157, 57133, 57108, 57083, 57059, 57034, + 57009, 56984, 56959, 56935, 56910, 56885, 56860, 56835, + 56810, 56785, 56760, 56734, 56709, 56684, 56659, 56633, + 56608, 56583, 56557, 56532, 56507, 56481, 56456, 56430, + 56404, 56379, 56353, 56328, 56302, 56276, 56250, 56225, + 56199, 56173, 56147, 56121, 56095, 56069, 56043, 56017, + 55991, 55965, 55938, 55912, 55886, 55860, 55833, 55807, + 55781, 55754, 55728, 55701, 55675, 55648, 55622, 55595, + 55569, 55542, 55515, 55489, 55462, 55435, 55408, 55381, + 55354, 55327, 55300, 55274, 55246, 55219, 55192, 55165, + 55138, 55111, 55084, 55056, 55029, 55002, 54974, 54947, + 54920, 54892, 54865, 54837, 54810, 54782, 54755, 54727, + 54699, 54672, 54644, 54616, 54588, 54560, 54533, 54505, + 54477, 54449, 54421, 54393, 54365, 54337, 54308, 54280, + 54252, 54224, 54196, 54167, 54139, 54111, 54082, 54054, + 54026, 53997, 53969, 53940, 53911, 53883, 53854, 53826, + 53797, 53768, 53739, 53711, 53682, 53653, 53624, 53595, + 53566, 53537, 53508, 53479, 53450, 53421, 53392, 53363, + 53334, 53304, 53275, 53246, 53216, 53187, 53158, 53128, + 53099, 53069, 53040, 53010, 52981, 52951, 52922, 52892, + 52862, 52832, 52803, 52773, 52743, 52713, 52683, 52653, + 52624, 52594, 52564, 52534, 52503, 52473, 52443, 52413, + 52383, 52353, 52322, 52292, 52262, 52231, 52201, 52171, + 52140, 52110, 52079, 52049, 52018, 51988, 51957, 51926, + 51896, 51865, 51834, 51803, 51773, 51742, 51711, 51680, + 51649, 51618, 51587, 51556, 51525, 51494, 51463, 51432, + 51401, 51369, 51338, 51307, 51276, 51244, 51213, 51182, + 51150, 51119, 51087, 51056, 51024, 50993, 50961, 50929, + 50898, 50866, 50834, 50803, 50771, 50739, 50707, 50675, + 50644, 50612, 50580, 50548, 50516, 50484, 50452, 50420, + 50387, 50355, 50323, 50291, 50259, 50226, 50194, 50162, + 50129, 50097, 50065, 50032, 50000, 49967, 49935, 49902, + 49869, 49837, 49804, 49771, 49739, 49706, 49673, 49640, + 49608, 49575, 49542, 49509, 49476, 49443, 49410, 49377, + 49344, 49311, 49278, 49244, 49211, 49178, 49145, 49112, + 49078, 49045, 49012, 48978, 48945, 48911, 48878, 48844, + 48811, 48777, 48744, 48710, 48676, 48643, 48609, 48575, + 48542, 48508, 48474, 48440, 48406, 48372, 48338, 48304, + 48271, 48237, 48202, 48168, 48134, 48100, 48066, 48032, + 47998, 47963, 47929, 47895, 47860, 47826, 47792, 47757, + 47723, 47688, 47654, 47619, 47585, 47550, 47516, 47481, + 47446, 47412, 47377, 47342, 47308, 47273, 47238, 47203, + 47168, 47133, 47098, 47063, 47028, 46993, 46958, 46923, + 46888, 46853, 46818, 46783, 46747, 46712, 46677, 46642, + 46606, 46571, 46536, 46500, 46465, 46429, 46394, 46358, + 46323, 46287, 46252, 46216, 46180, 46145, 46109, 46073, + 46037, 46002, 45966, 45930, 45894, 45858, 45822, 45786, + 45750, 45714, 45678, 45642, 45606, 45570, 45534, 45498, + 45462, 45425, 45389, 45353, 45316, 45280, 45244, 45207, + 45171, 45135, 45098, 45062, 45025, 44989, 44952, 44915, + 44879, 44842, 44806, 44769, 44732, 44695, 44659, 44622, + 44585, 44548, 44511, 44474, 44437, 44400, 44363, 44326, + 44289, 44252, 44215, 44178, 44141, 44104, 44067, 44029, + 43992, 43955, 43918, 43880, 43843, 43806, 43768, 43731, + 43693, 43656, 43618, 43581, 43543, 43506, 43468, 43430, + 43393, 43355, 43317, 43280, 43242, 43204, 43166, 43128, + 43091, 43053, 43015, 42977, 42939, 42901, 42863, 42825, + 42787, 42749, 42711, 42672, 42634, 42596, 42558, 42520, + 42481, 42443, 42405, 42366, 42328, 42290, 42251, 42213, + 42174, 42136, 42097, 42059, 42020, 41982, 41943, 41904, + 41866, 41827, 41788, 41750, 41711, 41672, 41633, 41595, + 41556, 41517, 41478, 41439, 41400, 41361, 41322, 41283, + 41244, 41205, 41166, 41127, 41088, 41048, 41009, 40970, + 40931, 40891, 40852, 40813, 40773, 40734, 40695, 40655, + 40616, 40576, 40537, 40497, 40458, 40418, 40379, 40339, + 40300, 40260, 40220, 40180, 40141, 40101, 40061, 40021, + 39982, 39942, 39902, 39862, 39822, 39782, 39742, 39702, + 39662, 39622, 39582, 39542, 39502, 39462, 39422, 39382, + 39341, 39301, 39261, 39221, 39180, 39140, 39100, 39059, + 39019, 38979, 38938, 38898, 38857, 38817, 38776, 38736, + 38695, 38655, 38614, 38573, 38533, 38492, 38451, 38411, + 38370, 38329, 38288, 38248, 38207, 38166, 38125, 38084, + 38043, 38002, 37961, 37920, 37879, 37838, 37797, 37756, + 37715, 37674, 37633, 37592, 37551, 37509, 37468, 37427, + 37386, 37344, 37303, 37262, 37220, 37179, 37137, 37096, + 37055, 37013, 36972, 36930, 36889, 36847, 36805, 36764, + 36722, 36681, 36639, 36597, 36556, 36514, 36472, 36430, + 36388, 36347, 36305, 36263, 36221, 36179, 36137, 36095, + 36053, 36011, 35969, 35927, 35885, 35843, 35801, 35759, + 35717, 35675, 35633, 35590, 35548, 35506, 35464, 35421, + 35379, 35337, 35294, 35252, 35210, 35167, 35125, 35082, + 35040, 34997, 34955, 34912, 34870, 34827, 34785, 34742, + 34699, 34657, 34614, 34571, 34529, 34486, 34443, 34400, + 34358, 34315, 34272, 34229, 34186, 34143, 34100, 34057, + 34015, 33972, 33929, 33886, 33843, 33799, 33756, 33713, + 33670, 33627, 33584, 33541, 33498, 33454, 33411, 33368, + 33325, 33281, 33238, 33195, 33151, 33108, 33065, 33021, + 32978, 32934, 32891, 32847, 32804, 32760, 32717, 32673, + 32630, 32586, 32542, 32499, 32455, 32411, 32368, 32324, + 32280, 32236, 32193, 32149, 32105, 32061, 32017, 31974, + 31930, 31886, 31842, 31798, 31754, 31710, 31666, 31622, + 31578, 31534, 31490, 31446, 31402, 31357, 31313, 31269, + 31225, 31181, 31136, 31092, 31048, 31004, 30959, 30915, + 30871, 30826, 30782, 30738, 30693, 30649, 30604, 30560, + 30515, 30471, 30426, 30382, 30337, 30293, 30248, 30204, + 30159, 30114, 30070, 30025, 29980, 29936, 29891, 29846, + 29801, 29757, 29712, 29667, 29622, 29577, 29533, 29488, + 29443, 29398, 29353, 29308, 29263, 29218, 29173, 29128, + 29083, 29038, 28993, 28948, 28903, 28858, 28812, 28767, + 28722, 28677, 28632, 28586, 28541, 28496, 28451, 28405, + 28360, 28315, 28269, 28224, 28179, 28133, 28088, 28042, + 27997, 27952, 27906, 27861, 27815, 27770, 27724, 27678, + 27633, 27587, 27542, 27496, 27450, 27405, 27359, 27313, + 27268, 27222, 27176, 27131, 27085, 27039, 26993, 26947, + 26902, 26856, 26810, 26764, 26718, 26672, 26626, 26580, + 26534, 26488, 26442, 26396, 26350, 26304, 26258, 26212, + 26166, 26120, 26074, 26028, 25982, 25936, 25889, 25843, + 25797, 25751, 25705, 25658, 25612, 25566, 25520, 25473, + 25427, 25381, 25334, 25288, 25241, 25195, 25149, 25102, + 25056, 25009, 24963, 24916, 24870, 24823, 24777, 24730, + 24684, 24637, 24591, 24544, 24497, 24451, 24404, 24357, + 24311, 24264, 24217, 24171, 24124, 24077, 24030, 23984, + 23937, 23890, 23843, 23796, 23750, 23703, 23656, 23609, + 23562, 23515, 23468, 23421, 23374, 23327, 23280, 23233, + 23186, 23139, 23092, 23045, 22998, 22951, 22904, 22857, + 22810, 22763, 22716, 22668, 22621, 22574, 22527, 22480, + 22433, 22385, 22338, 22291, 22243, 22196, 22149, 22102, + 22054, 22007, 21960, 21912, 21865, 21817, 21770, 21723, + 21675, 21628, 21580, 21533, 21485, 21438, 21390, 21343, + 21295, 21248, 21200, 21153, 21105, 21057, 21010, 20962, + 20915, 20867, 20819, 20772, 20724, 20676, 20629, 20581, + 20533, 20485, 20438, 20390, 20342, 20294, 20246, 20199, + 20151, 20103, 20055, 20007, 19959, 19912, 19864, 19816, + 19768, 19720, 19672, 19624, 19576, 19528, 19480, 19432, + 19384, 19336, 19288, 19240, 19192, 19144, 19096, 19048, + 19000, 18951, 18903, 18855, 18807, 18759, 18711, 18663, + 18614, 18566, 18518, 18470, 18421, 18373, 18325, 18277, + 18228, 18180, 18132, 18084, 18035, 17987, 17939, 17890, + 17842, 17793, 17745, 17697, 17648, 17600, 17551, 17503, + 17455, 17406, 17358, 17309, 17261, 17212, 17164, 17115, + 17067, 17018, 16970, 16921, 16872, 16824, 16775, 16727, + 16678, 16629, 16581, 16532, 16484, 16435, 16386, 16338, + 16289, 16240, 16191, 16143, 16094, 16045, 15997, 15948, + 15899, 15850, 15802, 15753, 15704, 15655, 15606, 15557, + 15509, 15460, 15411, 15362, 15313, 15264, 15215, 15167, + 15118, 15069, 15020, 14971, 14922, 14873, 14824, 14775, + 14726, 14677, 14628, 14579, 14530, 14481, 14432, 14383, + 14334, 14285, 14236, 14187, 14138, 14089, 14040, 13990, + 13941, 13892, 13843, 13794, 13745, 13696, 13646, 13597, + 13548, 13499, 13450, 13401, 13351, 13302, 13253, 13204, + 13154, 13105, 13056, 13007, 12957, 12908, 12859, 12810, + 12760, 12711, 12662, 12612, 12563, 12514, 12464, 12415, + 12366, 12316, 12267, 12218, 12168, 12119, 12069, 12020, + 11970, 11921, 11872, 11822, 11773, 11723, 11674, 11624, + 11575, 11525, 11476, 11426, 11377, 11327, 11278, 11228, + 11179, 11129, 11080, 11030, 10981, 10931, 10882, 10832, + 10782, 10733, 10683, 10634, 10584, 10534, 10485, 10435, + 10386, 10336, 10286, 10237, 10187, 10137, 10088, 10038, + 9988, 9939, 9889, 9839, 9790, 9740, 9690, 9640, + 9591, 9541, 9491, 9442, 9392, 9342, 9292, 9243, + 9193, 9143, 9093, 9043, 8994, 8944, 8894, 8844, + 8794, 8745, 8695, 8645, 8595, 8545, 8496, 8446, + 8396, 8346, 8296, 8246, 8196, 8147, 8097, 8047, + 7997, 7947, 7897, 7847, 7797, 7747, 7697, 7648, + 7598, 7548, 7498, 7448, 7398, 7348, 7298, 7248, + 7198, 7148, 7098, 7048, 6998, 6948, 6898, 6848, + 6798, 6748, 6698, 6648, 6598, 6548, 6498, 6448, + 6398, 6348, 6298, 6248, 6198, 6148, 6098, 6048, + 5998, 5948, 5898, 5848, 5798, 5748, 5697, 5647, + 5597, 5547, 5497, 5447, 5397, 5347, 5297, 5247, + 5197, 5146, 5096, 5046, 4996, 4946, 4896, 4846, + 4796, 4745, 4695, 4645, 4595, 4545, 4495, 4445, + 4394, 4344, 4294, 4244, 4194, 4144, 4093, 4043, + 3993, 3943, 3893, 3843, 3792, 3742, 3692, 3642, + 3592, 3541, 3491, 3441, 3391, 3341, 3291, 3240, + 3190, 3140, 3090, 3039, 2989, 2939, 2889, 2839, + 2788, 2738, 2688, 2638, 2587, 2537, 2487, 2437, + 2387, 2336, 2286, 2236, 2186, 2135, 2085, 2035, + 1985, 1934, 1884, 1834, 1784, 1733, 1683, 1633, + 1583, 1532, 1482, 1432, 1382, 1331, 1281, 1231, + 1181, 1130, 1080, 1030, 980, 929, 879, 829, + 779, 728, 678, 628, 578, 527, 477, 427, + 376, 326, 276, 226, 175, 125, 75, 25, + -25, -75, -125, -175, -226, -276, -326, -376, + -427, -477, -527, -578, -628, -678, -728, -779, + -829, -879, -929, -980, -1030, -1080, -1130, -1181, + -1231, -1281, -1331, -1382, -1432, -1482, -1532, -1583, + -1633, -1683, -1733, -1784, -1834, -1884, -1934, -1985, + -2035, -2085, -2135, -2186, -2236, -2286, -2336, -2387, + -2437, -2487, -2537, -2588, -2638, -2688, -2738, -2788, + -2839, -2889, -2939, -2989, -3039, -3090, -3140, -3190, + -3240, -3291, -3341, -3391, -3441, -3491, -3541, -3592, + -3642, -3692, -3742, -3792, -3843, -3893, -3943, -3993, + -4043, -4093, -4144, -4194, -4244, -4294, -4344, -4394, + -4445, -4495, -4545, -4595, -4645, -4695, -4745, -4796, + -4846, -4896, -4946, -4996, -5046, -5096, -5146, -5197, + -5247, -5297, -5347, -5397, -5447, -5497, -5547, -5597, + -5647, -5697, -5748, -5798, -5848, -5898, -5948, -5998, + -6048, -6098, -6148, -6198, -6248, -6298, -6348, -6398, + -6448, -6498, -6548, -6598, -6648, -6698, -6748, -6798, + -6848, -6898, -6948, -6998, -7048, -7098, -7148, -7198, + -7248, -7298, -7348, -7398, -7448, -7498, -7548, -7598, + -7648, -7697, -7747, -7797, -7847, -7897, -7947, -7997, + -8047, -8097, -8147, -8196, -8246, -8296, -8346, -8396, + -8446, -8496, -8545, -8595, -8645, -8695, -8745, -8794, + -8844, -8894, -8944, -8994, -9043, -9093, -9143, -9193, + -9243, -9292, -9342, -9392, -9442, -9491, -9541, -9591, + -9640, -9690, -9740, -9790, -9839, -9889, -9939, -9988, + -10038, -10088, -10137, -10187, -10237, -10286, -10336, -10386, + -10435, -10485, -10534, -10584, -10634, -10683, -10733, -10782, + -10832, -10882, -10931, -10981, -11030, -11080, -11129, -11179, + -11228, -11278, -11327, -11377, -11426, -11476, -11525, -11575, + -11624, -11674, -11723, -11773, -11822, -11872, -11921, -11970, + -12020, -12069, -12119, -12168, -12218, -12267, -12316, -12366, + -12415, -12464, -12514, -12563, -12612, -12662, -12711, -12760, + -12810, -12859, -12908, -12957, -13007, -13056, -13105, -13154, + -13204, -13253, -13302, -13351, -13401, -13450, -13499, -13548, + -13597, -13647, -13696, -13745, -13794, -13843, -13892, -13941, + -13990, -14040, -14089, -14138, -14187, -14236, -14285, -14334, + -14383, -14432, -14481, -14530, -14579, -14628, -14677, -14726, + -14775, -14824, -14873, -14922, -14971, -15020, -15069, -15118, + -15167, -15215, -15264, -15313, -15362, -15411, -15460, -15509, + -15557, -15606, -15655, -15704, -15753, -15802, -15850, -15899, + -15948, -15997, -16045, -16094, -16143, -16191, -16240, -16289, + -16338, -16386, -16435, -16484, -16532, -16581, -16629, -16678, + -16727, -16775, -16824, -16872, -16921, -16970, -17018, -17067, + -17115, -17164, -17212, -17261, -17309, -17358, -17406, -17455, + -17503, -17551, -17600, -17648, -17697, -17745, -17793, -17842, + -17890, -17939, -17987, -18035, -18084, -18132, -18180, -18228, + -18277, -18325, -18373, -18421, -18470, -18518, -18566, -18614, + -18663, -18711, -18759, -18807, -18855, -18903, -18951, -19000, + -19048, -19096, -19144, -19192, -19240, -19288, -19336, -19384, + -19432, -19480, -19528, -19576, -19624, -19672, -19720, -19768, + -19816, -19864, -19912, -19959, -20007, -20055, -20103, -20151, + -20199, -20246, -20294, -20342, -20390, -20438, -20485, -20533, + -20581, -20629, -20676, -20724, -20772, -20819, -20867, -20915, + -20962, -21010, -21057, -21105, -21153, -21200, -21248, -21295, + -21343, -21390, -21438, -21485, -21533, -21580, -21628, -21675, + -21723, -21770, -21817, -21865, -21912, -21960, -22007, -22054, + -22102, -22149, -22196, -22243, -22291, -22338, -22385, -22433, + -22480, -22527, -22574, -22621, -22668, -22716, -22763, -22810, + -22857, -22904, -22951, -22998, -23045, -23092, -23139, -23186, + -23233, -23280, -23327, -23374, -23421, -23468, -23515, -23562, + -23609, -23656, -23703, -23750, -23796, -23843, -23890, -23937, + -23984, -24030, -24077, -24124, -24171, -24217, -24264, -24311, + -24357, -24404, -24451, -24497, -24544, -24591, -24637, -24684, + -24730, -24777, -24823, -24870, -24916, -24963, -25009, -25056, + -25102, -25149, -25195, -25241, -25288, -25334, -25381, -25427, + -25473, -25520, -25566, -25612, -25658, -25705, -25751, -25797, + -25843, -25889, -25936, -25982, -26028, -26074, -26120, -26166, + -26212, -26258, -26304, -26350, -26396, -26442, -26488, -26534, + -26580, -26626, -26672, -26718, -26764, -26810, -26856, -26902, + -26947, -26993, -27039, -27085, -27131, -27176, -27222, -27268, + -27313, -27359, -27405, -27450, -27496, -27542, -27587, -27633, + -27678, -27724, -27770, -27815, -27861, -27906, -27952, -27997, + -28042, -28088, -28133, -28179, -28224, -28269, -28315, -28360, + -28405, -28451, -28496, -28541, -28586, -28632, -28677, -28722, + -28767, -28812, -28858, -28903, -28948, -28993, -29038, -29083, + -29128, -29173, -29218, -29263, -29308, -29353, -29398, -29443, + -29488, -29533, -29577, -29622, -29667, -29712, -29757, -29801, + -29846, -29891, -29936, -29980, -30025, -30070, -30114, -30159, + -30204, -30248, -30293, -30337, -30382, -30426, -30471, -30515, + -30560, -30604, -30649, -30693, -30738, -30782, -30826, -30871, + -30915, -30959, -31004, -31048, -31092, -31136, -31181, -31225, + -31269, -31313, -31357, -31402, -31446, -31490, -31534, -31578, + -31622, -31666, -31710, -31754, -31798, -31842, -31886, -31930, + -31974, -32017, -32061, -32105, -32149, -32193, -32236, -32280, + -32324, -32368, -32411, -32455, -32499, -32542, -32586, -32630, + -32673, -32717, -32760, -32804, -32847, -32891, -32934, -32978, + -33021, -33065, -33108, -33151, -33195, -33238, -33281, -33325, + -33368, -33411, -33454, -33498, -33541, -33584, -33627, -33670, + -33713, -33756, -33799, -33843, -33886, -33929, -33972, -34015, + -34057, -34100, -34143, -34186, -34229, -34272, -34315, -34358, + -34400, -34443, -34486, -34529, -34571, -34614, -34657, -34699, + -34742, -34785, -34827, -34870, -34912, -34955, -34997, -35040, + -35082, -35125, -35167, -35210, -35252, -35294, -35337, -35379, + -35421, -35464, -35506, -35548, -35590, -35633, -35675, -35717, + -35759, -35801, -35843, -35885, -35927, -35969, -36011, -36053, + -36095, -36137, -36179, -36221, -36263, -36305, -36347, -36388, + -36430, -36472, -36514, -36555, -36597, -36639, -36681, -36722, + -36764, -36805, -36847, -36889, -36930, -36972, -37013, -37055, + -37096, -37137, -37179, -37220, -37262, -37303, -37344, -37386, + -37427, -37468, -37509, -37551, -37592, -37633, -37674, -37715, + -37756, -37797, -37838, -37879, -37920, -37961, -38002, -38043, + -38084, -38125, -38166, -38207, -38248, -38288, -38329, -38370, + -38411, -38451, -38492, -38533, -38573, -38614, -38655, -38695, + -38736, -38776, -38817, -38857, -38898, -38938, -38979, -39019, + -39059, -39100, -39140, -39180, -39221, -39261, -39301, -39341, + -39382, -39422, -39462, -39502, -39542, -39582, -39622, -39662, + -39702, -39742, -39782, -39822, -39862, -39902, -39942, -39982, + -40021, -40061, -40101, -40141, -40180, -40220, -40260, -40299, + -40339, -40379, -40418, -40458, -40497, -40537, -40576, -40616, + -40655, -40695, -40734, -40773, -40813, -40852, -40891, -40931, + -40970, -41009, -41048, -41087, -41127, -41166, -41205, -41244, + -41283, -41322, -41361, -41400, -41439, -41478, -41517, -41556, + -41595, -41633, -41672, -41711, -41750, -41788, -41827, -41866, + -41904, -41943, -41982, -42020, -42059, -42097, -42136, -42174, + -42213, -42251, -42290, -42328, -42366, -42405, -42443, -42481, + -42520, -42558, -42596, -42634, -42672, -42711, -42749, -42787, + -42825, -42863, -42901, -42939, -42977, -43015, -43053, -43091, + -43128, -43166, -43204, -43242, -43280, -43317, -43355, -43393, + -43430, -43468, -43506, -43543, -43581, -43618, -43656, -43693, + -43731, -43768, -43806, -43843, -43880, -43918, -43955, -43992, + -44029, -44067, -44104, -44141, -44178, -44215, -44252, -44289, + -44326, -44363, -44400, -44437, -44474, -44511, -44548, -44585, + -44622, -44659, -44695, -44732, -44769, -44806, -44842, -44879, + -44915, -44952, -44989, -45025, -45062, -45098, -45135, -45171, + -45207, -45244, -45280, -45316, -45353, -45389, -45425, -45462, + -45498, -45534, -45570, -45606, -45642, -45678, -45714, -45750, + -45786, -45822, -45858, -45894, -45930, -45966, -46002, -46037, + -46073, -46109, -46145, -46180, -46216, -46252, -46287, -46323, + -46358, -46394, -46429, -46465, -46500, -46536, -46571, -46606, + -46642, -46677, -46712, -46747, -46783, -46818, -46853, -46888, + -46923, -46958, -46993, -47028, -47063, -47098, -47133, -47168, + -47203, -47238, -47273, -47308, -47342, -47377, -47412, -47446, + -47481, -47516, -47550, -47585, -47619, -47654, -47688, -47723, + -47757, -47792, -47826, -47860, -47895, -47929, -47963, -47998, + -48032, -48066, -48100, -48134, -48168, -48202, -48236, -48271, + -48304, -48338, -48372, -48406, -48440, -48474, -48508, -48542, + -48575, -48609, -48643, -48676, -48710, -48744, -48777, -48811, + -48844, -48878, -48911, -48945, -48978, -49012, -49045, -49078, + -49112, -49145, -49178, -49211, -49244, -49278, -49311, -49344, + -49377, -49410, -49443, -49476, -49509, -49542, -49575, -49608, + -49640, -49673, -49706, -49739, -49771, -49804, -49837, -49869, + -49902, -49935, -49967, -50000, -50032, -50065, -50097, -50129, + -50162, -50194, -50226, -50259, -50291, -50323, -50355, -50387, + -50420, -50452, -50484, -50516, -50548, -50580, -50612, -50644, + -50675, -50707, -50739, -50771, -50803, -50834, -50866, -50898, + -50929, -50961, -50993, -51024, -51056, -51087, -51119, -51150, + -51182, -51213, -51244, -51276, -51307, -51338, -51369, -51401, + -51432, -51463, -51494, -51525, -51556, -51587, -51618, -51649, + -51680, -51711, -51742, -51773, -51803, -51834, -51865, -51896, + -51926, -51957, -51988, -52018, -52049, -52079, -52110, -52140, + -52171, -52201, -52231, -52262, -52292, -52322, -52353, -52383, + -52413, -52443, -52473, -52503, -52534, -52564, -52594, -52624, + -52653, -52683, -52713, -52743, -52773, -52803, -52832, -52862, + -52892, -52922, -52951, -52981, -53010, -53040, -53069, -53099, + -53128, -53158, -53187, -53216, -53246, -53275, -53304, -53334, + -53363, -53392, -53421, -53450, -53479, -53508, -53537, -53566, + -53595, -53624, -53653, -53682, -53711, -53739, -53768, -53797, + -53826, -53854, -53883, -53911, -53940, -53969, -53997, -54026, + -54054, -54082, -54111, -54139, -54167, -54196, -54224, -54252, + -54280, -54308, -54337, -54365, -54393, -54421, -54449, -54477, + -54505, -54533, -54560, -54588, -54616, -54644, -54672, -54699, + -54727, -54755, -54782, -54810, -54837, -54865, -54892, -54920, + -54947, -54974, -55002, -55029, -55056, -55084, -55111, -55138, + -55165, -55192, -55219, -55246, -55274, -55300, -55327, -55354, + -55381, -55408, -55435, -55462, -55489, -55515, -55542, -55569, + -55595, -55622, -55648, -55675, -55701, -55728, -55754, -55781, + -55807, -55833, -55860, -55886, -55912, -55938, -55965, -55991, + -56017, -56043, -56069, -56095, -56121, -56147, -56173, -56199, + -56225, -56250, -56276, -56302, -56328, -56353, -56379, -56404, + -56430, -56456, -56481, -56507, -56532, -56557, -56583, -56608, + -56633, -56659, -56684, -56709, -56734, -56760, -56785, -56810, + -56835, -56860, -56885, -56910, -56935, -56959, -56984, -57009, + -57034, -57059, -57083, -57108, -57133, -57157, -57182, -57206, + -57231, -57255, -57280, -57304, -57329, -57353, -57377, -57402, + -57426, -57450, -57474, -57498, -57522, -57546, -57570, -57594, + -57618, -57642, -57666, -57690, -57714, -57738, -57762, -57785, + -57809, -57833, -57856, -57880, -57903, -57927, -57950, -57974, + -57997, -58021, -58044, -58067, -58091, -58114, -58137, -58160, + -58183, -58207, -58230, -58253, -58276, -58299, -58322, -58345, + -58367, -58390, -58413, -58436, -58459, -58481, -58504, -58527, + -58549, -58572, -58594, -58617, -58639, -58662, -58684, -58706, + -58729, -58751, -58773, -58795, -58818, -58840, -58862, -58884, + -58906, -58928, -58950, -58972, -58994, -59016, -59038, -59059, + -59081, -59103, -59125, -59146, -59168, -59190, -59211, -59233, + -59254, -59276, -59297, -59318, -59340, -59361, -59382, -59404, + -59425, -59446, -59467, -59488, -59509, -59530, -59551, -59572, + -59593, -59614, -59635, -59656, -59677, -59697, -59718, -59739, + -59759, -59780, -59801, -59821, -59842, -59862, -59883, -59903, + -59923, -59944, -59964, -59984, -60004, -60025, -60045, -60065, + -60085, -60105, -60125, -60145, -60165, -60185, -60205, -60225, + -60244, -60264, -60284, -60304, -60323, -60343, -60363, -60382, + -60402, -60421, -60441, -60460, -60479, -60499, -60518, -60537, + -60556, -60576, -60595, -60614, -60633, -60652, -60671, -60690, + -60709, -60728, -60747, -60766, -60785, -60803, -60822, -60841, + -60859, -60878, -60897, -60915, -60934, -60952, -60971, -60989, + -61007, -61026, -61044, -61062, -61081, -61099, -61117, -61135, + -61153, -61171, -61189, -61207, -61225, -61243, -61261, -61279, + -61297, -61314, -61332, -61350, -61367, -61385, -61403, -61420, + -61438, -61455, -61473, -61490, -61507, -61525, -61542, -61559, + -61577, -61594, -61611, -61628, -61645, -61662, -61679, -61696, + -61713, -61730, -61747, -61764, -61780, -61797, -61814, -61831, + -61847, -61864, -61880, -61897, -61913, -61930, -61946, -61963, + -61979, -61995, -62012, -62028, -62044, -62060, -62076, -62092, + -62108, -62125, -62141, -62156, -62172, -62188, -62204, -62220, + -62236, -62251, -62267, -62283, -62298, -62314, -62329, -62345, + -62360, -62376, -62391, -62407, -62422, -62437, -62453, -62468, + -62483, -62498, -62513, -62528, -62543, -62558, -62573, -62588, + -62603, -62618, -62633, -62648, -62662, -62677, -62692, -62706, + -62721, -62735, -62750, -62764, -62779, -62793, -62808, -62822, + -62836, -62850, -62865, -62879, -62893, -62907, -62921, -62935, + -62949, -62963, -62977, -62991, -63005, -63019, -63032, -63046, + -63060, -63074, -63087, -63101, -63114, -63128, -63141, -63155, + -63168, -63182, -63195, -63208, -63221, -63235, -63248, -63261, + -63274, -63287, -63300, -63313, -63326, -63339, -63352, -63365, + -63378, -63390, -63403, -63416, -63429, -63441, -63454, -63466, + -63479, -63491, -63504, -63516, -63528, -63541, -63553, -63565, + -63578, -63590, -63602, -63614, -63626, -63638, -63650, -63662, + -63674, -63686, -63698, -63709, -63721, -63733, -63745, -63756, + -63768, -63779, -63791, -63803, -63814, -63825, -63837, -63848, + -63859, -63871, -63882, -63893, -63904, -63915, -63927, -63938, + -63949, -63960, -63971, -63981, -63992, -64003, -64014, -64025, + -64035, -64046, -64057, -64067, -64078, -64088, -64099, -64109, + -64120, -64130, -64140, -64151, -64161, -64171, -64181, -64192, + -64202, -64212, -64222, -64232, -64242, -64252, -64261, -64271, + -64281, -64291, -64301, -64310, -64320, -64330, -64339, -64349, + -64358, -64368, -64377, -64387, -64396, -64405, -64414, -64424, + -64433, -64442, -64451, -64460, -64469, -64478, -64487, -64496, + -64505, -64514, -64523, -64532, -64540, -64549, -64558, -64566, + -64575, -64584, -64592, -64601, -64609, -64617, -64626, -64634, + -64642, -64651, -64659, -64667, -64675, -64683, -64691, -64699, + -64707, -64715, -64723, -64731, -64739, -64747, -64754, -64762, + -64770, -64777, -64785, -64793, -64800, -64808, -64815, -64822, + -64830, -64837, -64844, -64852, -64859, -64866, -64873, -64880, + -64887, -64895, -64902, -64908, -64915, -64922, -64929, -64936, + -64943, -64949, -64956, -64963, -64969, -64976, -64982, -64989, + -64995, -65002, -65008, -65015, -65021, -65027, -65033, -65040, + -65046, -65052, -65058, -65064, -65070, -65076, -65082, -65088, + -65094, -65099, -65105, -65111, -65117, -65122, -65128, -65133, + -65139, -65144, -65150, -65155, -65161, -65166, -65171, -65177, + -65182, -65187, -65192, -65197, -65202, -65207, -65212, -65217, + -65222, -65227, -65232, -65237, -65242, -65246, -65251, -65256, + -65260, -65265, -65270, -65274, -65279, -65283, -65287, -65292, + -65296, -65300, -65305, -65309, -65313, -65317, -65321, -65325, + -65329, -65333, -65337, -65341, -65345, -65349, -65352, -65356, + -65360, -65363, -65367, -65371, -65374, -65378, -65381, -65385, + -65388, -65391, -65395, -65398, -65401, -65404, -65408, -65411, + -65414, -65417, -65420, -65423, -65426, -65429, -65431, -65434, + -65437, -65440, -65442, -65445, -65448, -65450, -65453, -65455, + -65458, -65460, -65463, -65465, -65467, -65470, -65472, -65474, + -65476, -65478, -65480, -65482, -65484, -65486, -65488, -65490, + -65492, -65494, -65496, -65497, -65499, -65501, -65502, -65504, + -65505, -65507, -65508, -65510, -65511, -65513, -65514, -65515, + -65516, -65518, -65519, -65520, -65521, -65522, -65523, -65524, + -65525, -65526, -65527, -65527, -65528, -65529, -65530, -65530, + -65531, -65531, -65532, -65532, -65533, -65533, -65534, -65534, + -65534, -65535, -65535, -65535, -65535, -65535, -65535, -65535, + -65535, -65535, -65535, -65535, -65535, -65535, -65535, -65534, + -65534, -65534, -65533, -65533, -65532, -65532, -65531, -65531, + -65530, -65530, -65529, -65528, -65527, -65527, -65526, -65525, + -65524, -65523, -65522, -65521, -65520, -65519, -65518, -65516, + -65515, -65514, -65513, -65511, -65510, -65508, -65507, -65505, + -65504, -65502, -65501, -65499, -65497, -65496, -65494, -65492, + -65490, -65488, -65486, -65484, -65482, -65480, -65478, -65476, + -65474, -65472, -65470, -65467, -65465, -65463, -65460, -65458, + -65455, -65453, -65450, -65448, -65445, -65442, -65440, -65437, + -65434, -65431, -65429, -65426, -65423, -65420, -65417, -65414, + -65411, -65408, -65404, -65401, -65398, -65395, -65391, -65388, + -65385, -65381, -65378, -65374, -65371, -65367, -65363, -65360, + -65356, -65352, -65349, -65345, -65341, -65337, -65333, -65329, + -65325, -65321, -65317, -65313, -65309, -65305, -65300, -65296, + -65292, -65287, -65283, -65279, -65274, -65270, -65265, -65260, + -65256, -65251, -65246, -65242, -65237, -65232, -65227, -65222, + -65217, -65212, -65207, -65202, -65197, -65192, -65187, -65182, + -65177, -65171, -65166, -65161, -65155, -65150, -65144, -65139, + -65133, -65128, -65122, -65117, -65111, -65105, -65099, -65094, + -65088, -65082, -65076, -65070, -65064, -65058, -65052, -65046, + -65040, -65033, -65027, -65021, -65015, -65008, -65002, -64995, + -64989, -64982, -64976, -64969, -64963, -64956, -64949, -64943, + -64936, -64929, -64922, -64915, -64908, -64902, -64895, -64887, + -64880, -64873, -64866, -64859, -64852, -64844, -64837, -64830, + -64822, -64815, -64808, -64800, -64793, -64785, -64777, -64770, + -64762, -64754, -64747, -64739, -64731, -64723, -64715, -64707, + -64699, -64691, -64683, -64675, -64667, -64659, -64651, -64642, + -64634, -64626, -64617, -64609, -64601, -64592, -64584, -64575, + -64566, -64558, -64549, -64540, -64532, -64523, -64514, -64505, + -64496, -64487, -64478, -64469, -64460, -64451, -64442, -64433, + -64424, -64414, -64405, -64396, -64387, -64377, -64368, -64358, + -64349, -64339, -64330, -64320, -64310, -64301, -64291, -64281, + -64271, -64261, -64252, -64242, -64232, -64222, -64212, -64202, + -64192, -64181, -64171, -64161, -64151, -64140, -64130, -64120, + -64109, -64099, -64088, -64078, -64067, -64057, -64046, -64035, + -64025, -64014, -64003, -63992, -63981, -63971, -63960, -63949, + -63938, -63927, -63915, -63904, -63893, -63882, -63871, -63859, + -63848, -63837, -63825, -63814, -63803, -63791, -63779, -63768, + -63756, -63745, -63733, -63721, -63709, -63698, -63686, -63674, + -63662, -63650, -63638, -63626, -63614, -63602, -63590, -63578, + -63565, -63553, -63541, -63528, -63516, -63504, -63491, -63479, + -63466, -63454, -63441, -63429, -63416, -63403, -63390, -63378, + -63365, -63352, -63339, -63326, -63313, -63300, -63287, -63274, + -63261, -63248, -63235, -63221, -63208, -63195, -63182, -63168, + -63155, -63141, -63128, -63114, -63101, -63087, -63074, -63060, + -63046, -63032, -63019, -63005, -62991, -62977, -62963, -62949, + -62935, -62921, -62907, -62893, -62879, -62865, -62850, -62836, + -62822, -62808, -62793, -62779, -62764, -62750, -62735, -62721, + -62706, -62692, -62677, -62662, -62648, -62633, -62618, -62603, + -62588, -62573, -62558, -62543, -62528, -62513, -62498, -62483, + -62468, -62453, -62437, -62422, -62407, -62391, -62376, -62360, + -62345, -62329, -62314, -62298, -62283, -62267, -62251, -62236, + -62220, -62204, -62188, -62172, -62156, -62141, -62125, -62108, + -62092, -62076, -62060, -62044, -62028, -62012, -61995, -61979, + -61963, -61946, -61930, -61913, -61897, -61880, -61864, -61847, + -61831, -61814, -61797, -61780, -61764, -61747, -61730, -61713, + -61696, -61679, -61662, -61645, -61628, -61611, -61594, -61577, + -61559, -61542, -61525, -61507, -61490, -61473, -61455, -61438, + -61420, -61403, -61385, -61367, -61350, -61332, -61314, -61297, + -61279, -61261, -61243, -61225, -61207, -61189, -61171, -61153, + -61135, -61117, -61099, -61081, -61062, -61044, -61026, -61007, + -60989, -60971, -60952, -60934, -60915, -60897, -60878, -60859, + -60841, -60822, -60803, -60785, -60766, -60747, -60728, -60709, + -60690, -60671, -60652, -60633, -60614, -60595, -60576, -60556, + -60537, -60518, -60499, -60479, -60460, -60441, -60421, -60402, + -60382, -60363, -60343, -60323, -60304, -60284, -60264, -60244, + -60225, -60205, -60185, -60165, -60145, -60125, -60105, -60085, + -60065, -60045, -60025, -60004, -59984, -59964, -59944, -59923, + -59903, -59883, -59862, -59842, -59821, -59801, -59780, -59759, + -59739, -59718, -59697, -59677, -59656, -59635, -59614, -59593, + -59572, -59551, -59530, -59509, -59488, -59467, -59446, -59425, + -59404, -59382, -59361, -59340, -59318, -59297, -59276, -59254, + -59233, -59211, -59189, -59168, -59146, -59125, -59103, -59081, + -59059, -59038, -59016, -58994, -58972, -58950, -58928, -58906, + -58884, -58862, -58840, -58818, -58795, -58773, -58751, -58729, + -58706, -58684, -58662, -58639, -58617, -58594, -58572, -58549, + -58527, -58504, -58481, -58459, -58436, -58413, -58390, -58367, + -58345, -58322, -58299, -58276, -58253, -58230, -58207, -58183, + -58160, -58137, -58114, -58091, -58067, -58044, -58021, -57997, + -57974, -57950, -57927, -57903, -57880, -57856, -57833, -57809, + -57785, -57762, -57738, -57714, -57690, -57666, -57642, -57618, + -57594, -57570, -57546, -57522, -57498, -57474, -57450, -57426, + -57402, -57377, -57353, -57329, -57304, -57280, -57255, -57231, + -57206, -57182, -57157, -57133, -57108, -57083, -57059, -57034, + -57009, -56984, -56959, -56935, -56910, -56885, -56860, -56835, + -56810, -56785, -56760, -56734, -56709, -56684, -56659, -56633, + -56608, -56583, -56557, -56532, -56507, -56481, -56456, -56430, + -56404, -56379, -56353, -56328, -56302, -56276, -56250, -56225, + -56199, -56173, -56147, -56121, -56095, -56069, -56043, -56017, + -55991, -55965, -55938, -55912, -55886, -55860, -55833, -55807, + -55781, -55754, -55728, -55701, -55675, -55648, -55622, -55595, + -55569, -55542, -55515, -55489, -55462, -55435, -55408, -55381, + -55354, -55327, -55300, -55274, -55246, -55219, -55192, -55165, + -55138, -55111, -55084, -55056, -55029, -55002, -54974, -54947, + -54920, -54892, -54865, -54837, -54810, -54782, -54755, -54727, + -54699, -54672, -54644, -54616, -54588, -54560, -54533, -54505, + -54477, -54449, -54421, -54393, -54365, -54337, -54308, -54280, + -54252, -54224, -54196, -54167, -54139, -54111, -54082, -54054, + -54026, -53997, -53969, -53940, -53911, -53883, -53854, -53826, + -53797, -53768, -53739, -53711, -53682, -53653, -53624, -53595, + -53566, -53537, -53508, -53479, -53450, -53421, -53392, -53363, + -53334, -53304, -53275, -53246, -53216, -53187, -53158, -53128, + -53099, -53069, -53040, -53010, -52981, -52951, -52922, -52892, + -52862, -52832, -52803, -52773, -52743, -52713, -52683, -52653, + -52624, -52594, -52564, -52534, -52503, -52473, -52443, -52413, + -52383, -52353, -52322, -52292, -52262, -52231, -52201, -52171, + -52140, -52110, -52079, -52049, -52018, -51988, -51957, -51926, + -51896, -51865, -51834, -51803, -51773, -51742, -51711, -51680, + -51649, -51618, -51587, -51556, -51525, -51494, -51463, -51432, + -51401, -51369, -51338, -51307, -51276, -51244, -51213, -51182, + -51150, -51119, -51087, -51056, -51024, -50993, -50961, -50929, + -50898, -50866, -50834, -50803, -50771, -50739, -50707, -50675, + -50644, -50612, -50580, -50548, -50516, -50484, -50452, -50420, + -50387, -50355, -50323, -50291, -50259, -50226, -50194, -50162, + -50129, -50097, -50065, -50032, -50000, -49967, -49935, -49902, + -49869, -49837, -49804, -49771, -49739, -49706, -49673, -49640, + -49608, -49575, -49542, -49509, -49476, -49443, -49410, -49377, + -49344, -49311, -49278, -49244, -49211, -49178, -49145, -49112, + -49078, -49045, -49012, -48978, -48945, -48911, -48878, -48844, + -48811, -48777, -48744, -48710, -48676, -48643, -48609, -48575, + -48542, -48508, -48474, -48440, -48406, -48372, -48338, -48305, + -48271, -48237, -48202, -48168, -48134, -48100, -48066, -48032, + -47998, -47963, -47929, -47895, -47860, -47826, -47792, -47757, + -47723, -47688, -47654, -47619, -47585, -47550, -47516, -47481, + -47446, -47412, -47377, -47342, -47307, -47273, -47238, -47203, + -47168, -47133, -47098, -47063, -47028, -46993, -46958, -46923, + -46888, -46853, -46818, -46783, -46747, -46712, -46677, -46642, + -46606, -46571, -46536, -46500, -46465, -46429, -46394, -46358, + -46323, -46287, -46251, -46216, -46180, -46145, -46109, -46073, + -46037, -46002, -45966, -45930, -45894, -45858, -45822, -45786, + -45750, -45714, -45678, -45642, -45606, -45570, -45534, -45498, + -45462, -45425, -45389, -45353, -45316, -45280, -45244, -45207, + -45171, -45135, -45098, -45062, -45025, -44989, -44952, -44915, + -44879, -44842, -44806, -44769, -44732, -44695, -44659, -44622, + -44585, -44548, -44511, -44474, -44437, -44400, -44363, -44326, + -44289, -44252, -44215, -44178, -44141, -44104, -44067, -44029, + -43992, -43955, -43918, -43880, -43843, -43806, -43768, -43731, + -43693, -43656, -43618, -43581, -43543, -43506, -43468, -43430, + -43393, -43355, -43317, -43280, -43242, -43204, -43166, -43128, + -43091, -43053, -43015, -42977, -42939, -42901, -42863, -42825, + -42787, -42749, -42711, -42672, -42634, -42596, -42558, -42520, + -42481, -42443, -42405, -42366, -42328, -42290, -42251, -42213, + -42174, -42136, -42097, -42059, -42020, -41982, -41943, -41904, + -41866, -41827, -41788, -41750, -41711, -41672, -41633, -41595, + -41556, -41517, -41478, -41439, -41400, -41361, -41322, -41283, + -41244, -41205, -41166, -41127, -41087, -41048, -41009, -40970, + -40931, -40891, -40852, -40813, -40773, -40734, -40695, -40655, + -40616, -40576, -40537, -40497, -40458, -40418, -40379, -40339, + -40299, -40260, -40220, -40180, -40141, -40101, -40061, -40021, + -39982, -39942, -39902, -39862, -39822, -39782, -39742, -39702, + -39662, -39622, -39582, -39542, -39502, -39462, -39422, -39382, + -39341, -39301, -39261, -39221, -39180, -39140, -39100, -39059, + -39019, -38979, -38938, -38898, -38857, -38817, -38776, -38736, + -38695, -38655, -38614, -38573, -38533, -38492, -38451, -38411, + -38370, -38329, -38288, -38248, -38207, -38166, -38125, -38084, + -38043, -38002, -37961, -37920, -37879, -37838, -37797, -37756, + -37715, -37674, -37633, -37592, -37550, -37509, -37468, -37427, + -37386, -37344, -37303, -37262, -37220, -37179, -37137, -37096, + -37055, -37013, -36972, -36930, -36889, -36847, -36805, -36764, + -36722, -36681, -36639, -36597, -36556, -36514, -36472, -36430, + -36388, -36347, -36305, -36263, -36221, -36179, -36137, -36095, + -36053, -36011, -35969, -35927, -35885, -35843, -35801, -35759, + -35717, -35675, -35633, -35590, -35548, -35506, -35464, -35421, + -35379, -35337, -35294, -35252, -35210, -35167, -35125, -35082, + -35040, -34997, -34955, -34912, -34870, -34827, -34785, -34742, + -34699, -34657, -34614, -34571, -34529, -34486, -34443, -34400, + -34358, -34315, -34272, -34229, -34186, -34143, -34100, -34057, + -34015, -33972, -33929, -33886, -33843, -33799, -33756, -33713, + -33670, -33627, -33584, -33541, -33498, -33454, -33411, -33368, + -33325, -33281, -33238, -33195, -33151, -33108, -33065, -33021, + -32978, -32934, -32891, -32847, -32804, -32760, -32717, -32673, + -32630, -32586, -32542, -32499, -32455, -32411, -32368, -32324, + -32280, -32236, -32193, -32149, -32105, -32061, -32017, -31974, + -31930, -31886, -31842, -31798, -31754, -31710, -31666, -31622, + -31578, -31534, -31490, -31446, -31402, -31357, -31313, -31269, + -31225, -31181, -31136, -31092, -31048, -31004, -30959, -30915, + -30871, -30826, -30782, -30738, -30693, -30649, -30604, -30560, + -30515, -30471, -30426, -30382, -30337, -30293, -30248, -30204, + -30159, -30114, -30070, -30025, -29980, -29936, -29891, -29846, + -29801, -29757, -29712, -29667, -29622, -29577, -29533, -29488, + -29443, -29398, -29353, -29308, -29263, -29218, -29173, -29128, + -29083, -29038, -28993, -28948, -28903, -28858, -28812, -28767, + -28722, -28677, -28632, -28586, -28541, -28496, -28451, -28405, + -28360, -28315, -28269, -28224, -28179, -28133, -28088, -28042, + -27997, -27952, -27906, -27861, -27815, -27770, -27724, -27678, + -27633, -27587, -27542, -27496, -27450, -27405, -27359, -27313, + -27268, -27222, -27176, -27131, -27085, -27039, -26993, -26947, + -26902, -26856, -26810, -26764, -26718, -26672, -26626, -26580, + -26534, -26488, -26442, -26396, -26350, -26304, -26258, -26212, + -26166, -26120, -26074, -26028, -25982, -25936, -25889, -25843, + -25797, -25751, -25705, -25658, -25612, -25566, -25520, -25473, + -25427, -25381, -25334, -25288, -25241, -25195, -25149, -25102, + -25056, -25009, -24963, -24916, -24870, -24823, -24777, -24730, + -24684, -24637, -24591, -24544, -24497, -24451, -24404, -24357, + -24311, -24264, -24217, -24171, -24124, -24077, -24030, -23984, + -23937, -23890, -23843, -23796, -23750, -23703, -23656, -23609, + -23562, -23515, -23468, -23421, -23374, -23327, -23280, -23233, + -23186, -23139, -23092, -23045, -22998, -22951, -22904, -22857, + -22810, -22763, -22716, -22668, -22621, -22574, -22527, -22480, + -22432, -22385, -22338, -22291, -22243, -22196, -22149, -22102, + -22054, -22007, -21960, -21912, -21865, -21817, -21770, -21723, + -21675, -21628, -21580, -21533, -21485, -21438, -21390, -21343, + -21295, -21248, -21200, -21153, -21105, -21057, -21010, -20962, + -20915, -20867, -20819, -20772, -20724, -20676, -20629, -20581, + -20533, -20485, -20438, -20390, -20342, -20294, -20246, -20199, + -20151, -20103, -20055, -20007, -19959, -19912, -19864, -19816, + -19768, -19720, -19672, -19624, -19576, -19528, -19480, -19432, + -19384, -19336, -19288, -19240, -19192, -19144, -19096, -19048, + -19000, -18951, -18903, -18855, -18807, -18759, -18711, -18663, + -18614, -18566, -18518, -18470, -18421, -18373, -18325, -18277, + -18228, -18180, -18132, -18084, -18035, -17987, -17939, -17890, + -17842, -17793, -17745, -17697, -17648, -17600, -17551, -17503, + -17455, -17406, -17358, -17309, -17261, -17212, -17164, -17115, + -17067, -17018, -16970, -16921, -16872, -16824, -16775, -16727, + -16678, -16629, -16581, -16532, -16484, -16435, -16386, -16338, + -16289, -16240, -16191, -16143, -16094, -16045, -15997, -15948, + -15899, -15850, -15802, -15753, -15704, -15655, -15606, -15557, + -15509, -15460, -15411, -15362, -15313, -15264, -15215, -15167, + -15118, -15069, -15020, -14971, -14922, -14873, -14824, -14775, + -14726, -14677, -14628, -14579, -14530, -14481, -14432, -14383, + -14334, -14285, -14236, -14187, -14138, -14089, -14040, -13990, + -13941, -13892, -13843, -13794, -13745, -13696, -13647, -13597, + -13548, -13499, -13450, -13401, -13351, -13302, -13253, -13204, + -13154, -13105, -13056, -13007, -12957, -12908, -12859, -12810, + -12760, -12711, -12662, -12612, -12563, -12514, -12464, -12415, + -12366, -12316, -12267, -12217, -12168, -12119, -12069, -12020, + -11970, -11921, -11872, -11822, -11773, -11723, -11674, -11624, + -11575, -11525, -11476, -11426, -11377, -11327, -11278, -11228, + -11179, -11129, -11080, -11030, -10981, -10931, -10882, -10832, + -10782, -10733, -10683, -10634, -10584, -10534, -10485, -10435, + -10386, -10336, -10286, -10237, -10187, -10137, -10088, -10038, + -9988, -9939, -9889, -9839, -9790, -9740, -9690, -9640, + -9591, -9541, -9491, -9442, -9392, -9342, -9292, -9243, + -9193, -9143, -9093, -9043, -8994, -8944, -8894, -8844, + -8794, -8745, -8695, -8645, -8595, -8545, -8496, -8446, + -8396, -8346, -8296, -8246, -8196, -8147, -8097, -8047, + -7997, -7947, -7897, -7847, -7797, -7747, -7697, -7648, + -7598, -7548, -7498, -7448, -7398, -7348, -7298, -7248, + -7198, -7148, -7098, -7048, -6998, -6948, -6898, -6848, + -6798, -6748, -6698, -6648, -6598, -6548, -6498, -6448, + -6398, -6348, -6298, -6248, -6198, -6148, -6098, -6048, + -5998, -5948, -5898, -5848, -5798, -5747, -5697, -5647, + -5597, -5547, -5497, -5447, -5397, -5347, -5297, -5247, + -5197, -5146, -5096, -5046, -4996, -4946, -4896, -4846, + -4796, -4745, -4695, -4645, -4595, -4545, -4495, -4445, + -4394, -4344, -4294, -4244, -4194, -4144, -4093, -4043, + -3993, -3943, -3893, -3843, -3792, -3742, -3692, -3642, + -3592, -3541, -3491, -3441, -3391, -3341, -3291, -3240, + -3190, -3140, -3090, -3039, -2989, -2939, -2889, -2839, + -2788, -2738, -2688, -2638, -2588, -2537, -2487, -2437, + -2387, -2336, -2286, -2236, -2186, -2135, -2085, -2035, + -1985, -1934, -1884, -1834, -1784, -1733, -1683, -1633, + -1583, -1532, -1482, -1432, -1382, -1331, -1281, -1231, + -1181, -1130, -1080, -1030, -980, -929, -879, -829, + -779, -728, -678, -628, -578, -527, -477, -427, + -376, -326, -276, -226, -175, -125, -75, -25, + 25, 75, 125, 175, 226, 276, 326, 376, + 427, 477, 527, 578, 628, 678, 728, 779, + 829, 879, 929, 980, 1030, 1080, 1130, 1181, + 1231, 1281, 1331, 1382, 1432, 1482, 1532, 1583, + 1633, 1683, 1733, 1784, 1834, 1884, 1934, 1985, + 2035, 2085, 2135, 2186, 2236, 2286, 2336, 2387, + 2437, 2487, 2537, 2587, 2638, 2688, 2738, 2788, + 2839, 2889, 2939, 2989, 3039, 3090, 3140, 3190, + 3240, 3291, 3341, 3391, 3441, 3491, 3542, 3592, + 3642, 3692, 3742, 3792, 3843, 3893, 3943, 3993, + 4043, 4093, 4144, 4194, 4244, 4294, 4344, 4394, + 4445, 4495, 4545, 4595, 4645, 4695, 4745, 4796, + 4846, 4896, 4946, 4996, 5046, 5096, 5146, 5197, + 5247, 5297, 5347, 5397, 5447, 5497, 5547, 5597, + 5647, 5697, 5747, 5798, 5848, 5898, 5948, 5998, + 6048, 6098, 6148, 6198, 6248, 6298, 6348, 6398, + 6448, 6498, 6548, 6598, 6648, 6698, 6748, 6798, + 6848, 6898, 6948, 6998, 7048, 7098, 7148, 7198, + 7248, 7298, 7348, 7398, 7448, 7498, 7548, 7598, + 7648, 7697, 7747, 7797, 7847, 7897, 7947, 7997, + 8047, 8097, 8147, 8196, 8246, 8296, 8346, 8396, + 8446, 8496, 8545, 8595, 8645, 8695, 8745, 8794, + 8844, 8894, 8944, 8994, 9043, 9093, 9143, 9193, + 9243, 9292, 9342, 9392, 9442, 9491, 9541, 9591, + 9640, 9690, 9740, 9790, 9839, 9889, 9939, 9988, + 10038, 10088, 10137, 10187, 10237, 10286, 10336, 10386, + 10435, 10485, 10534, 10584, 10634, 10683, 10733, 10782, + 10832, 10882, 10931, 10981, 11030, 11080, 11129, 11179, + 11228, 11278, 11327, 11377, 11426, 11476, 11525, 11575, + 11624, 11674, 11723, 11773, 11822, 11872, 11921, 11970, + 12020, 12069, 12119, 12168, 12218, 12267, 12316, 12366, + 12415, 12464, 12514, 12563, 12612, 12662, 12711, 12760, + 12810, 12859, 12908, 12957, 13007, 13056, 13105, 13154, + 13204, 13253, 13302, 13351, 13401, 13450, 13499, 13548, + 13597, 13647, 13696, 13745, 13794, 13843, 13892, 13941, + 13990, 14040, 14089, 14138, 14187, 14236, 14285, 14334, + 14383, 14432, 14481, 14530, 14579, 14628, 14677, 14726, + 14775, 14824, 14873, 14922, 14971, 15020, 15069, 15118, + 15167, 15215, 15264, 15313, 15362, 15411, 15460, 15509, + 15557, 15606, 15655, 15704, 15753, 15802, 15850, 15899, + 15948, 15997, 16045, 16094, 16143, 16191, 16240, 16289, + 16338, 16386, 16435, 16484, 16532, 16581, 16629, 16678, + 16727, 16775, 16824, 16872, 16921, 16970, 17018, 17067, + 17115, 17164, 17212, 17261, 17309, 17358, 17406, 17455, + 17503, 17551, 17600, 17648, 17697, 17745, 17793, 17842, + 17890, 17939, 17987, 18035, 18084, 18132, 18180, 18228, + 18277, 18325, 18373, 18421, 18470, 18518, 18566, 18614, + 18663, 18711, 18759, 18807, 18855, 18903, 18951, 19000, + 19048, 19096, 19144, 19192, 19240, 19288, 19336, 19384, + 19432, 19480, 19528, 19576, 19624, 19672, 19720, 19768, + 19816, 19864, 19912, 19959, 20007, 20055, 20103, 20151, + 20199, 20246, 20294, 20342, 20390, 20438, 20485, 20533, + 20581, 20629, 20676, 20724, 20772, 20819, 20867, 20915, + 20962, 21010, 21057, 21105, 21153, 21200, 21248, 21295, + 21343, 21390, 21438, 21485, 21533, 21580, 21628, 21675, + 21723, 21770, 21817, 21865, 21912, 21960, 22007, 22054, + 22102, 22149, 22196, 22243, 22291, 22338, 22385, 22432, + 22480, 22527, 22574, 22621, 22668, 22716, 22763, 22810, + 22857, 22904, 22951, 22998, 23045, 23092, 23139, 23186, + 23233, 23280, 23327, 23374, 23421, 23468, 23515, 23562, + 23609, 23656, 23703, 23750, 23796, 23843, 23890, 23937, + 23984, 24030, 24077, 24124, 24171, 24217, 24264, 24311, + 24357, 24404, 24451, 24497, 24544, 24591, 24637, 24684, + 24730, 24777, 24823, 24870, 24916, 24963, 25009, 25056, + 25102, 25149, 25195, 25241, 25288, 25334, 25381, 25427, + 25473, 25520, 25566, 25612, 25658, 25705, 25751, 25797, + 25843, 25889, 25936, 25982, 26028, 26074, 26120, 26166, + 26212, 26258, 26304, 26350, 26396, 26442, 26488, 26534, + 26580, 26626, 26672, 26718, 26764, 26810, 26856, 26902, + 26947, 26993, 27039, 27085, 27131, 27176, 27222, 27268, + 27313, 27359, 27405, 27450, 27496, 27542, 27587, 27633, + 27678, 27724, 27770, 27815, 27861, 27906, 27952, 27997, + 28042, 28088, 28133, 28179, 28224, 28269, 28315, 28360, + 28405, 28451, 28496, 28541, 28586, 28632, 28677, 28722, + 28767, 28812, 28858, 28903, 28948, 28993, 29038, 29083, + 29128, 29173, 29218, 29263, 29308, 29353, 29398, 29443, + 29488, 29533, 29577, 29622, 29667, 29712, 29757, 29801, + 29846, 29891, 29936, 29980, 30025, 30070, 30114, 30159, + 30204, 30248, 30293, 30337, 30382, 30427, 30471, 30516, + 30560, 30604, 30649, 30693, 30738, 30782, 30826, 30871, + 30915, 30959, 31004, 31048, 31092, 31136, 31181, 31225, + 31269, 31313, 31357, 31402, 31446, 31490, 31534, 31578, + 31622, 31666, 31710, 31754, 31798, 31842, 31886, 31930, + 31974, 32017, 32061, 32105, 32149, 32193, 32236, 32280, + 32324, 32368, 32411, 32455, 32499, 32542, 32586, 32630, + 32673, 32717, 32760, 32804, 32847, 32891, 32934, 32978, + 33021, 33065, 33108, 33151, 33195, 33238, 33281, 33325, + 33368, 33411, 33454, 33498, 33541, 33584, 33627, 33670, + 33713, 33756, 33799, 33843, 33886, 33929, 33972, 34015, + 34057, 34100, 34143, 34186, 34229, 34272, 34315, 34358, + 34400, 34443, 34486, 34529, 34571, 34614, 34657, 34699, + 34742, 34785, 34827, 34870, 34912, 34955, 34997, 35040, + 35082, 35125, 35167, 35210, 35252, 35294, 35337, 35379, + 35421, 35464, 35506, 35548, 35590, 35633, 35675, 35717, + 35759, 35801, 35843, 35885, 35927, 35969, 36011, 36053, + 36095, 36137, 36179, 36221, 36263, 36305, 36347, 36388, + 36430, 36472, 36514, 36556, 36597, 36639, 36681, 36722, + 36764, 36805, 36847, 36889, 36930, 36972, 37013, 37055, + 37096, 37137, 37179, 37220, 37262, 37303, 37344, 37386, + 37427, 37468, 37509, 37551, 37592, 37633, 37674, 37715, + 37756, 37797, 37838, 37879, 37920, 37961, 38002, 38043, + 38084, 38125, 38166, 38207, 38248, 38288, 38329, 38370, + 38411, 38451, 38492, 38533, 38573, 38614, 38655, 38695, + 38736, 38776, 38817, 38857, 38898, 38938, 38979, 39019, + 39059, 39100, 39140, 39180, 39221, 39261, 39301, 39341, + 39382, 39422, 39462, 39502, 39542, 39582, 39622, 39662, + 39702, 39742, 39782, 39822, 39862, 39902, 39942, 39982, + 40021, 40061, 40101, 40141, 40180, 40220, 40260, 40299, + 40339, 40379, 40418, 40458, 40497, 40537, 40576, 40616, + 40655, 40695, 40734, 40773, 40813, 40852, 40891, 40931, + 40970, 41009, 41048, 41087, 41127, 41166, 41205, 41244, + 41283, 41322, 41361, 41400, 41439, 41478, 41517, 41556, + 41595, 41633, 41672, 41711, 41750, 41788, 41827, 41866, + 41904, 41943, 41982, 42020, 42059, 42097, 42136, 42174, + 42213, 42251, 42290, 42328, 42366, 42405, 42443, 42481, + 42520, 42558, 42596, 42634, 42672, 42711, 42749, 42787, + 42825, 42863, 42901, 42939, 42977, 43015, 43053, 43091, + 43128, 43166, 43204, 43242, 43280, 43317, 43355, 43393, + 43430, 43468, 43506, 43543, 43581, 43618, 43656, 43693, + 43731, 43768, 43806, 43843, 43880, 43918, 43955, 43992, + 44029, 44067, 44104, 44141, 44178, 44215, 44252, 44289, + 44326, 44363, 44400, 44437, 44474, 44511, 44548, 44585, + 44622, 44659, 44695, 44732, 44769, 44806, 44842, 44879, + 44915, 44952, 44989, 45025, 45062, 45098, 45135, 45171, + 45207, 45244, 45280, 45316, 45353, 45389, 45425, 45462, + 45498, 45534, 45570, 45606, 45642, 45678, 45714, 45750, + 45786, 45822, 45858, 45894, 45930, 45966, 46002, 46037, + 46073, 46109, 46145, 46180, 46216, 46252, 46287, 46323, + 46358, 46394, 46429, 46465, 46500, 46536, 46571, 46606, + 46642, 46677, 46712, 46747, 46783, 46818, 46853, 46888, + 46923, 46958, 46993, 47028, 47063, 47098, 47133, 47168, + 47203, 47238, 47273, 47308, 47342, 47377, 47412, 47446, + 47481, 47516, 47550, 47585, 47619, 47654, 47688, 47723, + 47757, 47792, 47826, 47861, 47895, 47929, 47963, 47998, + 48032, 48066, 48100, 48134, 48168, 48202, 48237, 48271, + 48305, 48338, 48372, 48406, 48440, 48474, 48508, 48542, + 48575, 48609, 48643, 48676, 48710, 48744, 48777, 48811, + 48844, 48878, 48911, 48945, 48978, 49012, 49045, 49078, + 49112, 49145, 49178, 49211, 49244, 49278, 49311, 49344, + 49377, 49410, 49443, 49476, 49509, 49542, 49575, 49608, + 49640, 49673, 49706, 49739, 49771, 49804, 49837, 49869, + 49902, 49935, 49967, 50000, 50032, 50064, 50097, 50129, + 50162, 50194, 50226, 50259, 50291, 50323, 50355, 50387, + 50420, 50452, 50484, 50516, 50548, 50580, 50612, 50644, + 50675, 50707, 50739, 50771, 50803, 50834, 50866, 50898, + 50929, 50961, 50993, 51024, 51056, 51087, 51119, 51150, + 51182, 51213, 51244, 51276, 51307, 51338, 51369, 51401, + 51432, 51463, 51494, 51525, 51556, 51587, 51618, 51649, + 51680, 51711, 51742, 51773, 51803, 51834, 51865, 51896, + 51926, 51957, 51988, 52018, 52049, 52079, 52110, 52140, + 52171, 52201, 52231, 52262, 52292, 52322, 52353, 52383, + 52413, 52443, 52473, 52503, 52534, 52564, 52594, 52624, + 52653, 52683, 52713, 52743, 52773, 52803, 52832, 52862, + 52892, 52922, 52951, 52981, 53010, 53040, 53069, 53099, + 53128, 53158, 53187, 53216, 53246, 53275, 53304, 53334, + 53363, 53392, 53421, 53450, 53479, 53508, 53537, 53566, + 53595, 53624, 53653, 53682, 53711, 53739, 53768, 53797, + 53826, 53854, 53883, 53912, 53940, 53969, 53997, 54026, + 54054, 54082, 54111, 54139, 54167, 54196, 54224, 54252, + 54280, 54309, 54337, 54365, 54393, 54421, 54449, 54477, + 54505, 54533, 54560, 54588, 54616, 54644, 54672, 54699, + 54727, 54755, 54782, 54810, 54837, 54865, 54892, 54920, + 54947, 54974, 55002, 55029, 55056, 55084, 55111, 55138, + 55165, 55192, 55219, 55246, 55274, 55300, 55327, 55354, + 55381, 55408, 55435, 55462, 55489, 55515, 55542, 55569, + 55595, 55622, 55648, 55675, 55701, 55728, 55754, 55781, + 55807, 55833, 55860, 55886, 55912, 55938, 55965, 55991, + 56017, 56043, 56069, 56095, 56121, 56147, 56173, 56199, + 56225, 56250, 56276, 56302, 56328, 56353, 56379, 56404, + 56430, 56456, 56481, 56507, 56532, 56557, 56583, 56608, + 56633, 56659, 56684, 56709, 56734, 56760, 56785, 56810, + 56835, 56860, 56885, 56910, 56935, 56959, 56984, 57009, + 57034, 57059, 57083, 57108, 57133, 57157, 57182, 57206, + 57231, 57255, 57280, 57304, 57329, 57353, 57377, 57402, + 57426, 57450, 57474, 57498, 57522, 57546, 57570, 57594, + 57618, 57642, 57666, 57690, 57714, 57738, 57762, 57785, + 57809, 57833, 57856, 57880, 57903, 57927, 57950, 57974, + 57997, 58021, 58044, 58067, 58091, 58114, 58137, 58160, + 58183, 58207, 58230, 58253, 58276, 58299, 58322, 58345, + 58367, 58390, 58413, 58436, 58459, 58481, 58504, 58527, + 58549, 58572, 58594, 58617, 58639, 58662, 58684, 58706, + 58729, 58751, 58773, 58795, 58818, 58840, 58862, 58884, + 58906, 58928, 58950, 58972, 58994, 59016, 59038, 59059, + 59081, 59103, 59125, 59146, 59168, 59190, 59211, 59233, + 59254, 59276, 59297, 59318, 59340, 59361, 59382, 59404, + 59425, 59446, 59467, 59488, 59509, 59530, 59551, 59572, + 59593, 59614, 59635, 59656, 59677, 59697, 59718, 59739, + 59759, 59780, 59801, 59821, 59842, 59862, 59883, 59903, + 59923, 59944, 59964, 59984, 60004, 60025, 60045, 60065, + 60085, 60105, 60125, 60145, 60165, 60185, 60205, 60225, + 60244, 60264, 60284, 60304, 60323, 60343, 60363, 60382, + 60402, 60421, 60441, 60460, 60479, 60499, 60518, 60537, + 60556, 60576, 60595, 60614, 60633, 60652, 60671, 60690, + 60709, 60728, 60747, 60766, 60785, 60803, 60822, 60841, + 60859, 60878, 60897, 60915, 60934, 60952, 60971, 60989, + 61007, 61026, 61044, 61062, 61081, 61099, 61117, 61135, + 61153, 61171, 61189, 61207, 61225, 61243, 61261, 61279, + 61297, 61314, 61332, 61350, 61367, 61385, 61403, 61420, + 61438, 61455, 61473, 61490, 61507, 61525, 61542, 61559, + 61577, 61594, 61611, 61628, 61645, 61662, 61679, 61696, + 61713, 61730, 61747, 61764, 61780, 61797, 61814, 61831, + 61847, 61864, 61880, 61897, 61913, 61930, 61946, 61963, + 61979, 61995, 62012, 62028, 62044, 62060, 62076, 62092, + 62108, 62125, 62141, 62156, 62172, 62188, 62204, 62220, + 62236, 62251, 62267, 62283, 62298, 62314, 62329, 62345, + 62360, 62376, 62391, 62407, 62422, 62437, 62453, 62468, + 62483, 62498, 62513, 62528, 62543, 62558, 62573, 62588, + 62603, 62618, 62633, 62648, 62662, 62677, 62692, 62706, + 62721, 62735, 62750, 62764, 62779, 62793, 62808, 62822, + 62836, 62850, 62865, 62879, 62893, 62907, 62921, 62935, + 62949, 62963, 62977, 62991, 63005, 63019, 63032, 63046, + 63060, 63074, 63087, 63101, 63114, 63128, 63141, 63155, + 63168, 63182, 63195, 63208, 63221, 63235, 63248, 63261, + 63274, 63287, 63300, 63313, 63326, 63339, 63352, 63365, + 63378, 63390, 63403, 63416, 63429, 63441, 63454, 63466, + 63479, 63491, 63504, 63516, 63528, 63541, 63553, 63565, + 63578, 63590, 63602, 63614, 63626, 63638, 63650, 63662, + 63674, 63686, 63698, 63709, 63721, 63733, 63745, 63756, + 63768, 63779, 63791, 63803, 63814, 63825, 63837, 63848, + 63859, 63871, 63882, 63893, 63904, 63915, 63927, 63938, + 63949, 63960, 63971, 63981, 63992, 64003, 64014, 64025, + 64035, 64046, 64057, 64067, 64078, 64088, 64099, 64109, + 64120, 64130, 64140, 64151, 64161, 64171, 64181, 64192, + 64202, 64212, 64222, 64232, 64242, 64252, 64261, 64271, + 64281, 64291, 64301, 64310, 64320, 64330, 64339, 64349, + 64358, 64368, 64377, 64387, 64396, 64405, 64414, 64424, + 64433, 64442, 64451, 64460, 64469, 64478, 64487, 64496, + 64505, 64514, 64523, 64532, 64540, 64549, 64558, 64566, + 64575, 64584, 64592, 64600, 64609, 64617, 64626, 64634, + 64642, 64651, 64659, 64667, 64675, 64683, 64691, 64699, + 64707, 64715, 64723, 64731, 64739, 64747, 64754, 64762, + 64770, 64777, 64785, 64793, 64800, 64808, 64815, 64822, + 64830, 64837, 64844, 64852, 64859, 64866, 64873, 64880, + 64887, 64895, 64902, 64908, 64915, 64922, 64929, 64936, + 64943, 64949, 64956, 64963, 64969, 64976, 64982, 64989, + 64995, 65002, 65008, 65015, 65021, 65027, 65033, 65040, + 65046, 65052, 65058, 65064, 65070, 65076, 65082, 65088, + 65094, 65099, 65105, 65111, 65117, 65122, 65128, 65133, + 65139, 65144, 65150, 65155, 65161, 65166, 65171, 65177, + 65182, 65187, 65192, 65197, 65202, 65207, 65212, 65217, + 65222, 65227, 65232, 65237, 65242, 65246, 65251, 65256, + 65260, 65265, 65270, 65274, 65279, 65283, 65287, 65292, + 65296, 65300, 65305, 65309, 65313, 65317, 65321, 65325, + 65329, 65333, 65337, 65341, 65345, 65349, 65352, 65356, + 65360, 65363, 65367, 65371, 65374, 65378, 65381, 65385, + 65388, 65391, 65395, 65398, 65401, 65404, 65408, 65411, + 65414, 65417, 65420, 65423, 65426, 65429, 65431, 65434, + 65437, 65440, 65442, 65445, 65448, 65450, 65453, 65455, + 65458, 65460, 65463, 65465, 65467, 65470, 65472, 65474, + 65476, 65478, 65480, 65482, 65484, 65486, 65488, 65490, + 65492, 65494, 65496, 65497, 65499, 65501, 65502, 65504, + 65505, 65507, 65508, 65510, 65511, 65513, 65514, 65515, + 65516, 65518, 65519, 65520, 65521, 65522, 65523, 65524, + 65525, 65526, 65527, 65527, 65528, 65529, 65530, 65530, + 65531, 65531, 65532, 65532, 65533, 65533, 65534, 65534, + 65534, 65535, 65535, 65535, 65535, 65535, 65535, 65535 + }; + + private static uint[] tanToAngle = + { + 0, 333772, 667544, 1001315, 1335086, 1668857, 2002626, 2336395, + 2670163, 3003929, 3337694, 3671457, 4005219, 4338979, 4672736, 5006492, + 5340245, 5673995, 6007743, 6341488, 6675230, 7008968, 7342704, 7676435, + 8010164, 8343888, 8677609, 9011325, 9345037, 9678744, 10012447, 10346145, + 10679838, 11013526, 11347209, 11680887, 12014558, 12348225, 12681885, 13015539, + 13349187, 13682829, 14016464, 14350092, 14683714, 15017328, 15350936, 15684536, + 16018129, 16351714, 16685291, 17018860, 17352422, 17685974, 18019518, 18353054, + 18686582, 19020100, 19353610, 19687110, 20020600, 20354080, 20687552, 21021014, + 21354466, 21687906, 22021338, 22354758, 22688168, 23021568, 23354956, 23688332, + 24021698, 24355052, 24688396, 25021726, 25355046, 25688352, 26021648, 26354930, + 26688200, 27021456, 27354702, 27687932, 28021150, 28354356, 28687548, 29020724, + 29353888, 29687038, 30020174, 30353296, 30686404, 31019496, 31352574, 31685636, + 32018684, 32351718, 32684734, 33017736, 33350722, 33683692, 34016648, 34349584, + 34682508, 35015412, 35348300, 35681172, 36014028, 36346868, 36679688, 37012492, + 37345276, 37678044, 38010792, 38343524, 38676240, 39008936, 39341612, 39674272, + 40006912, 40339532, 40672132, 41004716, 41337276, 41669820, 42002344, 42334848, + 42667332, 42999796, 43332236, 43664660, 43997060, 44329444, 44661800, 44994140, + 45326456, 45658752, 45991028, 46323280, 46655512, 46987720, 47319908, 47652072, + 47984212, 48316332, 48648428, 48980500, 49312548, 49644576, 49976580, 50308556, + 50640512, 50972444, 51304352, 51636236, 51968096, 52299928, 52631740, 52963524, + 53295284, 53627020, 53958728, 54290412, 54622068, 54953704, 55285308, 55616888, + 55948444, 56279972, 56611472, 56942948, 57274396, 57605816, 57937212, 58268576, + 58599916, 58931228, 59262512, 59593768, 59924992, 60256192, 60587364, 60918508, + 61249620, 61580704, 61911760, 62242788, 62573788, 62904756, 63235692, 63566604, + 63897480, 64228332, 64559148, 64889940, 65220696, 65551424, 65882120, 66212788, + 66543420, 66874024, 67204600, 67535136, 67865648, 68196120, 68526568, 68856984, + 69187360, 69517712, 69848024, 70178304, 70508560, 70838776, 71168960, 71499112, + 71829224, 72159312, 72489360, 72819376, 73149360, 73479304, 73809216, 74139096, + 74468936, 74798744, 75128520, 75458264, 75787968, 76117632, 76447264, 76776864, + 77106424, 77435952, 77765440, 78094888, 78424304, 78753688, 79083032, 79412336, + 79741608, 80070840, 80400032, 80729192, 81058312, 81387392, 81716432, 82045440, + 82374408, 82703336, 83032224, 83361080, 83689896, 84018664, 84347400, 84676096, + 85004760, 85333376, 85661952, 85990488, 86318984, 86647448, 86975864, 87304240, + 87632576, 87960872, 88289128, 88617344, 88945520, 89273648, 89601736, 89929792, + 90257792, 90585760, 90913688, 91241568, 91569408, 91897200, 92224960, 92552672, + 92880336, 93207968, 93535552, 93863088, 94190584, 94518040, 94845448, 95172816, + 95500136, 95827416, 96154648, 96481832, 96808976, 97136080, 97463136, 97790144, + 98117112, 98444032, 98770904, 99097736, 99424520, 99751256, 100077944, 100404592, + 100731192, 101057744, 101384248, 101710712, 102037128, 102363488, 102689808, 103016080, + 103342312, 103668488, 103994616, 104320696, 104646736, 104972720, 105298656, 105624552, + 105950392, 106276184, 106601928, 106927624, 107253272, 107578872, 107904416, 108229920, + 108555368, 108880768, 109206120, 109531416, 109856664, 110181872, 110507016, 110832120, + 111157168, 111482168, 111807112, 112132008, 112456856, 112781648, 113106392, 113431080, + 113755720, 114080312, 114404848, 114729328, 115053760, 115378136, 115702464, 116026744, + 116350960, 116675128, 116999248, 117323312, 117647320, 117971272, 118295176, 118619024, + 118942816, 119266560, 119590248, 119913880, 120237456, 120560984, 120884456, 121207864, + 121531224, 121854528, 122177784, 122500976, 122824112, 123147200, 123470224, 123793200, + 124116120, 124438976, 124761784, 125084528, 125407224, 125729856, 126052432, 126374960, + 126697424, 127019832, 127342184, 127664472, 127986712, 128308888, 128631008, 128953072, + 129275080, 129597024, 129918912, 130240744, 130562520, 130884232, 131205888, 131527480, + 131849016, 132170496, 132491912, 132813272, 133134576, 133455816, 133776992, 134098120, + 134419184, 134740176, 135061120, 135382000, 135702816, 136023584, 136344272, 136664912, + 136985488, 137306016, 137626464, 137946864, 138267184, 138587456, 138907664, 139227808, + 139547904, 139867920, 140187888, 140507776, 140827616, 141147392, 141467104, 141786752, + 142106336, 142425856, 142745312, 143064720, 143384048, 143703312, 144022512, 144341664, + 144660736, 144979744, 145298704, 145617584, 145936400, 146255168, 146573856, 146892480, + 147211040, 147529536, 147847968, 148166336, 148484640, 148802880, 149121056, 149439152, + 149757200, 150075168, 150393072, 150710912, 151028688, 151346400, 151664048, 151981616, + 152299136, 152616576, 152933952, 153251264, 153568496, 153885680, 154202784, 154519824, + 154836784, 155153696, 155470528, 155787296, 156104000, 156420624, 156737200, 157053696, + 157370112, 157686480, 158002768, 158318976, 158635136, 158951216, 159267232, 159583168, + 159899040, 160214848, 160530592, 160846256, 161161840, 161477376, 161792832, 162108208, + 162423520, 162738768, 163053952, 163369040, 163684080, 163999040, 164313936, 164628752, + 164943504, 165258176, 165572784, 165887312, 166201776, 166516160, 166830480, 167144736, + 167458912, 167773008, 168087040, 168400992, 168714880, 169028688, 169342432, 169656096, + 169969696, 170283216, 170596672, 170910032, 171223344, 171536576, 171849728, 172162800, + 172475808, 172788736, 173101600, 173414384, 173727104, 174039728, 174352288, 174664784, + 174977200, 175289536, 175601792, 175913984, 176226096, 176538144, 176850096, 177161984, + 177473792, 177785536, 178097200, 178408784, 178720288, 179031728, 179343088, 179654368, + 179965568, 180276704, 180587744, 180898720, 181209616, 181520448, 181831184, 182141856, + 182452448, 182762960, 183073408, 183383760, 183694048, 184004240, 184314368, 184624416, + 184934400, 185244288, 185554096, 185863840, 186173504, 186483072, 186792576, 187102000, + 187411344, 187720608, 188029808, 188338912, 188647936, 188956896, 189265760, 189574560, + 189883264, 190191904, 190500448, 190808928, 191117312, 191425632, 191733872, 192042016, + 192350096, 192658096, 192966000, 193273840, 193581584, 193889264, 194196848, 194504352, + 194811792, 195119136, 195426400, 195733584, 196040688, 196347712, 196654656, 196961520, + 197268304, 197574992, 197881616, 198188144, 198494592, 198800960, 199107248, 199413456, + 199719584, 200025616, 200331584, 200637456, 200943248, 201248960, 201554576, 201860128, + 202165584, 202470960, 202776256, 203081456, 203386592, 203691632, 203996592, 204301472, + 204606256, 204910976, 205215600, 205520144, 205824592, 206128960, 206433248, 206737456, + 207041584, 207345616, 207649568, 207953424, 208257216, 208560912, 208864512, 209168048, + 209471488, 209774832, 210078112, 210381296, 210684384, 210987408, 211290336, 211593184, + 211895936, 212198608, 212501184, 212803680, 213106096, 213408432, 213710672, 214012816, + 214314880, 214616864, 214918768, 215220576, 215522288, 215823920, 216125472, 216426928, + 216728304, 217029584, 217330784, 217631904, 217932928, 218233856, 218534704, 218835472, + 219136144, 219436720, 219737216, 220037632, 220337952, 220638192, 220938336, 221238384, + 221538352, 221838240, 222138032, 222437728, 222737344, 223036880, 223336304, 223635664, + 223934912, 224234096, 224533168, 224832160, 225131072, 225429872, 225728608, 226027232, + 226325776, 226624240, 226922608, 227220880, 227519056, 227817152, 228115168, 228413088, + 228710912, 229008640, 229306288, 229603840, 229901312, 230198688, 230495968, 230793152, + 231090256, 231387280, 231684192, 231981024, 232277760, 232574416, 232870960, 233167440, + 233463808, 233760096, 234056288, 234352384, 234648384, 234944304, 235240128, 235535872, + 235831504, 236127056, 236422512, 236717888, 237013152, 237308336, 237603424, 237898416, + 238193328, 238488144, 238782864, 239077488, 239372016, 239666464, 239960816, 240255072, + 240549232, 240843312, 241137280, 241431168, 241724960, 242018656, 242312256, 242605776, + 242899200, 243192512, 243485744, 243778896, 244071936, 244364880, 244657744, 244950496, + 245243168, 245535744, 245828224, 246120608, 246412912, 246705104, 246997216, 247289216, + 247581136, 247872960, 248164688, 248456320, 248747856, 249039296, 249330640, 249621904, + 249913056, 250204128, 250495088, 250785968, 251076736, 251367424, 251658016, 251948512, + 252238912, 252529200, 252819408, 253109520, 253399536, 253689456, 253979280, 254269008, + 254558640, 254848176, 255137632, 255426976, 255716224, 256005376, 256294432, 256583392, + 256872256, 257161024, 257449696, 257738272, 258026752, 258315136, 258603424, 258891600, + 259179696, 259467696, 259755600, 260043392, 260331104, 260618704, 260906224, 261193632, + 261480960, 261768176, 262055296, 262342320, 262629248, 262916080, 263202816, 263489456, + 263776000, 264062432, 264348784, 264635024, 264921168, 265207216, 265493168, 265779024, + 266064784, 266350448, 266636000, 266921472, 267206832, 267492096, 267777264, 268062336, + 268347312, 268632192, 268916960, 269201632, 269486208, 269770688, 270055072, 270339360, + 270623552, 270907616, 271191616, 271475488, 271759296, 272042976, 272326560, 272610048, + 272893440, 273176736, 273459936, 273743040, 274026048, 274308928, 274591744, 274874432, + 275157024, 275439520, 275721920, 276004224, 276286432, 276568512, 276850528, 277132416, + 277414240, 277695936, 277977536, 278259040, 278540448, 278821728, 279102944, 279384032, + 279665056, 279945952, 280226752, 280507456, 280788064, 281068544, 281348960, 281629248, + 281909472, 282189568, 282469568, 282749440, 283029248, 283308960, 283588544, 283868032, + 284147424, 284426720, 284705920, 284985024, 285264000, 285542912, 285821696, 286100384, + 286378976, 286657440, 286935840, 287214112, 287492320, 287770400, 288048384, 288326240, + 288604032, 288881696, 289159264, 289436768, 289714112, 289991392, 290268576, 290545632, + 290822592, 291099456, 291376224, 291652896, 291929440, 292205888, 292482272, 292758528, + 293034656, 293310720, 293586656, 293862496, 294138240, 294413888, 294689440, 294964864, + 295240192, 295515424, 295790560, 296065600, 296340512, 296615360, 296890080, 297164704, + 297439200, 297713632, 297987936, 298262144, 298536256, 298810240, 299084160, 299357952, + 299631648, 299905248, 300178720, 300452128, 300725408, 300998592, 301271680, 301544640, + 301817536, 302090304, 302362976, 302635520, 302908000, 303180352, 303452608, 303724768, + 303996800, 304268768, 304540608, 304812320, 305083968, 305355520, 305626944, 305898272, + 306169472, 306440608, 306711616, 306982528, 307253344, 307524064, 307794656, 308065152, + 308335552, 308605856, 308876032, 309146112, 309416096, 309685984, 309955744, 310225408, + 310494976, 310764448, 311033824, 311303072, 311572224, 311841280, 312110208, 312379040, + 312647776, 312916416, 313184960, 313453376, 313721696, 313989920, 314258016, 314526016, + 314793920, 315061728, 315329408, 315597024, 315864512, 316131872, 316399168, 316666336, + 316933408, 317200384, 317467232, 317733984, 318000640, 318267200, 318533632, 318799968, + 319066208, 319332352, 319598368, 319864288, 320130112, 320395808, 320661408, 320926912, + 321192320, 321457632, 321722816, 321987904, 322252864, 322517760, 322782528, 323047200, + 323311744, 323576192, 323840544, 324104800, 324368928, 324632992, 324896928, 325160736, + 325424448, 325688096, 325951584, 326215008, 326478304, 326741504, 327004608, 327267584, + 327530464, 327793248, 328055904, 328318496, 328580960, 328843296, 329105568, 329367712, + 329629760, 329891680, 330153536, 330415264, 330676864, 330938400, 331199808, 331461120, + 331722304, 331983392, 332244384, 332505280, 332766048, 333026752, 333287296, 333547776, + 333808128, 334068384, 334328544, 334588576, 334848512, 335108352, 335368064, 335627712, + 335887200, 336146624, 336405920, 336665120, 336924224, 337183200, 337442112, 337700864, + 337959552, 338218112, 338476576, 338734944, 338993184, 339251328, 339509376, 339767296, + 340025120, 340282848, 340540480, 340797984, 341055392, 341312704, 341569888, 341826976, + 342083968, 342340832, 342597600, 342854272, 343110848, 343367296, 343623648, 343879904, + 344136032, 344392064, 344648000, 344903808, 345159520, 345415136, 345670656, 345926048, + 346181344, 346436512, 346691616, 346946592, 347201440, 347456224, 347710880, 347965440, + 348219872, 348474208, 348728448, 348982592, 349236608, 349490528, 349744320, 349998048, + 350251648, 350505152, 350758528, 351011808, 351264992, 351518048, 351771040, 352023872, + 352276640, 352529280, 352781824, 353034272, 353286592, 353538816, 353790944, 354042944, + 354294880, 354546656, 354798368, 355049952, 355301440, 355552800, 355804096, 356055264, + 356306304, 356557280, 356808128, 357058848, 357309504, 357560032, 357810464, 358060768, + 358311008, 358561088, 358811104, 359060992, 359310784, 359560480, 359810048, 360059520, + 360308896, 360558144, 360807296, 361056352, 361305312, 361554144, 361802880, 362051488, + 362300032, 362548448, 362796736, 363044960, 363293056, 363541024, 363788928, 364036704, + 364284384, 364531936, 364779392, 365026752, 365274016, 365521152, 365768192, 366015136, + 366261952, 366508672, 366755296, 367001792, 367248192, 367494496, 367740704, 367986784, + 368232768, 368478656, 368724416, 368970080, 369215648, 369461088, 369706432, 369951680, + 370196800, 370441824, 370686752, 370931584, 371176288, 371420896, 371665408, 371909792, + 372154080, 372398272, 372642336, 372886304, 373130176, 373373952, 373617600, 373861152, + 374104608, 374347936, 374591168, 374834304, 375077312, 375320224, 375563040, 375805760, + 376048352, 376290848, 376533248, 376775520, 377017696, 377259776, 377501728, 377743584, + 377985344, 378227008, 378468544, 378709984, 378951328, 379192544, 379433664, 379674688, + 379915584, 380156416, 380397088, 380637696, 380878176, 381118560, 381358848, 381599040, + 381839104, 382079072, 382318912, 382558656, 382798304, 383037856, 383277280, 383516640, + 383755840, 383994976, 384233984, 384472896, 384711712, 384950400, 385188992, 385427488, + 385665888, 385904160, 386142336, 386380384, 386618368, 386856224, 387093984, 387331616, + 387569152, 387806592, 388043936, 388281152, 388518272, 388755296, 388992224, 389229024, + 389465728, 389702336, 389938816, 390175200, 390411488, 390647680, 390883744, 391119712, + 391355584, 391591328, 391826976, 392062528, 392297984, 392533312, 392768544, 393003680, + 393238720, 393473632, 393708448, 393943168, 394177760, 394412256, 394646656, 394880960, + 395115136, 395349216, 395583200, 395817088, 396050848, 396284512, 396518080, 396751520, + 396984864, 397218112, 397451264, 397684288, 397917248, 398150080, 398382784, 398615424, + 398847936, 399080320, 399312640, 399544832, 399776928, 400008928, 400240832, 400472608, + 400704288, 400935872, 401167328, 401398720, 401629984, 401861120, 402092192, 402323136, + 402553984, 402784736, 403015360, 403245888, 403476320, 403706656, 403936896, 404167008, + 404397024, 404626944, 404856736, 405086432, 405316032, 405545536, 405774912, 406004224, + 406233408, 406462464, 406691456, 406920320, 407149088, 407377760, 407606336, 407834784, + 408063136, 408291392, 408519520, 408747584, 408975520, 409203360, 409431072, 409658720, + 409886240, 410113664, 410340992, 410568192, 410795296, 411022304, 411249216, 411476032, + 411702720, 411929312, 412155808, 412382176, 412608480, 412834656, 413060736, 413286720, + 413512576, 413738336, 413964000, 414189568, 414415040, 414640384, 414865632, 415090784, + 415315840, 415540800, 415765632, 415990368, 416215008, 416439552, 416663968, 416888288, + 417112512, 417336640, 417560672, 417784576, 418008384, 418232096, 418455712, 418679200, + 418902624, 419125920, 419349120, 419572192, 419795200, 420018080, 420240864, 420463552, + 420686144, 420908608, 421130976, 421353280, 421575424, 421797504, 422019488, 422241344, + 422463104, 422684768, 422906336, 423127776, 423349120, 423570400, 423791520, 424012576, + 424233536, 424454368, 424675104, 424895744, 425116288, 425336736, 425557056, 425777280, + 425997408, 426217440, 426437376, 426657184, 426876928, 427096544, 427316064, 427535488, + 427754784, 427974016, 428193120, 428412128, 428631040, 428849856, 429068544, 429287168, + 429505664, 429724064, 429942368, 430160576, 430378656, 430596672, 430814560, 431032352, + 431250048, 431467616, 431685120, 431902496, 432119808, 432336992, 432554080, 432771040, + 432987936, 433204736, 433421408, 433637984, 433854464, 434070848, 434287104, 434503296, + 434719360, 434935360, 435151232, 435367008, 435582656, 435798240, 436013696, 436229088, + 436444352, 436659520, 436874592, 437089568, 437304416, 437519200, 437733856, 437948416, + 438162880, 438377248, 438591520, 438805696, 439019744, 439233728, 439447584, 439661344, + 439875008, 440088576, 440302048, 440515392, 440728672, 440941824, 441154880, 441367872, + 441580736, 441793472, 442006144, 442218720, 442431168, 442643552, 442855808, 443067968, + 443280032, 443492000, 443703872, 443915648, 444127296, 444338880, 444550336, 444761696, + 444972992, 445184160, 445395232, 445606176, 445817056, 446027840, 446238496, 446449088, + 446659552, 446869920, 447080192, 447290400, 447500448, 447710432, 447920320, 448130112, + 448339776, 448549376, 448758848, 448968224, 449177536, 449386720, 449595808, 449804800, + 450013664, 450222464, 450431168, 450639776, 450848256, 451056640, 451264960, 451473152, + 451681248, 451889248, 452097152, 452304960, 452512672, 452720288, 452927808, 453135232, + 453342528, 453549760, 453756864, 453963904, 454170816, 454377632, 454584384, 454791008, + 454997536, 455203968, 455410304, 455616544, 455822688, 456028704, 456234656, 456440512, + 456646240, 456851904, 457057472, 457262912, 457468256, 457673536, 457878688, 458083744, + 458288736, 458493600, 458698368, 458903040, 459107616, 459312096, 459516480, 459720768, + 459924960, 460129056, 460333056, 460536960, 460740736, 460944448, 461148064, 461351584, + 461554976, 461758304, 461961536, 462164640, 462367680, 462570592, 462773440, 462976160, + 463178816, 463381344, 463583776, 463786144, 463988384, 464190560, 464392608, 464594560, + 464796448, 464998208, 465199872, 465401472, 465602944, 465804320, 466005600, 466206816, + 466407904, 466608896, 466809824, 467010624, 467211328, 467411936, 467612480, 467812896, + 468013216, 468213440, 468413600, 468613632, 468813568, 469013440, 469213184, 469412832, + 469612416, 469811872, 470011232, 470210528, 470409696, 470608800, 470807776, 471006688, + 471205472, 471404192, 471602784, 471801312, 471999712, 472198048, 472396288, 472594400, + 472792448, 472990400, 473188256, 473385984, 473583648, 473781216, 473978688, 474176064, + 474373344, 474570528, 474767616, 474964608, 475161504, 475358336, 475555040, 475751648, + 475948192, 476144608, 476340928, 476537184, 476733312, 476929376, 477125344, 477321184, + 477516960, 477712640, 477908224, 478103712, 478299104, 478494400, 478689600, 478884704, + 479079744, 479274656, 479469504, 479664224, 479858880, 480053408, 480247872, 480442240, + 480636512, 480830656, 481024736, 481218752, 481412640, 481606432, 481800128, 481993760, + 482187264, 482380704, 482574016, 482767264, 482960416, 483153472, 483346432, 483539296, + 483732064, 483924768, 484117344, 484309856, 484502240, 484694560, 484886784, 485078912, + 485270944, 485462880, 485654720, 485846464, 486038144, 486229696, 486421184, 486612576, + 486803840, 486995040, 487186176, 487377184, 487568096, 487758912, 487949664, 488140320, + 488330880, 488521312, 488711712, 488901984, 489092160, 489282240, 489472256, 489662176, + 489851968, 490041696, 490231328, 490420896, 490610336, 490799712, 490988960, 491178144, + 491367232, 491556224, 491745120, 491933920, 492122656, 492311264, 492499808, 492688256, + 492876608, 493064864, 493253056, 493441120, 493629120, 493817024, 494004832, 494192544, + 494380160, 494567712, 494755136, 494942496, 495129760, 495316928, 495504000, 495691008, + 495877888, 496064704, 496251424, 496438048, 496624608, 496811040, 496997408, 497183680, + 497369856, 497555936, 497741920, 497927840, 498113632, 498299360, 498484992, 498670560, + 498856000, 499041376, 499226656, 499411840, 499596928, 499781920, 499966848, 500151680, + 500336416, 500521056, 500705600, 500890080, 501074464, 501258752, 501442944, 501627040, + 501811072, 501995008, 502178848, 502362592, 502546240, 502729824, 502913312, 503096704, + 503280000, 503463232, 503646368, 503829408, 504012352, 504195200, 504377984, 504560672, + 504743264, 504925760, 505108192, 505290496, 505472736, 505654912, 505836960, 506018944, + 506200832, 506382624, 506564320, 506745952, 506927488, 507108928, 507290272, 507471552, + 507652736, 507833824, 508014816, 508195744, 508376576, 508557312, 508737952, 508918528, + 509099008, 509279392, 509459680, 509639904, 509820032, 510000064, 510180000, 510359872, + 510539648, 510719328, 510898944, 511078432, 511257856, 511437216, 511616448, 511795616, + 511974688, 512153664, 512332576, 512511392, 512690112, 512868768, 513047296, 513225792, + 513404160, 513582432, 513760640, 513938784, 514116800, 514294752, 514472608, 514650368, + 514828064, 515005664, 515183168, 515360608, 515537952, 515715200, 515892352, 516069440, + 516246432, 516423328, 516600160, 516776896, 516953536, 517130112, 517306592, 517482976, + 517659264, 517835488, 518011616, 518187680, 518363648, 518539520, 518715296, 518891008, + 519066624, 519242144, 519417600, 519592960, 519768256, 519943424, 520118528, 520293568, + 520468480, 520643328, 520818112, 520992800, 521167392, 521341888, 521516320, 521690656, + 521864896, 522039072, 522213152, 522387168, 522561056, 522734912, 522908640, 523082304, + 523255872, 523429376, 523602784, 523776096, 523949312, 524122464, 524295552, 524468512, + 524641440, 524814240, 524986976, 525159616, 525332192, 525504640, 525677056, 525849344, + 526021568, 526193728, 526365792, 526537760, 526709632, 526881440, 527053152, 527224800, + 527396352, 527567840, 527739200, 527910528, 528081728, 528252864, 528423936, 528594880, + 528765760, 528936576, 529107296, 529277920, 529448480, 529618944, 529789344, 529959648, + 530129856, 530300000, 530470048, 530640000, 530809888, 530979712, 531149440, 531319072, + 531488608, 531658080, 531827488, 531996800, 532166016, 532335168, 532504224, 532673184, + 532842080, 533010912, 533179616, 533348288, 533516832, 533685312, 533853728, 534022048, + 534190272, 534358432, 534526496, 534694496, 534862400, 535030240, 535197984, 535365632, + 535533216, 535700704, 535868128, 536035456, 536202720, 536369888, 536536992, 536704000, + 536870912 + }; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Trig.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Trig.cs new file mode 100644 index 00000000..b0e8abc0 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Math/Trig.cs @@ -0,0 +1,73 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Runtime.CompilerServices; + +namespace ManagedDoom +{ + public static partial class Trig + { + public const int FineAngleCount = 8192; + public const int FineMask = FineAngleCount - 1; + public const int AngleToFineShift = 19; + + private const int fineCosineOffset = FineAngleCount / 4; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Tan(Angle anglePlus90) + { + return new Fixed(fineTangent[anglePlus90.Data >> AngleToFineShift]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Tan(int fineAnglePlus90) + { + return new Fixed(fineTangent[fineAnglePlus90]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Sin(Angle angle) + { + return new Fixed(fineSine[angle.Data >> AngleToFineShift]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Sin(int fineAngle) + { + return new Fixed(fineSine[fineAngle]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Cos(Angle angle) + { + return new Fixed(fineSine[(angle.Data >> AngleToFineShift) + fineCosineOffset]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Cos(int fineAngle) + { + return new Fixed(fineSine[fineAngle + fineCosineOffset]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Angle TanToAngle(uint tan) + { + return new Angle(tanToAngle[tan]); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/DoomMenu.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/DoomMenu.cs new file mode 100644 index 00000000..0dab24fd --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/DoomMenu.cs @@ -0,0 +1,486 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class DoomMenu + { + private DoomApplication app; + private GameOptions options; + + private SelectableMenu main; + private SelectableMenu episodeMenu; + private SelectableMenu skillMenu; + private SelectableMenu optionMenu; + private SelectableMenu volume; + private LoadMenu load; + private SaveMenu save; + private HelpScreen help; + + private PressAnyKey thisIsShareware; + private PressAnyKey saveFailed; + private YesNoConfirm nightmareConfirm; + private YesNoConfirm endGameConfirm; + private QuitConfirm quitConfirm; + + private MenuDef current; + + private bool active; + + private int tics; + + private int selectedEpisode; + + private SaveSlots saveSlots; + + public DoomMenu(DoomApplication app) + { + this.app = app; + options = app.Options; + + thisIsShareware = new PressAnyKey( + this, + DoomInfo.Strings.SWSTRING, + null); + + saveFailed = new PressAnyKey( + this, + DoomInfo.Strings.SAVEDEAD, + null); + + nightmareConfirm = new YesNoConfirm( + this, + DoomInfo.Strings.NIGHTMARE, + () => app.NewGame(GameSkill.Nightmare, selectedEpisode, 1)); + + endGameConfirm = new YesNoConfirm( + this, + DoomInfo.Strings.ENDGAME, + () => app.EndGame()); + + quitConfirm = new QuitConfirm( + this, + app); + + skillMenu = new SelectableMenu( + this, + "M_NEWG", 96, 14, + "M_SKILL", 54, 38, + 2, + + new SimpleMenuItem( + "M_JKILL", 16, 58, 48, 63, + () => app.NewGame(GameSkill.Baby, selectedEpisode, 1), + null), + + new SimpleMenuItem( + "M_ROUGH", 16, 74, 48, 79, + () => app.NewGame(GameSkill.Easy, selectedEpisode, 1), + null), + + new SimpleMenuItem( + "M_HURT", 16, 90, 48, 95, + () => app.NewGame(GameSkill.Medium, selectedEpisode, 1), + null), + + new SimpleMenuItem( + "M_ULTRA", 16, 106, 48, 111, + () => app.NewGame(GameSkill.Hard, selectedEpisode, 1), + null), + + new SimpleMenuItem( + "M_NMARE", 16, 122, 48, 127, + null, + nightmareConfirm)); + + if (app.Options.GameMode == GameMode.Retail) + { + episodeMenu = new SelectableMenu( + this, + "M_EPISOD", 54, 38, + 0, + + new SimpleMenuItem( + "M_EPI1", 16, 58, 48, 63, + () => selectedEpisode = 1, + skillMenu), + + new SimpleMenuItem( + "M_EPI2", 16, 74, 48, 79, + () => selectedEpisode = 2, + skillMenu), + + new SimpleMenuItem( + "M_EPI3", 16, 90, 48, 95, + () => selectedEpisode = 3, + skillMenu), + + new SimpleMenuItem( + "M_EPI4", 16, 106, 48, 111, + () => selectedEpisode = 4, + skillMenu)); + } + else + { + if (app.Options.GameMode == GameMode.Shareware) + { + episodeMenu = new SelectableMenu( + this, + "M_EPISOD", 54, 38, + 0, + + new SimpleMenuItem( + "M_EPI1", 16, 58, 48, 63, + () => selectedEpisode = 1, + skillMenu), + + new SimpleMenuItem( + "M_EPI2", 16, 74, 48, 79, + null, + thisIsShareware), + + new SimpleMenuItem( + "M_EPI3", 16, 90, 48, 95, + null, + thisIsShareware)); + } + else + { + episodeMenu = new SelectableMenu( + this, + "M_EPISOD", 54, 38, + 0, + + new SimpleMenuItem( + "M_EPI1", 16, 58, 48, 63, + () => selectedEpisode = 1, + skillMenu), + new SimpleMenuItem( + "M_EPI2", 16, 74, 48, 79, + () => selectedEpisode = 2, + skillMenu), + new SimpleMenuItem( + "M_EPI3", 16, 90, 48, 95, + () => selectedEpisode = 3, + skillMenu)); + } + } + + var sound = options.Sound; + var music = options.Music; + volume = new SelectableMenu( + this, + "M_SVOL", 60, 38, + 0, + + new SliderMenuItem( + "M_SFXVOL", 48, 59, 80, 64, + sound.MaxVolume + 1, + () => sound.Volume, + vol => sound.Volume = vol), + + new SliderMenuItem("M_MUSVOL", 48, 91, 80, 96, + music.MaxVolume + 1, + () => music.Volume, + vol => music.Volume = vol)); + + var renderer = options.Renderer; + var userInput = options.UserInput; + optionMenu = new SelectableMenu( + this, + "M_OPTTTL", 108, 15, + 0, + + new SimpleMenuItem( + "M_ENDGAM", 28, 32, 60, 37, + null, + endGameConfirm, + () => app.State == ApplicationState.Game), + + new ToggleMenuItem( + "M_MESSG", 28, 48, 60, 53, "M_MSGON", "M_MSGOFF", 180, + () => renderer.DisplayMessage ? 0 : 1, + value => renderer.DisplayMessage = value == 0), + + new SliderMenuItem( + "M_SCRNSZ", 28, 80 - 16, 60, 85 - 16, + renderer.MaxWindowSize + 1, + () => renderer.WindowSize, + size => renderer.WindowSize = size), + + new SliderMenuItem( + "M_MSENS", 28, 112 - 16, 60, 117 - 16, + userInput.MaxMouseSensitivity + 1, + () => userInput.MouseSensitivity, + ms => userInput.MouseSensitivity = ms), + + new SimpleMenuItem( + "M_SVOL", 28, 144 - 16, 60, 149 - 16, + null, + volume)); + + load = new LoadMenu( + this, + "M_LOADG", 72, 28, + 0, + new TextBoxMenuItem(48, 49, 72, 61), + new TextBoxMenuItem(48, 65, 72, 77), + new TextBoxMenuItem(48, 81, 72, 93), + new TextBoxMenuItem(48, 97, 72, 109), + new TextBoxMenuItem(48, 113, 72, 125), + new TextBoxMenuItem(48, 129, 72, 141)); + + save = new SaveMenu( + this, + "M_SAVEG", 72, 28, + 0, + new TextBoxMenuItem(48, 49, 72, 61), + new TextBoxMenuItem(48, 65, 72, 77), + new TextBoxMenuItem(48, 81, 72, 93), + new TextBoxMenuItem(48, 97, 72, 109), + new TextBoxMenuItem(48, 113, 72, 125), + new TextBoxMenuItem(48, 129, 72, 141)); + + help = new HelpScreen(this); + + if (app.Options.GameMode == GameMode.Commercial) + { + main = new SelectableMenu( + this, + "M_DOOM", 94, 2, + 0, + new SimpleMenuItem("M_NGAME", 65, 67, 97, 72, null, skillMenu), + new SimpleMenuItem("M_OPTION", 65, 83, 97, 88, null, optionMenu), + new SimpleMenuItem("M_LOADG", 65, 99, 97, 104, null, load), + new SimpleMenuItem("M_SAVEG", 65, 115, 97, 120, null, save, + () => !(app.State == ApplicationState.Game && + app.Game.State != GameState.Level)), + new SimpleMenuItem("M_QUITG", 65, 131, 97, 136, null, quitConfirm)); + } + else + { + main = new SelectableMenu( + this, + "M_DOOM", 94, 2, + 0, + new SimpleMenuItem("M_NGAME", 65, 59, 97, 64, null, episodeMenu), + new SimpleMenuItem("M_OPTION", 65, 75, 97, 80, null, optionMenu), + new SimpleMenuItem("M_LOADG", 65, 91, 97, 96, null, load), + new SimpleMenuItem("M_SAVEG", 65, 107, 97, 112, null, save, + () => !(app.State == ApplicationState.Game && + app.Game.State != GameState.Level)), + new SimpleMenuItem("M_RDTHIS", 65, 123, 97, 128, null, help), + new SimpleMenuItem("M_QUITG", 65, 139, 97, 144, null, quitConfirm)); + } + + current = main; + active = false; + + tics = 0; + + selectedEpisode = 1; + + saveSlots = new SaveSlots(); + } + + public bool DoEvent(DoomEvent e) + { + if (active) + { + if (current.DoEvent(e)) + { + return true; + } + + if (e.Key == DoomKey.Escape && e.Type == EventType.KeyDown) + { + Close(); + } + + return true; + } + else + { + if (e.Key == DoomKey.Escape && e.Type == EventType.KeyDown) + { + SetCurrent(main); + Open(); + StartSound(Sfx.SWTCHN); + return true; + } + + if (e.Type == EventType.KeyDown && app.State == ApplicationState.Opening) + { + if (e.Key == DoomKey.Enter || + e.Key == DoomKey.Space || + e.Key == DoomKey.LControl || + e.Key == DoomKey.RControl || + e.Key == DoomKey.Escape) + { + SetCurrent(main); + Open(); + StartSound(Sfx.SWTCHN); + return true; + } + } + + return false; + } + } + + public void Update() + { + tics++; + + if (current != null) + { + current.Update(); + } + + if (active && !app.Options.NetGame) + { + app.PauseGame(); + } + } + + public void SetCurrent(MenuDef next) + { + current = next; + current.Open(); + } + + public void Open() + { + active = true; + } + + public void Close() + { + active = false; + + if (!app.Options.NetGame) + { + app.ResumeGame(); + } + } + + public void StartSound(Sfx sfx) + { + options.Sound.StartSound(sfx); + } + + public void NotifySaveFailed() + { + SetCurrent(saveFailed); + } + + public void ShowHelpScreen() + { + SetCurrent(help); + Open(); + StartSound(Sfx.SWTCHN); + } + + public void ShowSaveScreen() + { + SetCurrent(save); + Open(); + StartSound(Sfx.SWTCHN); + } + + public void ShowLoadScreen() + { + SetCurrent(load); + Open(); + StartSound(Sfx.SWTCHN); + } + + public void ShowVolumeControl() + { + SetCurrent(volume); + Open(); + StartSound(Sfx.SWTCHN); + } + + public void QuickSave() + { + if (save.LastSaveSlot == -1) + { + ShowSaveScreen(); + } + else + { + var desc = saveSlots[save.LastSaveSlot]; + var confirm = new YesNoConfirm( + this, + ((string)DoomInfo.Strings.QSPROMPT).Replace("%s", desc), + () => save.DoSave(save.LastSaveSlot)); + SetCurrent(confirm); + Open(); + StartSound(Sfx.SWTCHN); + } + } + + public void QuickLoad() + { + if (save.LastSaveSlot == -1) + { + var pak = new PressAnyKey( + this, + DoomInfo.Strings.QSAVESPOT, + null); + SetCurrent(pak); + Open(); + StartSound(Sfx.SWTCHN); + } + else + { + var desc = saveSlots[save.LastSaveSlot]; + var confirm = new YesNoConfirm( + this, + ((string)DoomInfo.Strings.QLPROMPT).Replace("%s", desc), + () => load.DoLoad(save.LastSaveSlot)); + SetCurrent(confirm); + Open(); + StartSound(Sfx.SWTCHN); + } + } + + public void EndGame() + { + SetCurrent(endGameConfirm); + Open(); + StartSound(Sfx.SWTCHN); + } + + public void Quit() + { + SetCurrent(quitConfirm); + Open(); + StartSound(Sfx.SWTCHN); + } + + public DoomApplication Application => app; + public GameOptions Options => app.Options; + public MenuDef Current => current; + public bool Active => active; + public int Tics => tics; + public SaveSlots SaveSlots => saveSlots; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/HelpScreen.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/HelpScreen.cs new file mode 100644 index 00000000..5e17e7d9 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/HelpScreen.cs @@ -0,0 +1,76 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class HelpScreen : MenuDef + { + private int pageCount; + + private int page; + + public HelpScreen(DoomMenu menu) : base(menu) + { + if (menu.Options.GameMode == GameMode.Shareware) + { + pageCount = 2; + } + else + { + pageCount = 1; + } + } + + public override void Open() + { + page = pageCount - 1; + } + + public override bool DoEvent(DoomEvent e) + { + if (e.Type != EventType.KeyDown) + { + return true; + } + + if (e.Key == DoomKey.Enter || + e.Key == DoomKey.Space || + e.Key == DoomKey.LControl || + e.Key == DoomKey.RControl) + { + page--; + if (page == -1) + { + Menu.Close(); + } + Menu.StartSound(Sfx.PISTOL); + } + + if (e.Key == DoomKey.Escape) + { + Menu.Close(); + Menu.StartSound(Sfx.SWTCHX); + } + + return true; + } + + public int Page => page; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/LoadMenu.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/LoadMenu.cs new file mode 100644 index 00000000..c7e47753 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/LoadMenu.cs @@ -0,0 +1,134 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class LoadMenu : MenuDef + { + private string[] name; + private int[] titleX; + private int[] titleY; + private TextBoxMenuItem[] items; + + private int index; + private TextBoxMenuItem choice; + + public LoadMenu( + DoomMenu menu, + string name, int titleX, int titleY, + int firstChoice, + params TextBoxMenuItem[] items) : base(menu) + { + this.name = new[] { name }; + this.titleX = new[] { titleX }; + this.titleY = new[] { titleY }; + this.items = items; + + index = firstChoice; + choice = items[index]; + } + + public override void Open() + { + for (var i = 0; i < items.Length; i++) + { + items[i].SetText(Menu.SaveSlots[i]); + } + } + + private void Up() + { + index--; + if (index < 0) + { + index = items.Length - 1; + } + + choice = items[index]; + } + + private void Down() + { + index++; + if (index >= items.Length) + { + index = 0; + } + + choice = items[index]; + } + + public override bool DoEvent(DoomEvent e) + { + if (e.Type != EventType.KeyDown) + { + return true; + } + + if (e.Key == DoomKey.Up) + { + Up(); + Menu.StartSound(Sfx.PSTOP); + } + + if (e.Key == DoomKey.Down) + { + Down(); + Menu.StartSound(Sfx.PSTOP); + } + + if (e.Key == DoomKey.Enter) + { + if (DoLoad(index)) + { + Menu.Close(); + } + Menu.StartSound(Sfx.PISTOL); + } + + if (e.Key == DoomKey.Escape) + { + Menu.Close(); + Menu.StartSound(Sfx.SWTCHX); + } + + return true; + } + + public bool DoLoad(int slotNumber) + { + if (Menu.SaveSlots[slotNumber] != null) + { + Menu.Application.LoadGame(slotNumber); + return true; + } + else + { + return false; + } + } + + public IReadOnlyList Name => name; + public IReadOnlyList TitleX => titleX; + public IReadOnlyList TitleY => titleY; + public IReadOnlyList Items => items; + public MenuItem Choice => choice; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/MenuDef.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/MenuDef.cs new file mode 100644 index 00000000..37d2be7a --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/MenuDef.cs @@ -0,0 +1,43 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public abstract class MenuDef + { + private DoomMenu menu; + + public MenuDef(DoomMenu menu) + { + this.menu = menu; + } + + public virtual void Open() + { + } + + public virtual void Update() + { + } + + public abstract bool DoEvent(DoomEvent e); + + public DoomMenu Menu => menu; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/MenuItem.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/MenuItem.cs new file mode 100644 index 00000000..2ad408e8 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/MenuItem.cs @@ -0,0 +1,43 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public abstract class MenuItem + { + private int skullX; + private int skullY; + private MenuDef next; + + private MenuItem() + { + } + + public MenuItem(int skullX, int skullY, MenuDef next) + { + this.skullX = skullX; + this.skullY = skullY; + this.next = next; + } + + public int SkullX => skullX; + public int SkullY => skullY; + public MenuDef Next => next; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/PressAnyKey.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/PressAnyKey.cs new file mode 100644 index 00000000..78f89564 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/PressAnyKey.cs @@ -0,0 +1,54 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class PressAnyKey : MenuDef + { + private string[] text; + private Action action; + + public PressAnyKey(DoomMenu menu, string text, Action action) : base(menu) + { + this.text = text.Split('\n'); + this.action = action; + } + + public override bool DoEvent(DoomEvent e) + { + if (e.Type == EventType.KeyDown) + { + if (action != null) + { + action(); + } + + Menu.Close(); + Menu.StartSound(Sfx.SWTCHX); + + return true; + } + + return true; + } + + public IReadOnlyList Text => text; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/QuitConfirm.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/QuitConfirm.cs new file mode 100644 index 00000000..ab8b1d5a --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/QuitConfirm.cs @@ -0,0 +1,140 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using SFML.System; +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class QuitConfirm : MenuDef + { + private static readonly Sfx[] doomQuitSoundList = new Sfx[] + { + Sfx.PLDETH, + Sfx.DMPAIN, + Sfx.POPAIN, + Sfx.SLOP, + Sfx.TELEPT, + Sfx.POSIT1, + Sfx.POSIT3, + Sfx.SGTATK + }; + + private static readonly Sfx[] doom2QuitSoundList = new Sfx[] + { + Sfx.VILACT, + Sfx.GETPOW, + Sfx.BOSCUB, + Sfx.SLOP, + Sfx.SKESWG, + Sfx.KNTDTH, + Sfx.BSPACT, + Sfx.SGTATK + }; + + private DoomApplication app; + private DoomRandom random; + private string[] text; + + private int endCount; + + public QuitConfirm(DoomMenu menu, DoomApplication app) : base(menu) + { + this.app = app; + random = new DoomRandom(DateTime.Now.Millisecond); + endCount = -1; + } + + public override void Open() + { + IReadOnlyList list; + if (app.Options.GameMode == GameMode.Commercial) + { + if (app.Options.MissionPack == MissionPack.Doom2) + { + list = DoomInfo.QuitMessages.Doom2; + } + else + { + list = DoomInfo.QuitMessages.FinalDoom; + } + } + else + { + list = DoomInfo.QuitMessages.Doom; + } + + text = (list[random.Next() % list.Count] + "\n\n" + DoomInfo.Strings.PRESSYN).Split('\n'); + } + + public override bool DoEvent(DoomEvent e) + { + if (endCount != -1) + { + return true; + } + + if (e.Type != EventType.KeyDown) + { + return true; + } + + if (e.Key == DoomKey.Y || + e.Key == DoomKey.Enter || + e.Key == DoomKey.Space) + { + endCount = 0; + + Sfx sfx; + if (Menu.Options.GameMode == GameMode.Commercial) + { + sfx = doom2QuitSoundList[random.Next() % doom2QuitSoundList.Length]; + } + else + { + sfx = doomQuitSoundList[random.Next() % doomQuitSoundList.Length]; + } + Menu.StartSound(sfx); + } + + if (e.Key == DoomKey.N || + e.Key == DoomKey.Escape) + { + Menu.Close(); + Menu.StartSound(Sfx.SWTCHX); + } + + return true; + } + + public override void Update() + { + if (endCount != -1) + { + endCount++; + } + + if (endCount == 50) + { + app.Quit(); + } + } + + public IReadOnlyList Text => text; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SaveMenu.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SaveMenu.cs new file mode 100644 index 00000000..95b9fd7e --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SaveMenu.cs @@ -0,0 +1,167 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ManagedDoom +{ + public sealed class SaveMenu : MenuDef + { + private string[] name; + private int[] titleX; + private int[] titleY; + private TextBoxMenuItem[] items; + + private int index; + private TextBoxMenuItem choice; + + private TextInput textInput; + + private int lastSaveSlot; + + public SaveMenu( + DoomMenu menu, + string name, int titleX, int titleY, + int firstChoice, + params TextBoxMenuItem[] items) : base(menu) + { + this.name = new[] { name }; + this.titleX = new[] { titleX }; + this.titleY = new[] { titleY }; + this.items = items; + + index = firstChoice; + choice = items[index]; + + lastSaveSlot = -1; + } + + public override void Open() + { + if (Menu.Application.State != ApplicationState.Game || + Menu.Application.Game.State != GameState.Level) + { + Menu.NotifySaveFailed(); + return; + } + + for (var i = 0; i < items.Length; i++) + { + items[i].SetText(Menu.SaveSlots[i]); + } + } + + private void Up() + { + index--; + if (index < 0) + { + index = items.Length - 1; + } + + choice = items[index]; + } + + private void Down() + { + index++; + if (index >= items.Length) + { + index = 0; + } + + choice = items[index]; + } + + public override bool DoEvent(DoomEvent e) + { + if (e.Type != EventType.KeyDown) + { + return true; + } + + if (textInput != null) + { + var result = textInput.DoEvent(e); + + if (textInput.State == TextInputState.Canceled) + { + textInput = null; + } + else if (textInput.State == TextInputState.Finished) + { + textInput = null; + } + + if (result) + { + return true; + } + } + + if (e.Key == DoomKey.Up) + { + Up(); + Menu.StartSound(Sfx.PSTOP); + } + + if (e.Key == DoomKey.Down) + { + Down(); + Menu.StartSound(Sfx.PSTOP); + } + + if (e.Key == DoomKey.Enter) + { + textInput = choice.Edit(() => DoSave(index)); + Menu.StartSound(Sfx.PISTOL); + } + + if (e.Key == DoomKey.Escape) + { + Menu.Close(); + Menu.StartSound(Sfx.SWTCHX); + } + + return true; + } + + public void DoSave(int slotNumber) + { + Menu.SaveSlots[slotNumber] = new string(items[slotNumber].Text.ToArray()); + if (Menu.Application.SaveGame(slotNumber, Menu.SaveSlots[slotNumber])) + { + Menu.Close(); + lastSaveSlot = slotNumber; + } + else + { + Menu.NotifySaveFailed(); + } + Menu.StartSound(Sfx.PISTOL); + } + + public IReadOnlyList Name => name; + public IReadOnlyList TitleX => titleX; + public IReadOnlyList TitleY => titleY; + public IReadOnlyList Items => items; + public MenuItem Choice => choice; + public int LastSaveSlot => lastSaveSlot; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SaveSlots.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SaveSlots.cs new file mode 100644 index 00000000..14b7e3f1 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SaveSlots.cs @@ -0,0 +1,71 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.IO; + +namespace ManagedDoom +{ + public sealed class SaveSlots + { + private static readonly int slotCount = 0; + private static readonly int descriptionSize = 24; + + private string[] slots; + + private void ReadSlots() + { + /*slots = new string[slotCount]; + + var directory = ConfigUtilities.GetExeDirectory(); + var buffer = new byte[descriptionSize]; + for (var i = 0; i < slots.Length; i++) + { + var path = Path.Combine(directory, "doomsav" + i + ".dsg"); + if (File.Exists(path)) + { + using (var reader = new FileStream(path, FileMode.Open, FileAccess.Read)) + { + reader.Read(buffer, 0, buffer.Length); + slots[i] = DoomInterop.ToString(buffer, 0, buffer.Length); + } + } + }*/ + // TODO: implement save slot + } + + public string this[int number] + { + get + { + if (slots == null) + { + ReadSlots(); + } + + return slots[number]; + } + + set + { + slots[number] = value; + } + } + + public int Count => slots.Length; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SelectableMenu.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SelectableMenu.cs new file mode 100644 index 00000000..0e8b182c --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SelectableMenu.cs @@ -0,0 +1,231 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class SelectableMenu : MenuDef + { + private string[] name; + private int[] titleX; + private int[] titleY; + private MenuItem[] items; + + private int index; + private MenuItem choice; + + private TextInput textInput; + + public SelectableMenu( + DoomMenu menu, + string name, int titleX, int titleY, + int firstChoice, + params MenuItem[] items) : base(menu) + { + this.name = new[] { name }; + this.titleX = new[] { titleX }; + this.titleY = new[] { titleY }; + this.items = items; + + index = firstChoice; + choice = items[index]; + } + + public SelectableMenu( + DoomMenu menu, + string name1, int titleX1, int titleY1, + string name2, int titleX2, int titleY2, + int firstChoice, + params MenuItem[] items) : base(menu) + { + this.name = new[] { name1, name2 }; + this.titleX = new[] { titleX1, titleX2 }; + this.titleY = new[] { titleY1, titleY2 }; + this.items = items; + + index = firstChoice; + choice = items[index]; + } + + public override void Open() + { + foreach (var item in items) + { + var toggle = item as ToggleMenuItem; + if (toggle != null) + { + toggle.Reset(); + } + + var slider = item as SliderMenuItem; + if (slider != null) + { + slider.Reset(); + } + } + } + + private void Up() + { + index--; + if (index < 0) + { + index = items.Length - 1; + } + + choice = items[index]; + } + + private void Down() + { + index++; + if (index >= items.Length) + { + index = 0; + } + + choice = items[index]; + } + + public override bool DoEvent(DoomEvent e) + { + if (e.Type != EventType.KeyDown) + { + return true; + } + + if (textInput != null) + { + var result = textInput.DoEvent(e); + + if (textInput.State == TextInputState.Canceled) + { + textInput = null; + } + else if (textInput.State == TextInputState.Finished) + { + textInput = null; + } + + if (result) + { + return true; + } + } + + if (e.Key == DoomKey.Up) + { + Up(); + Menu.StartSound(Sfx.PSTOP); + } + + if (e.Key == DoomKey.Down) + { + Down(); + Menu.StartSound(Sfx.PSTOP); + } + + if (e.Key == DoomKey.Left) + { + var toggle = choice as ToggleMenuItem; + if (toggle != null) + { + toggle.Down(); + Menu.StartSound(Sfx.PISTOL); + } + + var slider = choice as SliderMenuItem; + if (slider != null) + { + slider.Down(); + Menu.StartSound(Sfx.STNMOV); + } + } + + if (e.Key == DoomKey.Right) + { + var toggle = choice as ToggleMenuItem; + if (toggle != null) + { + toggle.Up(); + Menu.StartSound(Sfx.PISTOL); + } + + var slider = choice as SliderMenuItem; + if (slider != null) + { + slider.Up(); + Menu.StartSound(Sfx.STNMOV); + } + } + + if (e.Key == DoomKey.Enter) + { + var toggle = choice as ToggleMenuItem; + if (toggle != null) + { + toggle.Up(); + Menu.StartSound(Sfx.PISTOL); + } + + var simple = choice as SimpleMenuItem; + if (simple != null) + { + if (simple.Selectable) + { + if (simple.Action != null) + { + simple.Action(); + } + if (simple.Next != null) + { + Menu.SetCurrent(simple.Next); + } + else + { + Menu.Close(); + } + } + Menu.StartSound(Sfx.PISTOL); + return true; + } + + if (choice.Next != null) + { + Menu.SetCurrent(choice.Next); + Menu.StartSound(Sfx.PISTOL); + } + } + + if (e.Key == DoomKey.Escape) + { + Menu.Close(); + Menu.StartSound(Sfx.SWTCHX); + } + + return true; + } + + public IReadOnlyList Name => name; + public IReadOnlyList TitleX => titleX; + public IReadOnlyList TitleY => titleY; + public IReadOnlyList Items => items; + public MenuItem Choice => choice; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SimpleMenuItem.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SimpleMenuItem.cs new file mode 100644 index 00000000..cb4ea259 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SimpleMenuItem.cs @@ -0,0 +1,78 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public class SimpleMenuItem : MenuItem + { + private string name; + private int itemX; + private int itemY; + private Action action; + private Func selectable; + + public SimpleMenuItem( + string name, + int skullX, int skullY, + int itemX, int itemY, + Action action, MenuDef next) + : base(skullX, skullY, next) + { + this.name = name; + this.itemX = itemX; + this.itemY = itemY; + this.action = action; + this.selectable = null; + } + + public SimpleMenuItem( + string name, + int skullX, int skullY, + int itemX, int itemY, + Action action, MenuDef next, Func selectable) + : base(skullX, skullY, next) + { + this.name = name; + this.itemX = itemX; + this.itemY = itemY; + this.action = action; + this.selectable = selectable; + } + + public string Name => name; + public int ItemX => itemX; + public int ItemY => itemY; + public Action Action => action; + + public bool Selectable + { + get + { + if (selectable == null) + { + return true; + } + else + { + return selectable(); + } + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SliderMenuItem.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SliderMenuItem.cs new file mode 100644 index 00000000..5f8696c8 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/SliderMenuItem.cs @@ -0,0 +1,97 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public class SliderMenuItem : MenuItem + { + private string name; + private int itemX; + private int itemY; + + private int sliderLength; + private int sliderPosition; + + private Func reset; + private Action action; + + public SliderMenuItem( + string name, + int skullX, int skullY, + int itemX, int itemY, + int sliderLength, + Func reset, + Action action) + : base(skullX, skullY, null) + { + this.name = name; + this.itemX = itemX; + this.itemY = itemY; + + this.sliderLength = sliderLength; + sliderPosition = 0; + + this.action = action; + this.reset = reset; + } + + public void Reset() + { + if (reset != null) + { + sliderPosition = reset(); + } + } + + public void Up() + { + if (sliderPosition < SliderLength - 1) + { + sliderPosition++; + } + + if (action != null) + { + action(sliderPosition); + } + } + + public void Down() + { + if (sliderPosition > 0) + { + sliderPosition--; + } + + if (action != null) + { + action(sliderPosition); + } + } + + public string Name => name; + public int ItemX => itemX; + public int ItemY => itemY; + + public int SliderX => itemX; + public int SliderY => itemY + 16; + public int SliderLength => sliderLength; + public int SliderPosition => sliderPosition; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/TextBoxMenuItem.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/TextBoxMenuItem.cs new file mode 100644 index 00000000..62e16487 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/TextBoxMenuItem.cs @@ -0,0 +1,76 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public class TextBoxMenuItem : MenuItem + { + private int itemX; + private int itemY; + + private IReadOnlyList text; + private TextInput edit; + + public TextBoxMenuItem(int skullX, int skullY, int itemX, int itemY) + : base(skullX, skullY, null) + { + this.itemX = itemX; + this.itemY = itemY; + } + + public TextInput Edit(Action finished) + { + edit = new TextInput( + text != null ? text : new char[0], + cs => { }, + cs => { text = cs; edit = null; finished(); }, + () => { edit = null; }); + + return edit; + } + + public void SetText(string text) + { + if (text != null) + { + this.text = text.ToCharArray(); + } + } + + public IReadOnlyList Text + { + get + { + if (edit == null) + { + return text; + } + else + { + return edit.Text; + } + } + } + + public int ItemX => itemX; + public int ItemY => itemY; + public bool Editing => edit != null; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/TextInput.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/TextInput.cs new file mode 100644 index 00000000..9bd3c8ba --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/TextInput.cs @@ -0,0 +1,87 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ManagedDoom +{ + public sealed class TextInput + { + private List text; + private Action> typed; + private Action> finished; + private Action canceled; + + private TextInputState state; + + public TextInput( + IReadOnlyList initialText, + Action> typed, + Action> finished, + Action canceled) + { + this.text = initialText.ToList(); + this.typed = typed; + this.finished = finished; + this.canceled = canceled; + + state = TextInputState.Typing; + } + + public bool DoEvent(DoomEvent e) + { + var ch = e.Key.GetChar(); + if (ch != 0) + { + text.Add(ch); + typed(text); + return true; + } + + if (e.Key == DoomKey.Backspace && e.Type == EventType.KeyDown) + { + if (text.Count > 0) + { + text.RemoveAt(text.Count - 1); + } + typed(text); + return true; + } + + if (e.Key == DoomKey.Enter && e.Type == EventType.KeyDown) + { + finished(text); + state = TextInputState.Finished; + return true; + } + + if (e.Key == DoomKey.Escape && e.Type == EventType.KeyDown) + { + canceled(); + state = TextInputState.Canceled; + return true; + } + + return true; + } + + public IReadOnlyList Text => text; + public TextInputState State => state; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/TextInputState.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/TextInputState.cs new file mode 100644 index 00000000..9967daff --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/TextInputState.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum TextInputState + { + Typing, + Finished, + Canceled + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/ToggleMenuItem.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/ToggleMenuItem.cs new file mode 100644 index 00000000..a57501bf --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/ToggleMenuItem.cs @@ -0,0 +1,102 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public class ToggleMenuItem : MenuItem + { + private string name; + private int itemX; + private int itemY; + + private string[] states; + private int stateX; + + private int stateNumber; + + private Func reset; + private Action action; + + public ToggleMenuItem( + string name, + int skullX, int skullY, + int itemX, int itemY, + string state1, string state2, + int stateX, + Func reset, + Action action) + : base(skullX, skullY, null) + { + this.name = name; + this.itemX = itemX; + this.itemY = itemY; + + this.states = new[] { state1, state2 }; + this.stateX = stateX; + + stateNumber = 0; + + this.action = action; + this.reset = reset; + } + + public void Reset() + { + if (reset != null) + { + stateNumber = reset(); + } + } + + public void Up() + { + stateNumber++; + if (stateNumber == states.Length) + { + stateNumber = 0; + } + + if (action != null) + { + action(stateNumber); + } + } + + public void Down() + { + stateNumber--; + if (stateNumber == -1) + { + stateNumber = states.Length - 1; + } + + if (action != null) + { + action(stateNumber); + } + } + + public string Name => name; + public int ItemX => itemX; + public int ItemY => itemY; + + public string State => states[stateNumber]; + public int StateX => stateX; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/YesNoConfirm.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/YesNoConfirm.cs new file mode 100644 index 00000000..adc35aea --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Menu/YesNoConfirm.cs @@ -0,0 +1,62 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class YesNoConfirm : MenuDef + { + private string[] text; + private Action action; + + public YesNoConfirm(DoomMenu menu, string text, Action action) : base(menu) + { + this.text = text.Split('\n'); + this.action = action; + } + + public override bool DoEvent(DoomEvent e) + { + if (e.Type != EventType.KeyDown) + { + return true; + } + + if (e.Key == DoomKey.Y || + e.Key == DoomKey.Enter || + e.Key == DoomKey.Space) + { + action(); + Menu.Close(); + Menu.StartSound(Sfx.PISTOL); + } + + if (e.Key == DoomKey.N || + e.Key == DoomKey.Escape) + { + Menu.Close(); + Menu.StartSound(Sfx.SWTCHX); + } + + return true; + } + + public IReadOnlyList Text => text; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/OpeningSequence.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/OpeningSequence.cs new file mode 100644 index 00000000..26bcbf8c --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/OpeningSequence.cs @@ -0,0 +1,262 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class OpeningSequence + { + private CommonResource resource; + private GameOptions options; + + private OpeningSequenceState state; + + private int currentStage; + private int nextStage; + + private int count; + private int timer; + + private TicCmd[] cmds; + private Demo demo; + private DoomGame game; + + private bool reset; + + public OpeningSequence(CommonResource resource, GameOptions options) + { + this.resource = resource; + this.options = options; + + cmds = new TicCmd[Player.MaxPlayerCount]; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + cmds[i] = new TicCmd(); + } + + currentStage = 0; + nextStage = 0; + + reset = false; + + StartTitleScreen(); + } + + public void Reset() + { + currentStage = 0; + nextStage = 0; + + demo = null; + game = null; + + reset = true; + + StartTitleScreen(); + } + + public UpdateResult Update() + { + var updateResult = UpdateResult.None; + + if (nextStage != currentStage) + { + switch (nextStage) + { + case 0: + StartTitleScreen(); + break; + case 1: + StartDemo("DEMO1"); + break; + case 2: + StartCreditScreen(); + break; + case 3: + StartDemo("DEMO2"); + break; + case 4: + StartTitleScreen(); + break; + case 5: + StartDemo("DEMO3"); + break; + case 6: + StartCreditScreen(); + break; + case 7: + StartDemo("DEMO4"); + break; + } + + currentStage = nextStage; + updateResult = UpdateResult.NeedWipe; + } + + switch (currentStage) + { + case 0: + count++; + if (count == timer) + { + nextStage = 1; + } + break; + + case 1: + if (!demo.ReadCmd(cmds)) + { + nextStage = 2; + } + else + { + game.Update(cmds); + } + break; + + case 2: + count++; + if (count == timer) + { + nextStage = 3; + } + break; + + case 3: + if (!demo.ReadCmd(cmds)) + { + nextStage = 4; + } + else + { + game.Update(cmds); + } + break; + + case 4: + count++; + if (count == timer) + { + nextStage = 5; + } + break; + + case 5: + if (!demo.ReadCmd(cmds)) + { + if (resource.Wad.GetLumpNumber("DEMO4") == -1) + { + nextStage = 0; + } + else + { + nextStage = 6; + } + } + else + { + game.Update(cmds); + } + break; + + case 6: + count++; + if (count == timer) + { + nextStage = 7; + } + break; + + case 7: + if (!demo.ReadCmd(cmds)) + { + nextStage = 0; + } + else + { + game.Update(cmds); + } + break; + } + + if (state == OpeningSequenceState.Title && count == 1) + { + if (options.GameMode == GameMode.Commercial) + { + options.Music.StartMusic(Bgm.DM2TTL, false); + } + else + { + options.Music.StartMusic(Bgm.INTRO, false); + } + } + + if (reset) + { + reset = false; + return UpdateResult.NeedWipe; + } + else + { + return updateResult; + } + } + + private void StartTitleScreen() + { + state = OpeningSequenceState.Title; + + count = 0; + if (options.GameMode == GameMode.Commercial) + { + timer = 35 * 11; + } + else + { + timer = 170; + } + } + + private void StartCreditScreen() + { + state = OpeningSequenceState.Credit; + + count = 0; + timer = 200; + } + + private void StartDemo(string lump) + { + state = OpeningSequenceState.Demo; + + demo = new Demo(resource.Wad.ReadLump(lump)); + demo.Options.GameVersion = options.GameVersion; + demo.Options.GameMode = options.GameMode; + demo.Options.MissionPack = options.MissionPack; + demo.Options.Renderer = options.Renderer; + demo.Options.Sound = options.Sound; + demo.Options.Music = options.Music; + + game = new DoomGame(resource, demo.Options); + game.DeferedInitNew(); + } + + public OpeningSequenceState State => state; + public DoomGame DemoGame => game; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/OpeningSequenceState.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/OpeningSequenceState.cs new file mode 100644 index 00000000..60fa4818 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/OpeningSequenceState.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum OpeningSequenceState + { + Title, + Credit, + Demo + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Wad/LumpInfo.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Wad/LumpInfo.cs new file mode 100644 index 00000000..d9051986 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Wad/LumpInfo.cs @@ -0,0 +1,45 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.IO; + +namespace ManagedDoom +{ + public sealed class LumpInfo + { + public const int DataSize = 16; + + private string name; + private Stream stream; + private int position; + private int size; + + public LumpInfo(string name, Stream stream, int position, int size) + { + this.name = name; + this.stream = stream; + this.position = position; + this.size = size; + } + + public string Name => name; + public Stream Stream => stream; + public int Position => position; + public int Size => size; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Wad/Wad.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Wad/Wad.cs new file mode 100644 index 00000000..87af6215 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/Wad/Wad.cs @@ -0,0 +1,237 @@ +using System.Threading.Tasks; +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.ExceptionServices; + +namespace ManagedDoom +{ + public sealed class Wad : IDisposable + { + private List names; + private List streams; + private List lumpInfos; + private GameVersion gameVersion; + private GameMode gameMode; + private MissionPack missionPack; + + public Wad(string[] fileNames) + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Open wad files: "); + + names = new List(); + streams = new List(); + lumpInfos = new List(); + + foreach (var fileName in fileNames) + { + AddFile(fileName); + } + + gameMode = GetGameMode(names); + missionPack = GetMissionPack(names); + gameVersion = GetGameVersion(names); + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + Dispose(); + ExceptionDispatchInfo.Throw(e); + } + } + + private void AddFile(string fileName) + { + names.Add(Path.GetFileNameWithoutExtension(fileName).ToLower()); + + //var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read); + var stream = DoomApplication.WadStream; + streams.Add(stream); + + string identification; + int lumpCount; + int lumpInfoTableOffset; + { + var data = new byte[12]; + if (stream.Read(data, 0, data.Length) != data.Length) + { + throw new Exception("Failed to read the WAD file."); + } + + identification = DoomInterop.ToString(data, 0, 4); + lumpCount = BitConverter.ToInt32(data, 4); + lumpInfoTableOffset = BitConverter.ToInt32(data, 8); + if (identification != "IWAD" && identification != "PWAD") + { + throw new Exception("The file is not a WAD file."); + } + } + + { + var data = new byte[LumpInfo.DataSize * lumpCount]; + stream.Seek(lumpInfoTableOffset, SeekOrigin.Begin); + if (stream.Read(data, 0, data.Length) != data.Length) + { + throw new Exception("Failed to read the WAD file."); + } + + for (var i = 0; i < lumpCount; i++) + { + var offset = LumpInfo.DataSize * i; + var lumpInfo = new LumpInfo( + DoomInterop.ToString(data, offset + 8, 8), + stream, + BitConverter.ToInt32(data, offset), + BitConverter.ToInt32(data, offset + 4)); + lumpInfos.Add(lumpInfo); + } + } + } + + public int GetLumpNumber(string name) + { + for (var i = lumpInfos.Count - 1; i >= 0; i--) + { + if (lumpInfos[i].Name == name) + { + return i; + } + } + + return -1; + } + + public int GetLumpSize(int number) + { + return lumpInfos[number].Size; + } + + public byte[] ReadLump(int number) + { + var lumpInfo = lumpInfos[number]; + + var data = new byte[lumpInfo.Size]; + + lumpInfo.Stream.Seek(lumpInfo.Position, SeekOrigin.Begin); + var read = lumpInfo.Stream.Read(data, 0, lumpInfo.Size); + if (read != lumpInfo.Size) + { + throw new Exception("Failed to read the lump " + number + "."); + } + + return data; + } + + public byte[] ReadLump(string name) + { + var lumpNumber = GetLumpNumber(name); + + if (lumpNumber == -1) + { + throw new Exception("The lump '" + name + "' was not found."); + } + + return ReadLump(lumpNumber); + } + + public void Dispose() + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Close wad files."); + + foreach (var stream in streams) + { + stream.Dispose(); + } + + streams.Clear(); + } + + private static GameVersion GetGameVersion(IReadOnlyList names) + { + foreach (var name in names) + { + switch (name.ToLower()) + { + case "doom2": + case "freedoom2": + return GameVersion.Version109; + case "doom": + case "doom1": + case "freedoom1": + return GameVersion.Ultimate; + case "plutonia": + case "tnt": + return GameVersion.Final; + } + } + + return GameVersion.Version109; + } + + private static GameMode GetGameMode(IReadOnlyList names) + { + foreach (var name in names) + { + switch (name.ToLower()) + { + case "doom2": + case "plutonia": + case "tnt": + case "freedoom2": + return GameMode.Commercial; + case "doom": + case "freedoom1": + return GameMode.Retail; + case "doom1": + return GameMode.Shareware; + } + } + + return GameMode.Indetermined; + } + + private static MissionPack GetMissionPack(IReadOnlyList names) + { + foreach (var name in names) + { + switch (name.ToLower()) + { + case "plutonia": + return MissionPack.Plutonia; + case "tnt": + return MissionPack.Tnt; + } + } + + return MissionPack.Doom2; + } + + public IReadOnlyList Names => names; + public IReadOnlyList LumpInfos => lumpInfos; + public GameVersion GameVersion => gameVersion; + public GameMode GameMode => gameMode; + public MissionPack MissionPack => missionPack; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/AmmoType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/AmmoType.cs new file mode 100644 index 00000000..9135673d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/AmmoType.cs @@ -0,0 +1,41 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum AmmoType + { + // Pistol / chaingun ammo. + Clip, + + // Shotgun / double barreled shotgun. + Shell, + + // Plasma rifle, BFG. + Cell, + + // Missile launcher. + Missile, + + Count, + + // Unlimited for chainsaw / fist. + NoAmmo + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/AutoMap.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/AutoMap.cs new file mode 100644 index 00000000..6045f819 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/AutoMap.cs @@ -0,0 +1,338 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class AutoMap + { + private World world; + + private Fixed minX; + private Fixed maxX; + private Fixed minY; + private Fixed maxY; + + private Fixed viewX; + private Fixed viewY; + + private bool visible; + private AutoMapState state; + + private Fixed zoom; + private bool follow; + + private bool zoomIn; + private bool zoomOut; + + private bool left; + private bool right; + private bool up; + private bool down; + + private List marks; + private int nextMarkNumber; + + public AutoMap(World world) + { + this.world = world; + + minX = Fixed.MaxValue; + maxX = Fixed.MinValue; + minY = Fixed.MaxValue; + maxY = Fixed.MinValue; + foreach (var vertex in world.Map.Vertices) + { + if (vertex.X < minX) + { + minX = vertex.X; + } + + if (vertex.X > maxX) + { + maxX = vertex.X; + } + + if (vertex.Y < minY) + { + minY = vertex.Y; + } + + if (vertex.Y > maxY) + { + maxY = vertex.Y; + } + } + + viewX = minX + (maxX - minX) / 2; + viewY = minY + (maxY - minY) / 2; + + visible = false; + state = AutoMapState.None; + + zoom = Fixed.One; + follow = true; + + zoomIn = false; + zoomOut = false; + left = false; + right = false; + up = false; + down = false; + + marks = new List(); + nextMarkNumber = 0; + } + + public void Update() + { + if (zoomIn) + { + zoom += zoom / 16; + } + + if (zoomOut) + { + zoom -= zoom / 16; + } + + if (zoom < Fixed.One / 2) + { + zoom = Fixed.One / 2; + } + else if (zoom > Fixed.One * 32) + { + zoom = Fixed.One * 32; + } + + if (left) + { + viewX -= 64 / zoom; + } + + if (right) + { + viewX += 64 / zoom; + } + + if (up) + { + viewY += 64 / zoom; + } + + if (down) + { + viewY -= 64 / zoom; + } + + if (viewX < minX) + { + viewX = minX; + } + else if (viewX > maxX) + { + viewX = maxX; + } + + if (viewY < minY) + { + viewY = minY; + } + else if (viewY > maxY) + { + viewY = maxY; + } + + if (follow) + { + var player = world.ConsolePlayer.Mobj; + viewX = player.X; + viewY = player.Y; + } + } + + public bool DoEvent(DoomEvent e) + { + if (e.Key == DoomKey.Add || e.Key == DoomKey.Quote) + { + if (e.Type == EventType.KeyDown) + { + zoomIn = true; + } + else if (e.Type == EventType.KeyUp) + { + zoomIn = false; + } + + return true; + } + else if (e.Key == DoomKey.Subtract || e.Key == DoomKey.Hyphen) + { + if (e.Type == EventType.KeyDown) + { + zoomOut = true; + } + else if (e.Type == EventType.KeyUp) + { + zoomOut = false; + } + + return true; + } + else if (e.Key == DoomKey.Left) + { + if (e.Type == EventType.KeyDown) + { + left = true; + } + else if (e.Type == EventType.KeyUp) + { + left = false; + } + + return true; + } + else if (e.Key == DoomKey.Right) + { + if (e.Type == EventType.KeyDown) + { + right = true; + } + else if (e.Type == EventType.KeyUp) + { + right = false; + } + + return true; + } + else if (e.Key == DoomKey.Up) + { + if (e.Type == EventType.KeyDown) + { + up = true; + } + else if (e.Type == EventType.KeyUp) + { + up = false; + } + + return true; + } + else if (e.Key == DoomKey.Down) + { + if (e.Type == EventType.KeyDown) + { + down = true; + } + else if (e.Type == EventType.KeyUp) + { + down = false; + } + + return true; + } + else if (e.Key == DoomKey.F) + { + if (e.Type == EventType.KeyDown) + { + follow = !follow; + if (follow) + { + world.ConsolePlayer.SendMessage(DoomInfo.Strings.AMSTR_FOLLOWON); + } + else + { + world.ConsolePlayer.SendMessage(DoomInfo.Strings.AMSTR_FOLLOWOFF); + } + return true; + } + } + else if (e.Key == DoomKey.M) + { + if (e.Type == EventType.KeyDown) + { + if (marks.Count < 10) + { + marks.Add(new Vertex(viewX, viewY)); + } + else + { + marks[nextMarkNumber] = new Vertex(viewX, viewY); + } + nextMarkNumber++; + if (nextMarkNumber == 10) + { + nextMarkNumber = 0; + } + world.ConsolePlayer.SendMessage(DoomInfo.Strings.AMSTR_MARKEDSPOT); + return true; + } + } + else if (e.Key == DoomKey.C) + { + if (e.Type == EventType.KeyDown) + { + marks.Clear(); + nextMarkNumber = 0; + world.ConsolePlayer.SendMessage(DoomInfo.Strings.AMSTR_MARKSCLEARED); + return true; + } + } + + return false; + } + + public void Open() + { + visible = true; + } + + public void Close() + { + visible = false; + zoomIn = false; + zoomOut = false; + left = false; + right = false; + up = false; + down = false; + } + + public void ToggleCheat() + { + state++; + if ((int)state == 3) + { + state = AutoMapState.None; + } + } + + public Fixed MinX => minX; + public Fixed MaxX => maxX; + public Fixed MinY => minY; + public Fixed MaxY => maxY; + public Fixed ViewX => viewX; + public Fixed ViewY => viewY; + public Fixed Zoom => zoom; + public bool Follow => follow; + public bool Visible => visible; + public AutoMapState State => state; + public IReadOnlyList Marks => marks; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/AutoMapState.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/AutoMapState.cs new file mode 100644 index 00000000..540b9253 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/AutoMapState.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum AutoMapState + { + None, + AllMap, + AllThings + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Box.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Box.cs new file mode 100644 index 00000000..8ddfbb14 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Box.cs @@ -0,0 +1,56 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static class Box + { + public const int Top = 0; + public const int Bottom = 1; + public const int Left = 2; + public const int Right = 3; + + public static void Clear(Fixed[] box) + { + box[Top] = box[Right] = Fixed.MinValue; + box[Bottom] = box[Left] = Fixed.MaxValue; + } + + public static void AddPoint(Fixed[] box, Fixed x, Fixed y) + { + if (x < box[Left]) + { + box[Left] = x; + } + else if (x > box[Right]) + { + box[Right] = x; + } + + if (y < box[Bottom]) + { + box[Bottom] = y; + } + else if (y > box[Top]) + { + box[Top] = y; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/BoxEx.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/BoxEx.cs new file mode 100644 index 00000000..d1fe325d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/BoxEx.cs @@ -0,0 +1,73 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Runtime.CompilerServices; + +namespace ManagedDoom +{ + public static class BoxEx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Top(this Fixed[] box) + { + return box[Box.Top]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Bottom(this Fixed[] box) + { + return box[Box.Bottom]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Left(this Fixed[] box) + { + return box[Box.Left]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fixed Right(this Fixed[] box) + { + return box[Box.Right]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Top(this int[] box) + { + return box[Box.Top]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Bottom(this int[] box) + { + return box[Box.Bottom]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Left(this int[] box) + { + return box[Box.Left]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Right(this int[] box) + { + return box[Box.Right]; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Button.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Button.cs new file mode 100644 index 00000000..b2d1a56b --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Button.cs @@ -0,0 +1,69 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Button + { + private LineDef line; + private ButtonPosition position; + private int texture; + private int timer; + private Mobj soundOrigin; + + public void Clear() + { + line = null; + position = 0; + texture = 0; + timer = 0; + soundOrigin = null; + } + + public LineDef Line + { + get => line; + set => line = value; + } + + public ButtonPosition Position + { + get => position; + set => position = value; + } + + public int Texture + { + get => texture; + set => texture = value; + } + + public int Timer + { + get => timer; + set => timer = value; + } + + public Mobj SoundOrigin + { + get => soundOrigin; + set => soundOrigin = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ButtonPosition.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ButtonPosition.cs new file mode 100644 index 00000000..33b6f315 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ButtonPosition.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum ButtonPosition + { + Top, + Middle, + Bottom + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CardType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CardType.cs new file mode 100644 index 00000000..48375675 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CardType.cs @@ -0,0 +1,33 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum CardType + { + BlueCard, + YellowCard, + RedCard, + BlueSkull, + YellowSkull, + RedSkull, + + Count + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CeilingMove.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CeilingMove.cs new file mode 100644 index 00000000..55375bc0 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CeilingMove.cs @@ -0,0 +1,231 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class CeilingMove : Thinker + { + private World world; + + private CeilingMoveType type; + private Sector sector; + private Fixed bottomHeight; + private Fixed topHeight; + private Fixed speed; + private bool crush; + + // 1 = up, 0 = waiting, -1 = down. + private int direction; + + // Corresponding sector tag. + private int tag; + + private int oldDirection; + + public CeilingMove(World world) + { + this.world = world; + } + + public override void Run() + { + SectorActionResult result; + + var sa = world.SectorAction; + + switch (direction) + { + case 0: + // In statis. + break; + + case 1: + // Up. + result = sa.MovePlane( + sector, + speed, + topHeight, + false, + 1, + direction); + + if ((world.LevelTime & 7) == 0) + { + switch (type) + { + case CeilingMoveType.SilentCrushAndRaise: + break; + + default: + world.StartSound(sector.SoundOrigin, Sfx.STNMOV, SfxType.Misc); + break; + } + } + + if (result == SectorActionResult.PastDestination) + { + switch (type) + { + case CeilingMoveType.RaiseToHighest: + sa.RemoveActiveCeiling(this); + break; + + case CeilingMoveType.SilentCrushAndRaise: + case CeilingMoveType.FastCrushAndRaise: + case CeilingMoveType.CrushAndRaise: + if (type == CeilingMoveType.SilentCrushAndRaise) + { + world.StartSound(sector.SoundOrigin, Sfx.PSTOP, SfxType.Misc); + } + direction = -1; + break; + + default: + break; + } + + } + break; + + case -1: + // Down. + result = sa.MovePlane( + sector, + speed, + bottomHeight, + crush, + 1, + direction); + + if ((world.LevelTime & 7) == 0) + { + switch (type) + { + case CeilingMoveType.SilentCrushAndRaise: + break; + + default: + world.StartSound(sector.SoundOrigin, Sfx.STNMOV, SfxType.Misc); + break; + } + } + + if (result == SectorActionResult.PastDestination) + { + switch (type) + { + case CeilingMoveType.SilentCrushAndRaise: + case CeilingMoveType.CrushAndRaise: + case CeilingMoveType.FastCrushAndRaise: + if (type == CeilingMoveType.SilentCrushAndRaise) + { + world.StartSound(sector.SoundOrigin, Sfx.PSTOP, SfxType.Misc); + } + if (type == CeilingMoveType.CrushAndRaise) + { + speed = SectorAction.CeilingSpeed; + } + direction = 1; + break; + + case CeilingMoveType.LowerAndCrush: + case CeilingMoveType.LowerToFloor: + sa.RemoveActiveCeiling(this); + break; + + default: + break; + } + } + else + { + if (result == SectorActionResult.Crushed) + { + switch (type) + { + case CeilingMoveType.SilentCrushAndRaise: + case CeilingMoveType.CrushAndRaise: + case CeilingMoveType.LowerAndCrush: + speed = SectorAction.CeilingSpeed / 8; + break; + + default: + break; + } + } + } + break; + } + } + + public CeilingMoveType Type + { + get => type; + set => type = value; + } + + public Sector Sector + { + get => sector; + set => sector = value; + } + + public Fixed BottomHeight + { + get => bottomHeight; + set => bottomHeight = value; + } + + public Fixed TopHeight + { + get => topHeight; + set => topHeight = value; + } + + public Fixed Speed + { + get => speed; + set => speed = value; + } + + public bool Crush + { + get => crush; + set => crush = value; + } + + public int Direction + { + get => direction; + set => direction = value; + } + + public int Tag + { + get => tag; + set => tag = value; + } + + public int OldDirection + { + get => oldDirection; + set => oldDirection = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CeilingMoveType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CeilingMoveType.cs new file mode 100644 index 00000000..2da9bba8 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CeilingMoveType.cs @@ -0,0 +1,31 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum CeilingMoveType + { + LowerToFloor, + RaiseToHighest, + LowerAndCrush, + CrushAndRaise, + FastCrushAndRaise, + SilentCrushAndRaise + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Cheat.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Cheat.cs new file mode 100644 index 00000000..9944980a --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Cheat.cs @@ -0,0 +1,408 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Linq; + +namespace ManagedDoom +{ + public sealed class Cheat + { + private static Tuple>[] list = new Tuple>[] + { + Tuple.Create("idfa", (Action)((cheat, typed) => cheat.FullAmmo())), + Tuple.Create("idkfa", (Action)((cheat, typed) => cheat.FullAmmoAndKeys())), + Tuple.Create("iddqd", (Action)((cheat, typed) => cheat.GodMode())), + Tuple.Create("idclip", (Action)((cheat, typed) => cheat.NoClip())), + Tuple.Create("idspispopd", (Action)((cheat, typed) => cheat.NoClip())), + Tuple.Create("iddt", (Action)((cheat, typed) => cheat.FullMap())), + Tuple.Create("idbehold", (Action)((cheat, typed) => cheat.ShowPowerUpList())), + Tuple.Create("idbehold?", (Action)((cheat, typed) => cheat.DoPowerUp(typed))), + Tuple.Create("idchoppers", (Action)((cheat, typed) => cheat.GiveChainsaw())), + Tuple.Create("tntem", (Action)((cheat, typed) => cheat.KillMonsters())), + Tuple.Create("killem", (Action)((cheat, typed) => cheat.KillMonsters())), + Tuple.Create("fhhall", (Action)((cheat, typed) => cheat.KillMonsters())), + Tuple.Create("idclev??", (Action)((cheat, typed) => cheat.ChangeLevel(typed))), + Tuple.Create("idmus??", (Action)((cheat, typed) => cheat.ChangeMusic(typed))) + }; + + private static readonly int maxLength = list.Max(tuple => tuple.Item1.Length); + + private World world; + + private char[] buffer; + private int p; + + public Cheat(World world) + { + this.world = world; + + buffer = new char[maxLength]; + p = 0; + } + + public bool DoEvent(DoomEvent e) + { + if (e.Type == EventType.KeyDown) + { + buffer[p] = e.Key.GetChar(); + + p = (p + 1) % buffer.Length; + + CheckBuffer(); + } + + return true; + } + + private void CheckBuffer() + { + for (var i = 0; i < list.Length; i++) + { + var code = list[i].Item1; + var q = p; + int j; + for (j = 0; j < code.Length; j++) + { + q--; + if (q == -1) + { + q = buffer.Length - 1; + } + var ch = code[code.Length - j - 1]; + if (buffer[q] != ch && ch != '?') + { + break; + } + } + + if (j == code.Length) + { + var typed = new char[code.Length]; + var k = code.Length; + q = p; + for (j = 0; j < code.Length; j++) + { + k--; + q--; + if (q == -1) + { + q = buffer.Length - 1; + } + typed[k] = buffer[q]; + } + list[i].Item2(this, new string(typed)); + } + } + } + + private void GiveWeapons() + { + var player = world.ConsolePlayer; + if (world.Options.GameMode == GameMode.Commercial) + { + for (var i = 0; i < (int)WeaponType.Count; i++) + { + player.WeaponOwned[i] = true; + } + } + else + { + for (var i = 0; i <= (int)WeaponType.Missile; i++) + { + player.WeaponOwned[i] = true; + } + player.WeaponOwned[(int)WeaponType.Chainsaw] = true; + if (world.Options.GameMode != GameMode.Shareware) + { + player.WeaponOwned[(int)WeaponType.Plasma] = true; + player.WeaponOwned[(int)WeaponType.Bfg] = true; + } + } + + player.Backpack = true; + for (var i = 0; i < (int)AmmoType.Count; i++) + { + player.MaxAmmo[i] = 2 * DoomInfo.AmmoInfos.Max[i]; + player.Ammo[i] = 2 * DoomInfo.AmmoInfos.Max[i]; + } + } + + private void FullAmmo() + { + GiveWeapons(); + var player = world.ConsolePlayer; + player.ArmorType = DoomInfo.DeHackEdConst.IdfaArmorClass; + player.ArmorPoints = DoomInfo.DeHackEdConst.IdfaArmor; + player.SendMessage(DoomInfo.Strings.STSTR_FAADDED); + } + + private void FullAmmoAndKeys() + { + GiveWeapons(); + var player = world.ConsolePlayer; + player.ArmorType = DoomInfo.DeHackEdConst.IdkfaArmorClass; + player.ArmorPoints = DoomInfo.DeHackEdConst.IdkfaArmor; + for (var i = 0; i < (int)CardType.Count; i++) + { + player.Cards[i] = true; + } + player.SendMessage(DoomInfo.Strings.STSTR_KFAADDED); + } + + private void GodMode() + { + var player = world.ConsolePlayer; + if ((player.Cheats & CheatFlags.GodMode) != 0) + { + player.Cheats &= ~CheatFlags.GodMode; + player.SendMessage(DoomInfo.Strings.STSTR_DQDOFF); + } + else + { + player.Cheats |= CheatFlags.GodMode; + player.Health = Math.Max(DoomInfo.DeHackEdConst.GodModeHealth, player.Health); + player.Mobj.Health = player.Health; + player.SendMessage(DoomInfo.Strings.STSTR_DQDON); + } + } + + private void NoClip() + { + var player = world.ConsolePlayer; + if ((player.Cheats & CheatFlags.NoClip) != 0) + { + player.Cheats &= ~CheatFlags.NoClip; + player.SendMessage(DoomInfo.Strings.STSTR_NCOFF); + } + else + { + player.Cheats |= CheatFlags.NoClip; + player.SendMessage(DoomInfo.Strings.STSTR_NCON); + } + } + + private void FullMap() + { + world.AutoMap.ToggleCheat(); + } + + private void ShowPowerUpList() + { + var player = world.ConsolePlayer; + player.SendMessage(DoomInfo.Strings.STSTR_BEHOLD); + } + + private void DoPowerUp(string typed) + { + switch (typed.Last()) + { + case 'v': + ToggleInvulnerability(); + break; + case 's': + ToggleStrength(); + break; + case 'i': + ToggleInvisibility(); + break; + case 'r': + ToggleIronFeet(); + break; + case 'a': + ToggleAllMap(); + break; + case 'l': + ToggleInfrared(); + break; + } + } + + private void ToggleInvulnerability() + { + var player = world.ConsolePlayer; + if (player.Powers[(int)PowerType.Invulnerability] > 0) + { + player.Powers[(int)PowerType.Invulnerability] = 0; + } + else + { + player.Powers[(int)PowerType.Invulnerability] = DoomInfo.PowerDuration.Invulnerability; + } + player.SendMessage(DoomInfo.Strings.STSTR_BEHOLDX); + } + + private void ToggleStrength() + { + var player = world.ConsolePlayer; + if (player.Powers[(int)PowerType.Strength] != 0) + { + player.Powers[(int)PowerType.Strength] = 0; + } + else + { + player.Powers[(int)PowerType.Strength] = 1; + } + player.SendMessage(DoomInfo.Strings.STSTR_BEHOLDX); + } + + private void ToggleInvisibility() + { + var player = world.ConsolePlayer; + if (player.Powers[(int)PowerType.Invisibility] > 0) + { + player.Powers[(int)PowerType.Invisibility] = 0; + player.Mobj.Flags &= ~MobjFlags.Shadow; + } + else + { + player.Powers[(int)PowerType.Invisibility] = DoomInfo.PowerDuration.Invisibility; + player.Mobj.Flags |= MobjFlags.Shadow; + } + player.SendMessage(DoomInfo.Strings.STSTR_BEHOLDX); + } + + private void ToggleIronFeet() + { + var player = world.ConsolePlayer; + if (player.Powers[(int)PowerType.IronFeet] > 0) + { + player.Powers[(int)PowerType.IronFeet] = 0; + } + else + { + player.Powers[(int)PowerType.IronFeet] = DoomInfo.PowerDuration.IronFeet; + } + player.SendMessage(DoomInfo.Strings.STSTR_BEHOLDX); + } + + private void ToggleAllMap() + { + var player = world.ConsolePlayer; + if (player.Powers[(int)PowerType.AllMap] != 0) + { + player.Powers[(int)PowerType.AllMap] = 0; + } + else + { + player.Powers[(int)PowerType.AllMap] = 1; + } + player.SendMessage(DoomInfo.Strings.STSTR_BEHOLDX); + } + + private void ToggleInfrared() + { + var player = world.ConsolePlayer; + if (player.Powers[(int)PowerType.Infrared] > 0) + { + player.Powers[(int)PowerType.Infrared] = 0; + } + else + { + player.Powers[(int)PowerType.Infrared] = DoomInfo.PowerDuration.Infrared; + } + player.SendMessage(DoomInfo.Strings.STSTR_BEHOLDX); + } + + private void GiveChainsaw() + { + var player = world.ConsolePlayer; + player.WeaponOwned[(int)WeaponType.Chainsaw] = true; + player.SendMessage(DoomInfo.Strings.STSTR_CHOPPERS); + } + + private void KillMonsters() + { + var player = world.ConsolePlayer; + var count = 0; + foreach (var thinker in world.Thinkers) + { + var mobj = thinker as Mobj; + if (mobj != null && + mobj.Player == null && + ((mobj.Flags & MobjFlags.CountKill) != 0 || mobj.Type == MobjType.Skull) && + mobj.Health > 0) + { + world.ThingInteraction.DamageMobj(mobj, null, player.Mobj, 10000); + count++; + } + } + player.SendMessage(count + " monsters killed"); + } + + private void ChangeLevel(string typed) + { + if (world.Options.GameMode == GameMode.Commercial) + { + int map; + if (!int.TryParse(typed.Substring(typed.Length - 2, 2), out map)) + { + return; + } + var skill = world.Options.Skill; + world.Game.DeferedInitNew(skill, 1, map); + } + else + { + int episode; + if (!int.TryParse(typed.Substring(typed.Length - 2, 1), out episode)) + { + return; + } + int map; + if (!int.TryParse(typed.Substring(typed.Length - 1, 1), out map)) + { + return; + } + var skill = world.Options.Skill; + world.Game.DeferedInitNew(skill, episode, map); + } + } + + private void ChangeMusic(string typed) + { + var options = new GameOptions(); + options.GameMode = world.Options.GameMode; + if (world.Options.GameMode == GameMode.Commercial) + { + int map; + if (!int.TryParse(typed.Substring(typed.Length - 2, 2), out map)) + { + return; + } + options.Map = map; + } + else + { + int episode; + if (!int.TryParse(typed.Substring(typed.Length - 2, 1), out episode)) + { + return; + } + int map; + if (!int.TryParse(typed.Substring(typed.Length - 1, 1), out map)) + { + return; + } + options.Episode = episode; + options.Map = map; + } + world.Options.Music.StartMusic(Map.GetMapBgm(options), true); + world.ConsolePlayer.SendMessage(DoomInfo.Strings.STSTR_MUS); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CheatFlags.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CheatFlags.cs new file mode 100644 index 00000000..2ebd127d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/CheatFlags.cs @@ -0,0 +1,33 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum CheatFlags + { + // No clipping, walk through barriers. + NoClip = 1, + + // No damage, no health loss. + GodMode = 2, + + // Not really a cheat, just a debug aid. + NoMomentum = 4 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Direction.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Direction.cs new file mode 100644 index 00000000..a8f0a2e0 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Direction.cs @@ -0,0 +1,35 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum Direction + { + East, + Northeast, + North, + Northwest, + west, + Southwest, + South, + Southeast, + None, + Count + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/DivLine.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/DivLine.cs new file mode 100644 index 00000000..90b4f57a --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/DivLine.cs @@ -0,0 +1,61 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class DivLine + { + private Fixed x; + private Fixed y; + private Fixed dx; + private Fixed dy; + + public void MakeFrom(LineDef line) + { + x = line.Vertex1.X; + y = line.Vertex1.Y; + dx = line.Dx; + dy = line.Dy; + } + + public Fixed X + { + get => x; + set => x = value; + } + + public Fixed Y + { + get => y; + set => y = value; + } + + public Fixed Dx + { + get => dx; + set => dx = value; + } + + public Fixed Dy + { + get => dy; + set => dy = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/FireFlicker.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/FireFlicker.cs new file mode 100644 index 00000000..4b425fce --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/FireFlicker.cs @@ -0,0 +1,81 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class FireFlicker : Thinker + { + private World world; + + private Sector sector; + private int count; + private int maxLight; + private int minLight; + + public FireFlicker(World world) + { + this.world = world; + } + + public override void Run() + { + if (--count > 0) + { + return; + } + + var amount = (world.Random.Next() & 3) * 16; + + if (sector.LightLevel - amount < minLight) + { + sector.LightLevel = minLight; + } + else + { + sector.LightLevel = maxLight - amount; + } + + count = 4; + } + + public Sector Sector + { + get => sector; + set => sector = value; + } + + public int Count + { + get => count; + set => count = value; + } + + public int MaxLight + { + get => maxLight; + set => maxLight = value; + } + + public int MinLight + { + get => minLight; + set => minLight = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/FloorMove.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/FloorMove.cs new file mode 100644 index 00000000..b38ee5be --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/FloorMove.cs @@ -0,0 +1,138 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class FloorMove : Thinker + { + private World world; + + private FloorMoveType type; + private bool crush; + private Sector sector; + private int direction; + private SectorSpecial newSpecial; + private int texture; + private Fixed floorDestHeight; + private Fixed speed; + + public FloorMove(World world) + { + this.world = world; + } + + public override void Run() + { + SectorActionResult result; + + var sa = world.SectorAction; + + result = sa.MovePlane( + sector, + speed, + floorDestHeight, + crush, + 0, + direction); + + if (((world.LevelTime + sector.Number) & 7) == 0) + { + world.StartSound(sector.SoundOrigin, Sfx.STNMOV, SfxType.Misc); + } + + if (result == SectorActionResult.PastDestination) + { + sector.SpecialData = null; + + if (direction == 1) + { + switch (type) + { + case FloorMoveType.DonutRaise: + sector.Special = newSpecial; + sector.FloorFlat = texture; + break; + } + } + else if (direction == -1) + { + switch (type) + { + case FloorMoveType.LowerAndChange: + sector.Special = newSpecial; + sector.FloorFlat = texture; + break; + } + } + + world.Thinkers.Remove(this); + + world.StartSound(sector.SoundOrigin, Sfx.PSTOP, SfxType.Misc); + } + } + + public FloorMoveType Type + { + get => type; + set => type = value; + } + + public bool Crush + { + get => crush; + set => crush = value; + } + + public Sector Sector + { + get => sector; + set => sector = value; + } + + public int Direction + { + get => direction; + set => direction = value; + } + + public SectorSpecial NewSpecial + { + get => newSpecial; + set => newSpecial = value; + } + + public int Texture + { + get => texture; + set => texture = value; + } + + public Fixed FloorDestHeight + { + get => floorDestHeight; + set => floorDestHeight = value; + } + + public Fixed Speed + { + get => speed; + set => speed = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/FloorMoveType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/FloorMoveType.cs new file mode 100644 index 00000000..87a5b4af --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/FloorMoveType.cs @@ -0,0 +1,55 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum FloorMoveType + { + // Lower floor to highest surrounding floor. + LowerFloor, + + // Lower floor to lowest surrounding floor. + LowerFloorToLowest, + + // Lower floor to highest surrounding floor very fast. + TurboLower, + + // Raise floor to lowest surrounding ceiling. + RaiseFloor, + + // Raise floor to next highest surrounding floor. + RaiseFloorToNearest, + + // Raise floor to shortest height texture around it. + RaiseToTexture, + + // Lower floor to lowest surrounding floor and + // change floor texture. + LowerAndChange, + + RaiseFloor24, + RaiseFloor24AndChange, + RaiseFloorCrush, + + // Raise to next highest floor, turbo-speed. + RaiseFloorTurbo, + DonutRaise, + RaiseFloor512 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/GlowingLight.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/GlowingLight.cs new file mode 100644 index 00000000..a8305dda --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/GlowingLight.cs @@ -0,0 +1,88 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class GlowingLight : Thinker + { + private static readonly int glowSpeed = 8; + + private World world; + + private Sector sector; + private int minLight; + private int maxLight; + private int direction; + + public GlowingLight(World world) + { + this.world = world; + } + + public override void Run() + { + switch (direction) + { + case -1: + // Down. + sector.LightLevel -= glowSpeed; + if (sector.LightLevel <= minLight) + { + sector.LightLevel += glowSpeed; + direction = 1; + } + break; + + case 1: + // Up. + sector.LightLevel += glowSpeed; + if (sector.LightLevel >= maxLight) + { + sector.LightLevel -= glowSpeed; + direction = -1; + } + break; + } + } + + public Sector Sector + { + get => sector; + set => sector = value; + } + + public int MinLight + { + get => minLight; + set => minLight = value; + } + + public int MaxLight + { + get => maxLight; + set => maxLight = value; + } + + public int Direction + { + get => direction; + set => direction = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Hitscan.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Hitscan.cs new file mode 100644 index 00000000..f31d317c --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Hitscan.cs @@ -0,0 +1,443 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Hitscan + { + private World world; + + public Hitscan(World world) + { + this.world = world; + + aimTraverseFunc = AimTraverse; + shootTraverseFunc = ShootTraverse; + } + + private Func aimTraverseFunc; + private Func shootTraverseFunc; + + // Who got hit (or null). + private Mobj lineTarget; + + private Mobj currentShooter; + private Fixed currentShooterZ; + + private Fixed currentRange; + private Fixed currentAimSlope; + private int currentDamage; + + // Slopes to top and bottom of target. + private Fixed topSlope; + private Fixed bottomSlope; + + /// + /// Find a thing or wall which is on the aiming line. + /// Sets lineTaget and aimSlope when a target is aimed at. + /// + private bool AimTraverse(Intercept intercept) + { + if (intercept.Line != null) + { + var line = intercept.Line; + + if ((line.Flags & LineFlags.TwoSided) == 0) + { + // Stop. + return false; + } + + var mc = world.MapCollision; + + // Crosses a two sided line. + // A two sided line will restrict the possible target ranges. + mc.LineOpening(line); + + if (mc.OpenBottom >= mc.OpenTop) + { + // Stop. + return false; + } + + var dist = currentRange * intercept.Frac; + + // The null check of the backsector below is necessary to avoid crash + // in certain PWADs, which contain two-sided lines with no backsector. + // These are imported from Chocolate Doom. + + if (line.BackSector == null || + line.FrontSector.FloorHeight != line.BackSector.FloorHeight) + { + var slope = (mc.OpenBottom - currentShooterZ) / dist; + if (slope > bottomSlope) + { + bottomSlope = slope; + } + } + + if (line.BackSector == null || + line.FrontSector.CeilingHeight != line.BackSector.CeilingHeight) + { + var slope = (mc.OpenTop - currentShooterZ) / dist; + if (slope < topSlope) + { + topSlope = slope; + } + } + + if (topSlope <= bottomSlope) + { + // Stop. + return false; + } + + // Shot continues. + return true; + } + + // Shoot a thing. + var thing = intercept.Thing; + if (thing == currentShooter) + { + // Can't shoot self. + return true; + } + + { + if ((thing.Flags & MobjFlags.Shootable) == 0) + { + // Corpse or something. + return true; + } + + // Check angles to see if the thing can be aimed at. + var dist = currentRange * intercept.Frac; + var thingTopSlope = (thing.Z + thing.Height - currentShooterZ) / dist; + + if (thingTopSlope < bottomSlope) + { + // Shot over the thing. + return true; + } + + var thingBottomSlope = (thing.Z - currentShooterZ) / dist; + + if (thingBottomSlope > topSlope) + { + // Shot under the thing. + return true; + } + + // This thing can be hit! + if (thingTopSlope > topSlope) + { + thingTopSlope = topSlope; + } + + if (thingBottomSlope < bottomSlope) + { + thingBottomSlope = bottomSlope; + } + + currentAimSlope = (thingTopSlope + thingBottomSlope) / 2; + lineTarget = thing; + + // Don't go any farther. + return false; + } + } + + /// + /// Fire a hitscan bullet along the aiming line. + /// + private bool ShootTraverse(Intercept intercept) + { + var mi = world.MapInteraction; + var pt = world.PathTraversal; + + if (intercept.Line != null) + { + var line = intercept.Line; + + if (line.Special != 0) + { + mi.ShootSpecialLine(currentShooter, line); + } + + if ((line.Flags & LineFlags.TwoSided) == 0) + { + goto hitLine; + } + + var mc = world.MapCollision; + + // Crosses a two sided line. + mc.LineOpening(line); + + var dist = currentRange * intercept.Frac; + + // Similar to AimTraverse, the code below is imported from Chocolate Doom. + if (line.BackSector == null) + { + { + var slope = (mc.OpenBottom - currentShooterZ) / dist; + if (slope > currentAimSlope) + { + goto hitLine; + } + } + + { + var slope = (mc.OpenTop - currentShooterZ) / dist; + if (slope < currentAimSlope) + { + goto hitLine; + } + } + } + else + { + if (line.FrontSector.FloorHeight != line.BackSector.FloorHeight) + { + var slope = (mc.OpenBottom - currentShooterZ) / dist; + if (slope > currentAimSlope) + { + goto hitLine; + } + } + + if (line.FrontSector.CeilingHeight != line.BackSector.CeilingHeight) + { + var slope = (mc.OpenTop - currentShooterZ) / dist; + if (slope < currentAimSlope) + { + goto hitLine; + } + } + } + + // Shot continues. + return true; + + // Hit line. + hitLine: + + // Position a bit closer. + var frac = intercept.Frac - Fixed.FromInt(4) / currentRange; + var x = pt.Trace.X + pt.Trace.Dx * frac; + var y = pt.Trace.Y + pt.Trace.Dy * frac; + var z = currentShooterZ + currentAimSlope * (frac * currentRange); + + if (line.FrontSector.CeilingFlat == world.Map.SkyFlatNumber) + { + // Don't shoot the sky! + if (z > line.FrontSector.CeilingHeight) + { + return false; + } + + // It's a sky hack wall. + if (line.BackSector != null && line.BackSector.CeilingFlat == world.Map.SkyFlatNumber) + { + return false; + } + } + + // Spawn bullet puffs. + SpawnPuff(x, y, z); + + // Don't go any farther. + return false; + } + + { + // Shoot a thing. + var thing = intercept.Thing; + if (thing == currentShooter) + { + // Can't shoot self. + return true; + } + + if ((thing.Flags & MobjFlags.Shootable) == 0) + { + // Corpse or something. + return true; + } + + // Check angles to see if the thing can be aimed at. + var dist = currentRange * intercept.Frac; + var thingTopSlope = (thing.Z + thing.Height - currentShooterZ) / dist; + + if (thingTopSlope < currentAimSlope) + { + // Shot over the thing. + return true; + } + + var thingBottomSlope = (thing.Z - currentShooterZ) / dist; + + if (thingBottomSlope > currentAimSlope) + { + // Shot under the thing. + return true; + } + + // Hit thing. + // Position a bit closer. + var frac = intercept.Frac - Fixed.FromInt(10) / currentRange; + + var x = pt.Trace.X + pt.Trace.Dx * frac; + var y = pt.Trace.Y + pt.Trace.Dy * frac; + var z = currentShooterZ + currentAimSlope * (frac * currentRange); + + // Spawn bullet puffs or blod spots, depending on target type. + if ((intercept.Thing.Flags & MobjFlags.NoBlood) != 0) + { + SpawnPuff(x, y, z); + } + else + { + SpawnBlood(x, y, z, currentDamage); + } + + if (currentDamage != 0) + { + world.ThingInteraction.DamageMobj(thing, currentShooter, currentShooter, currentDamage); + } + + // Don't go any farther. + return false; + } + } + + /// + /// Find a target on the aiming line. + /// Sets LineTaget when a target is aimed at. + /// + public Fixed AimLineAttack(Mobj shooter, Angle angle, Fixed range) + { + currentShooter = shooter; + currentShooterZ = shooter.Z + (shooter.Height >> 1) + Fixed.FromInt(8); + currentRange = range; + + var targetX = shooter.X + range.ToIntFloor() * Trig.Cos(angle); + var targetY = shooter.Y + range.ToIntFloor() * Trig.Sin(angle); + + // Can't shoot outside view angles. + topSlope = Fixed.FromInt(100) / 160; + bottomSlope = Fixed.FromInt(-100) / 160; + + lineTarget = null; + + world.PathTraversal.PathTraverse( + shooter.X, shooter.Y, + targetX, targetY, + PathTraverseFlags.AddLines | PathTraverseFlags.AddThings, + aimTraverseFunc); + + if (lineTarget != null) + { + return currentAimSlope; + } + + return Fixed.Zero; + } + + /// + /// Fire a hitscan bullet. + /// If damage == 0, it is just a test trace that will leave linetarget set. + /// + public void LineAttack(Mobj shooter, Angle angle, Fixed range, Fixed slope, int damage) + { + currentShooter = shooter; + currentShooterZ = shooter.Z + (shooter.Height >> 1) + Fixed.FromInt(8); + currentRange = range; + currentAimSlope = slope; + currentDamage = damage; + + var targetX = shooter.X + range.ToIntFloor() * Trig.Cos(angle); + var targetY = shooter.Y + range.ToIntFloor() * Trig.Sin(angle); + + world.PathTraversal.PathTraverse( + shooter.X, shooter.Y, + targetX, targetY, + PathTraverseFlags.AddLines | PathTraverseFlags.AddThings, + shootTraverseFunc); + } + + /// + /// Spawn a bullet puff. + /// + public void SpawnPuff(Fixed x, Fixed y, Fixed z) + { + var random = world.Random; + + z += new Fixed((random.Next() - random.Next()) << 10); + + var thing = world.ThingAllocation.SpawnMobj(x, y, z, MobjType.Puff); + thing.MomZ = Fixed.One; + thing.Tics -= random.Next() & 3; + + if (thing.Tics < 1) + { + thing.Tics = 1; + } + + // Don't make punches spark on the wall. + if (currentRange == WeaponBehavior.MeleeRange) + { + thing.SetState(MobjState.Puff3); + } + } + + /// + /// Spawn blood. + /// + public void SpawnBlood(Fixed x, Fixed y, Fixed z, int damage) + { + var random = world.Random; + + z += new Fixed((random.Next() - random.Next()) << 10); + + var thing = world.ThingAllocation.SpawnMobj(x, y, z, MobjType.Blood); + thing.MomZ = Fixed.FromInt(2); + thing.Tics -= random.Next() & 3; + + if (thing.Tics < 1) + { + thing.Tics = 1; + } + + if (damage <= 12 && damage >= 9) + { + thing.SetState(MobjState.Blood2); + } + else if (damage < 9) + { + thing.SetState(MobjState.Blood3); + } + } + + public Mobj LineTarget => lineTarget; + public Fixed BottomSlope => bottomSlope; + public Fixed TopSlope => topSlope; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Intercept.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Intercept.cs new file mode 100644 index 00000000..d0e1a4fd --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Intercept.cs @@ -0,0 +1,51 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Intercept + { + private Fixed frac; + private Mobj thing; + private LineDef line; + + public void Make(Fixed frac, Mobj thing) + { + this.frac = frac; + this.thing = thing; + this.line = null; + } + + public void Make(Fixed frac, LineDef line) + { + this.frac = frac; + this.thing = null; + this.line = line; + } + + public Fixed Frac + { + get => frac; + set => frac = value; + } + + public Mobj Thing => thing; + public LineDef Line => line; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ItemPickup.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ItemPickup.cs new file mode 100644 index 00000000..39ae6210 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ItemPickup.cs @@ -0,0 +1,761 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class ItemPickup + { + private World world; + + public ItemPickup(World world) + { + this.world = world; + } + + + + /// + /// Give the player the ammo. + /// + /// + /// The number of clip loads, not the individual count (0 = 1/2 clip). + /// + /// + /// False if the ammo can't be picked up at all. + /// + public bool GiveAmmo(Player player, AmmoType ammo, int amount) + { + if (ammo == AmmoType.NoAmmo) + { + return false; + } + + if (ammo < 0 || (int)ammo > (int)AmmoType.Count) + { + throw new Exception("Bad ammo type: " + ammo); + } + + if (player.Ammo[(int)ammo] == player.MaxAmmo[(int)ammo]) + { + return false; + } + + if (amount != 0) + { + amount *= DoomInfo.AmmoInfos.Clip[(int)ammo]; + } + else + { + amount = DoomInfo.AmmoInfos.Clip[(int)ammo] / 2; + } + + if (world.Options.Skill == GameSkill.Baby || + world.Options.Skill == GameSkill.Nightmare) + { + // Give double ammo in trainer mode, you'll need in nightmare. + amount <<= 1; + } + + var oldammo = player.Ammo[(int)ammo]; + player.Ammo[(int)ammo] += amount; + + if (player.Ammo[(int)ammo] > player.MaxAmmo[(int)ammo]) + { + player.Ammo[(int)ammo] = player.MaxAmmo[(int)ammo]; + } + + // If non zero ammo, don't change up weapons, player was lower on purpose. + if (oldammo != 0) + { + return true; + } + + // We were down to zero, so select a new weapon. + // Preferences are not user selectable. + switch (ammo) + { + case AmmoType.Clip: + if (player.ReadyWeapon == WeaponType.Fist) + { + if (player.WeaponOwned[(int)WeaponType.Chaingun]) + { + player.PendingWeapon = WeaponType.Chaingun; + } + else + { + player.PendingWeapon = WeaponType.Pistol; + } + } + break; + + case AmmoType.Shell: + if (player.ReadyWeapon == WeaponType.Fist + || player.ReadyWeapon == WeaponType.Pistol) + { + if (player.WeaponOwned[(int)WeaponType.Shotgun]) + { + player.PendingWeapon = WeaponType.Shotgun; + } + } + break; + + case AmmoType.Cell: + if (player.ReadyWeapon == WeaponType.Fist + || player.ReadyWeapon == WeaponType.Pistol) + { + if (player.WeaponOwned[(int)WeaponType.Plasma]) + { + player.PendingWeapon = WeaponType.Plasma; + } + } + break; + + case AmmoType.Missile: + if (player.ReadyWeapon == WeaponType.Fist) + { + if (player.WeaponOwned[(int)WeaponType.Missile]) + { + player.PendingWeapon = WeaponType.Missile; + } + } + break; + + default: + break; + } + + return true; + } + + + private static readonly int bonusAdd = 6; + + /// + /// Give the weapon to the player. + /// + /// + /// True if the weapons is dropped by a monster. + /// + public bool GiveWeapon(Player player, WeaponType weapon, bool dropped) + { + if (world.Options.NetGame && (world.Options.Deathmatch != 2) && !dropped) + { + // Leave placed weapons forever on net games. + if (player.WeaponOwned[(int)weapon]) + { + return false; + } + + player.BonusCount += bonusAdd; + player.WeaponOwned[(int)weapon] = true; + + if (world.Options.Deathmatch != 0) + { + GiveAmmo(player, DoomInfo.WeaponInfos[(int)weapon].Ammo, 5); + } + else + { + GiveAmmo(player, DoomInfo.WeaponInfos[(int)weapon].Ammo, 2); + } + + player.PendingWeapon = weapon; + + if (player == world.ConsolePlayer) + { + world.StartSound(player.Mobj, Sfx.WPNUP, SfxType.Misc); + } + + return false; + } + + bool gaveAmmo; + if (DoomInfo.WeaponInfos[(int)weapon].Ammo != AmmoType.NoAmmo) + { + // Give one clip with a dropped weapon, two clips with a found weapon. + if (dropped) + { + gaveAmmo = GiveAmmo(player, DoomInfo.WeaponInfos[(int)weapon].Ammo, 1); + } + else + { + gaveAmmo = GiveAmmo(player, DoomInfo.WeaponInfos[(int)weapon].Ammo, 2); + } + } + else + { + gaveAmmo = false; + } + + bool gaveWeapon; + if (player.WeaponOwned[(int)weapon]) + { + gaveWeapon = false; + } + else + { + gaveWeapon = true; + player.WeaponOwned[(int)weapon] = true; + player.PendingWeapon = weapon; + } + + return (gaveWeapon || gaveAmmo); + } + + + /// + /// Give the health point to the player. + /// + /// + /// False if the health point isn't needed at all. + /// + private bool GiveHealth(Player player, int amount) + { + if (player.Health >= DoomInfo.DeHackEdConst.InitialHealth) + { + return false; + } + + player.Health += amount; + if (player.Health > DoomInfo.DeHackEdConst.InitialHealth) + { + player.Health = DoomInfo.DeHackEdConst.InitialHealth; + } + + player.Mobj.Health = player.Health; + + return true; + } + + + /// + /// Give the armor to the player. + /// + /// + /// Returns false if the armor is worse than the current armor. + /// + private bool GiveArmor(Player player, int type) + { + var hits = type * 100; + + if (player.ArmorPoints >= hits) + { + // Don't pick up. + return false; + } + + player.ArmorType = type; + player.ArmorPoints = hits; + + return true; + } + + + /// + /// Give the card to the player. + /// + private void GiveCard(Player player, CardType card) + { + if (player.Cards[(int)card]) + { + return; + } + + player.BonusCount = bonusAdd; + player.Cards[(int)card] = true; + } + + + /// + /// Give the power up to the player. + /// + /// + /// False if the power up is not necessary. + /// + private bool GivePower(Player player, PowerType type) + { + if (type == PowerType.Invulnerability) + { + player.Powers[(int)type] = DoomInfo.PowerDuration.Invulnerability; + return true; + } + + if (type == PowerType.Invisibility) + { + player.Powers[(int)type] = DoomInfo.PowerDuration.Invisibility; + player.Mobj.Flags |= MobjFlags.Shadow; + return true; + } + + if (type == PowerType.Infrared) + { + player.Powers[(int)type] = DoomInfo.PowerDuration.Infrared; + return true; + } + + if (type == PowerType.IronFeet) + { + player.Powers[(int)type] = DoomInfo.PowerDuration.IronFeet; + return true; + } + + if (type == PowerType.Strength) + { + GiveHealth(player, 100); + player.Powers[(int)type] = 1; + return true; + } + + if (player.Powers[(int)type] != 0) + { + // Already got it. + return false; + } + + player.Powers[(int)type] = 1; + + return true; + } + + + /// + /// Check for item pickup. + /// + public void TouchSpecialThing(Mobj special, Mobj toucher) + { + var delta = special.Z - toucher.Z; + + if (delta > toucher.Height || delta < Fixed.FromInt(-8)) + { + // Out of reach. + return; + } + + var sound = Sfx.ITEMUP; + var player = toucher.Player; + + // Dead thing touching. + // Can happen with a sliding player corpse. + if (toucher.Health <= 0) + { + return; + } + + // Identify by sprite. + switch (special.Sprite) + { + // Armor. + case Sprite.ARM1: + if (!GiveArmor(player, DoomInfo.DeHackEdConst.GreenArmorClass)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTARMOR); + break; + + case Sprite.ARM2: + if (!GiveArmor(player, DoomInfo.DeHackEdConst.BlueArmorClass)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTMEGA); + break; + + // Bonus items. + case Sprite.BON1: + // Can go over 100%. + player.Health++; + if (player.Health > DoomInfo.DeHackEdConst.MaxHealth) + { + player.Health = DoomInfo.DeHackEdConst.MaxHealth; + } + player.Mobj.Health = player.Health; + player.SendMessage(DoomInfo.Strings.GOTHTHBONUS); + break; + + case Sprite.BON2: + // Can go over 100%. + player.ArmorPoints++; + if (player.ArmorPoints > DoomInfo.DeHackEdConst.MaxArmor) + { + player.ArmorPoints = DoomInfo.DeHackEdConst.MaxArmor; + } + if (player.ArmorType == 0) + { + player.ArmorType = DoomInfo.DeHackEdConst.GreenArmorClass; + } + player.SendMessage(DoomInfo.Strings.GOTARMBONUS); + break; + + case Sprite.SOUL: + player.Health += DoomInfo.DeHackEdConst.SoulsphereHealth; + if (player.Health > DoomInfo.DeHackEdConst.MaxSoulsphere) + { + player.Health = DoomInfo.DeHackEdConst.MaxSoulsphere; + } + player.Mobj.Health = player.Health; + player.SendMessage(DoomInfo.Strings.GOTSUPER); + sound = Sfx.GETPOW; + break; + + case Sprite.MEGA: + if (world.Options.GameMode != GameMode.Commercial) + { + return; + } + + player.Health = DoomInfo.DeHackEdConst.MegasphereHealth; + player.Mobj.Health = player.Health; + GiveArmor(player, DoomInfo.DeHackEdConst.BlueArmorClass); + player.SendMessage(DoomInfo.Strings.GOTMSPHERE); + sound = Sfx.GETPOW; + break; + + // Cards. + // Leave cards for everyone. + case Sprite.BKEY: + if (!player.Cards[(int)CardType.BlueCard]) + { + player.SendMessage(DoomInfo.Strings.GOTBLUECARD); + } + GiveCard(player, CardType.BlueCard); + if (!world.Options.NetGame) + { + break; + } + return; + + case Sprite.YKEY: + if (!player.Cards[(int)CardType.YellowCard]) + { + player.SendMessage(DoomInfo.Strings.GOTYELWCARD); + } + GiveCard(player, CardType.YellowCard); + if (!world.Options.NetGame) + { + break; + } + return; + + case Sprite.RKEY: + if (!player.Cards[(int)CardType.RedCard]) + { + player.SendMessage(DoomInfo.Strings.GOTREDCARD); + } + GiveCard(player, CardType.RedCard); + if (!world.Options.NetGame) + { + break; + } + return; + + case Sprite.BSKU: + if (!player.Cards[(int)CardType.BlueSkull]) + { + player.SendMessage(DoomInfo.Strings.GOTBLUESKUL); + } + GiveCard(player, CardType.BlueSkull); + if (!world.Options.NetGame) + { + break; + } + return; + + case Sprite.YSKU: + if (!player.Cards[(int)CardType.YellowSkull]) + { + player.SendMessage(DoomInfo.Strings.GOTYELWSKUL); + } + GiveCard(player, CardType.YellowSkull); + if (!world.Options.NetGame) + { + break; + } + return; + + case Sprite.RSKU: + if (!player.Cards[(int)CardType.RedSkull]) + { + player.SendMessage(DoomInfo.Strings.GOTREDSKULL); + } + GiveCard(player, CardType.RedSkull); + if (!world.Options.NetGame) + { + break; + } + return; + + // Medikits, heals. + case Sprite.STIM: + if (!GiveHealth(player, 10)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTSTIM); + break; + + case Sprite.MEDI: + if (!GiveHealth(player, 25)) + { + return; + } + if (player.Health < 25) + { + player.SendMessage(DoomInfo.Strings.GOTMEDINEED); + } + else + { + player.SendMessage(DoomInfo.Strings.GOTMEDIKIT); + } + break; + + + // Power ups. + case Sprite.PINV: + if (!GivePower(player, PowerType.Invulnerability)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTINVUL); + sound = Sfx.GETPOW; + break; + + case Sprite.PSTR: + if (!GivePower(player, PowerType.Strength)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTBERSERK); + if (player.ReadyWeapon != WeaponType.Fist) + { + player.PendingWeapon = WeaponType.Fist; + } + sound = Sfx.GETPOW; + break; + + case Sprite.PINS: + if (!GivePower(player, PowerType.Invisibility)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTINVIS); + sound = Sfx.GETPOW; + break; + + case Sprite.SUIT: + if (!GivePower(player, PowerType.IronFeet)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTSUIT); + sound = Sfx.GETPOW; + break; + + case Sprite.PMAP: + if (!GivePower(player, PowerType.AllMap)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTMAP); + sound = Sfx.GETPOW; + break; + + case Sprite.PVIS: + if (!GivePower(player, PowerType.Infrared)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTVISOR); + sound = Sfx.GETPOW; + break; + + // Ammo. + case Sprite.CLIP: + if ((special.Flags & MobjFlags.Dropped) != 0) + { + if (!GiveAmmo(player, AmmoType.Clip, 0)) + { + return; + } + } + else + { + if (!GiveAmmo(player, AmmoType.Clip, 1)) + { + return; + } + } + player.SendMessage(DoomInfo.Strings.GOTCLIP); + break; + + case Sprite.AMMO: + if (!GiveAmmo(player, AmmoType.Clip, 5)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTCLIPBOX); + break; + + case Sprite.ROCK: + if (!GiveAmmo(player, AmmoType.Missile, 1)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTROCKET); + break; + + case Sprite.BROK: + if (!GiveAmmo(player, AmmoType.Missile, 5)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTROCKBOX); + break; + + case Sprite.CELL: + if (!GiveAmmo(player, AmmoType.Cell, 1)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTCELL); + break; + + case Sprite.CELP: + if (!GiveAmmo(player, AmmoType.Cell, 5)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTCELLBOX); + break; + + case Sprite.SHEL: + if (!GiveAmmo(player, AmmoType.Shell, 1)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTSHELLS); + break; + + case Sprite.SBOX: + if (!GiveAmmo(player, AmmoType.Shell, 5)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTSHELLBOX); + break; + + case Sprite.BPAK: + if (!player.Backpack) + { + for (var i = 0; i < (int)AmmoType.Count; i++) + { + player.MaxAmmo[i] *= 2; + } + player.Backpack = true; + } + for (var i = 0; i < (int)AmmoType.Count; i++) + { + GiveAmmo(player, (AmmoType)i, 1); + } + player.SendMessage(DoomInfo.Strings.GOTBACKPACK); + break; + + // Weapons. + case Sprite.BFUG: + if (!GiveWeapon(player, WeaponType.Bfg, false)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTBFG9000); + sound = Sfx.WPNUP; + break; + + case Sprite.MGUN: + if (!GiveWeapon(player, WeaponType.Chaingun, (special.Flags & MobjFlags.Dropped) != 0)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTCHAINGUN); + sound = Sfx.WPNUP; + break; + + case Sprite.CSAW: + if (!GiveWeapon(player, WeaponType.Chainsaw, false)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTCHAINSAW); + sound = Sfx.WPNUP; + break; + + case Sprite.LAUN: + if (!GiveWeapon(player, WeaponType.Missile, false)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTLAUNCHER); + sound = Sfx.WPNUP; + break; + + case Sprite.PLAS: + if (!GiveWeapon(player, WeaponType.Plasma, false)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTPLASMA); + sound = Sfx.WPNUP; + break; + + case Sprite.SHOT: + if (!GiveWeapon(player, WeaponType.Shotgun, (special.Flags & MobjFlags.Dropped) != 0)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTSHOTGUN); + sound = Sfx.WPNUP; + break; + + case Sprite.SGN2: + if (!GiveWeapon(player, WeaponType.SuperShotgun, (special.Flags & MobjFlags.Dropped) != 0)) + { + return; + } + player.SendMessage(DoomInfo.Strings.GOTSHOTGUN2); + sound = Sfx.WPNUP; + break; + + default: + throw new Exception("Unknown gettable thing!"); + } + + if ((special.Flags & MobjFlags.CountItem) != 0) + { + player.ItemCount++; + } + + world.ThingAllocation.RemoveMobj(special); + + player.BonusCount += bonusAdd; + + if (player == world.ConsolePlayer) + { + world.StartSound(player.Mobj, sound, SfxType.Misc); + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/LightFlash.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/LightFlash.cs new file mode 100644 index 00000000..fed880a9 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/LightFlash.cs @@ -0,0 +1,93 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class LightFlash : Thinker + { + private World world; + + private Sector sector; + private int count; + private int maxLight; + private int minLight; + private int maxTime; + private int minTime; + + public LightFlash(World world) + { + this.world = world; + } + + public override void Run() + { + if (--count > 0) + { + return; + } + + if (sector.LightLevel == maxLight) + { + sector.LightLevel = minLight; + count = (world.Random.Next() & minTime) + 1; + } + else + { + sector.LightLevel = maxLight; + count = (world.Random.Next() & maxTime) + 1; + } + } + + public Sector Sector + { + get => sector; + set => sector = value; + } + + public int Count + { + get => count; + set => count = value; + } + + public int MaxLight + { + get => maxLight; + set => maxLight = value; + } + + public int MinLight + { + get => minLight; + set => minLight = value; + } + + public int MaxTime + { + get => maxTime; + set => maxTime = value; + } + + public int MinTime + { + get => minTime; + set => minTime = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/LightingChange.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/LightingChange.cs new file mode 100644 index 00000000..f28955cb --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/LightingChange.cs @@ -0,0 +1,145 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class LightingChange + { + private World world; + + public LightingChange(World world) + { + this.world = world; + } + + public void SpawnFireFlicker(Sector sector) + { + // Note that we are resetting sector attributes. + // Nothing special about it during gameplay. + sector.Special = 0; + + var flicker = new FireFlicker(world); + + world.Thinkers.Add(flicker); + + flicker.Sector = sector; + flicker.MaxLight = sector.LightLevel; + flicker.MinLight = FindMinSurroundingLight(sector, sector.LightLevel) + 16; + flicker.Count = 4; + } + + public void SpawnLightFlash(Sector sector) + { + // Nothing special about it during gameplay. + sector.Special = 0; + + var light = new LightFlash(world); + + world.Thinkers.Add(light); + + light.Sector = sector; + light.MaxLight = sector.LightLevel; + + light.MinLight = FindMinSurroundingLight(sector, sector.LightLevel); + light.MaxTime = 64; + light.MinTime = 7; + light.Count = (world.Random.Next() & light.MaxTime) + 1; + } + + public void SpawnStrobeFlash(Sector sector, int time, bool inSync) + { + var strobe = new StrobeFlash(world); + + world.Thinkers.Add(strobe); + + strobe.Sector = sector; + strobe.DarkTime = time; + strobe.BrightTime = StrobeFlash.StrobeBright; + strobe.MaxLight = sector.LightLevel; + strobe.MinLight = FindMinSurroundingLight(sector, sector.LightLevel); + + if (strobe.MinLight == strobe.MaxLight) + { + strobe.MinLight = 0; + } + + // Nothing special about it during gameplay. + sector.Special = 0; + + if (inSync) + { + strobe.Count = 1; + } + else + { + strobe.Count = (world.Random.Next() & 7) + 1; + } + } + + public void SpawnGlowingLight(Sector sector) + { + var glowing = new GlowingLight(world); + + world.Thinkers.Add(glowing); + + glowing.Sector = sector; + glowing.MinLight = FindMinSurroundingLight(sector, sector.LightLevel); + glowing.MaxLight = sector.LightLevel; + glowing.Direction = -1; + + sector.Special = 0; + } + + private int FindMinSurroundingLight(Sector sector, int max) + { + var min = max; + for (var i = 0; i < sector.Lines.Length; i++) + { + var line = sector.Lines[i]; + var check = GetNextSector(line, sector); + + if (check == null) + { + continue; + } + + if (check.LightLevel < min) + { + min = check.LightLevel; + } + } + return min; + } + + private Sector GetNextSector(LineDef line, Sector sector) + { + if ((line.Flags & LineFlags.TwoSided) == 0) + { + return null; + } + + if (line.FrontSector == sector) + { + return line.BackSector; + } + + return line.FrontSector; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MapCollision.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MapCollision.cs new file mode 100644 index 00000000..1ff50d3d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MapCollision.cs @@ -0,0 +1,79 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class MapCollision + { + private World world; + + private Fixed openTop; + private Fixed openBottom; + private Fixed openRange; + private Fixed lowFloor; + + public MapCollision(World world) + { + this.world = world; + } + + /// + /// Sets opentop and openbottom to the window through a two sided line. + /// + public void LineOpening(LineDef line) + { + if (line.BackSide == null) + { + // If the line is single sided, nothing can pass through. + openRange = Fixed.Zero; + return; + } + + var front = line.FrontSector; + var back = line.BackSector; + + if (front.CeilingHeight < back.CeilingHeight) + { + openTop = front.CeilingHeight; + } + else + { + openTop = back.CeilingHeight; + } + + if (front.FloorHeight > back.FloorHeight) + { + openBottom = front.FloorHeight; + lowFloor = back.FloorHeight; + } + else + { + openBottom = back.FloorHeight; + lowFloor = front.FloorHeight; + } + + openRange = openTop - openBottom; + } + + public Fixed OpenTop => openTop; + public Fixed OpenBottom => openBottom; + public Fixed OpenRange => openRange; + public Fixed LowFloor => lowFloor; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MapInteraction.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MapInteraction.cs new file mode 100644 index 00000000..32e7ed20 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MapInteraction.cs @@ -0,0 +1,1080 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class MapInteraction + { + private static readonly Fixed useRange = Fixed.FromInt(64); + + private World world; + + public MapInteraction(World world) + { + this.world = world; + + InitUse(); + } + + + + //////////////////////////////////////////////////////////// + // Line use + //////////////////////////////////////////////////////////// + + private Mobj useThing; + private Func useTraverseFunc; + + private void InitUse() + { + useTraverseFunc = UseTraverse; + } + + private bool UseTraverse(Intercept intercept) + { + var mc = world.MapCollision; + + if (intercept.Line.Special == 0) + { + mc.LineOpening(intercept.Line); + if (mc.OpenRange <= Fixed.Zero) + { + world.StartSound(useThing, Sfx.NOWAY, SfxType.Voice); + + // Can't use through a wall. + return false; + } + + // Not a special line, but keep checking. + return true; + } + + var side = 0; + if (Geometry.PointOnLineSide(useThing.X, useThing.Y, intercept.Line) == 1) + { + side = 1; + } + + UseSpecialLine(useThing, intercept.Line, side); + + // Can't use for than one special line in a row. + return false; + } + + /// + /// Looks for special lines in front of the player to activate. + /// + public void UseLines(Player player) + { + var pt = world.PathTraversal; + + useThing = player.Mobj; + + var angle = player.Mobj.Angle; + + var x1 = player.Mobj.X; + var y1 = player.Mobj.Y; + var x2 = x1 + useRange.ToIntFloor() * Trig.Cos(angle); + var y2 = y1 + useRange.ToIntFloor() * Trig.Sin(angle); + + pt.PathTraverse(x1, y1, x2, y2, PathTraverseFlags.AddLines, useTraverseFunc); + } + + /// + /// Called when a thing uses a special line. + /// Only the front sides of lines are usable. + /// + public bool UseSpecialLine(Mobj thing, LineDef line, int side) + { + var specials = world.Specials; + var sa = world.SectorAction; + + // Err... + // Use the back sides of VERY SPECIAL lines... + if (side != 0) + { + switch ((int)line.Special) + { + case 124: + // Sliding door open and close (unused). + break; + + default: + return false; + } + } + + // Switches that other things can activate. + if (thing.Player == null) + { + // Never open secret doors. + if ((line.Flags & LineFlags.Secret) != 0) + { + return false; + } + + switch ((int)line.Special) + { + case 1: // Manual door raise. + case 32: // Manual blue. + case 33: // Manual red. + case 34: // Manual yellow. + break; + + default: + return false; + } + } + + // Do something. + switch ((int)line.Special) + { + // MANUALS + case 1: // Vertical door. + case 26: // Blue door (locked). + case 27: // Yellow door (locked). + case 28: // Red door (locked). + + case 31: // Manual door open. + case 32: // Blue locked door open. + case 33: // Red locked door open. + case 34: // Yellow locked door open. + + case 117: // Blazing door raise. + case 118: // Blazing door open. + sa.DoLocalDoor(line, thing); + break; + + // SWITCHES + case 7: + // Build stairs. + if (sa.BuildStairs(line, StairType.Build8)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 9: + // Change donut. + if (sa.DoDonut(line)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 11: + // Exit level. + specials.ChangeSwitchTexture(line, false); + world.ExitLevel(); + break; + + case 14: + // Raise floor 32 and change texture. + if (sa.DoPlatform(line, PlatformType.RaiseAndChange, 32)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 15: + // Raise floor 24 and change texture. + if (sa.DoPlatform(line, PlatformType.RaiseAndChange, 24)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 18: + // Raise floor to next highest floor. + if (sa.DoFloor(line, FloorMoveType.RaiseFloorToNearest)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 20: + // Raise platform next highest floor and change texture. + if (sa.DoPlatform(line, PlatformType.RaiseToNearestAndChange, 0)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 21: + // Platform down, wait, up and stay. + if (sa.DoPlatform(line, PlatformType.DownWaitUpStay, 0)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 23: + // Lower floor to Lowest. + if (sa.DoFloor(line, FloorMoveType.LowerFloorToLowest)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 29: + // Raise door. + if (sa.DoDoor(line, VerticalDoorType.Normal)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 41: + // Lower ceiling to floor. + if (sa.DoCeiling(line, CeilingMoveType.LowerToFloor)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 71: + // Turbo lower floor. + if (sa.DoFloor(line, FloorMoveType.TurboLower)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 49: + // Ceiling crush and raise. + if (sa.DoCeiling(line, CeilingMoveType.CrushAndRaise)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 50: + // Close door. + if (sa.DoDoor(line, VerticalDoorType.Close)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 51: + // Secret exit. + specials.ChangeSwitchTexture(line, false); + world.SecretExitLevel(); + break; + + case 55: + // Raise floor crush. + if (sa.DoFloor(line, FloorMoveType.RaiseFloorCrush)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 101: + // Raise floor. + if (sa.DoFloor(line, FloorMoveType.RaiseFloor)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 102: + // Lower floor to surrounding floor height. + if (sa.DoFloor(line, FloorMoveType.LowerFloor)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 103: + // Open door. + if (sa.DoDoor(line, VerticalDoorType.Open)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 111: + // Blazing door raise (faster than turbo). + if (sa.DoDoor(line, VerticalDoorType.BlazeRaise)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 112: + // Blazing door open (faster than turbo). + if (sa.DoDoor(line, VerticalDoorType.BlazeOpen)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 113: + // Blazing door close (faster than turbo). + if (sa.DoDoor(line, VerticalDoorType.BlazeClose)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 122: + // Blazing platform down, wait, up and stay. + if (sa.DoPlatform(line, PlatformType.BlazeDwus, 0)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 127: + // Build stairs turbo 16. + if (sa.BuildStairs(line, StairType.Turbo16)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 131: + // Raise floor turbo. + if (sa.DoFloor(line, FloorMoveType.RaiseFloorTurbo)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 133: + // Blazing open door (blue). + case 135: + // Blazing open door (red). + case 137: + // Blazing open door (yellow). + if (sa.DoLockedDoor(line, VerticalDoorType.BlazeOpen, thing)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + case 140: + // Raise floor 512. + if (sa.DoFloor(line, FloorMoveType.RaiseFloor512)) + { + specials.ChangeSwitchTexture(line, false); + } + break; + + // BUTTONS + case 42: + // Close door. + if (sa.DoDoor(line, VerticalDoorType.Close)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 43: + // Lower ceiling to floor. + if (sa.DoCeiling(line, CeilingMoveType.LowerToFloor)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 45: + // lower floor to surrounding floor height. + if (sa.DoFloor(line, FloorMoveType.LowerFloor)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 60: + // Lower floor to Lowest. + if (sa.DoFloor(line, FloorMoveType.LowerFloorToLowest)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 61: + // Open door. + if (sa.DoDoor(line, VerticalDoorType.Open)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 62: + // Platform down, wait, up and stay. + if (sa.DoPlatform(line, PlatformType.DownWaitUpStay, 1)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 63: + // Raise door. + if (sa.DoDoor(line, VerticalDoorType.Normal)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 64: + // Raise floor to ceiling. + if (sa.DoFloor(line, FloorMoveType.RaiseFloor)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 66: + // Raise floor 24 and change texture. + if (sa.DoPlatform(line, PlatformType.RaiseAndChange, 24)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 67: + // Raise floor 32 and change texture. + if (sa.DoPlatform(line, PlatformType.RaiseAndChange, 32)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 65: + // Raise floor crush. + if (sa.DoFloor(line, FloorMoveType.RaiseFloorCrush)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 68: + // Raise platform to next highest floor and change texture. + if (sa.DoPlatform(line, PlatformType.RaiseToNearestAndChange, 0)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 69: + // Raise floor to next highest floor. + if (sa.DoFloor(line, FloorMoveType.RaiseFloorToNearest)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 70: + // Turbo lower floor. + if (sa.DoFloor(line, FloorMoveType.TurboLower)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 114: + // Blazing door raise (faster than turbo). + if (sa.DoDoor(line, VerticalDoorType.BlazeRaise)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 115: + // Blazing door open (faster than turbo). + if (sa.DoDoor(line, VerticalDoorType.BlazeOpen)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 116: + // Blazing door close (faster than turbo). + if (sa.DoDoor(line, VerticalDoorType.BlazeClose)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 123: + // Blazing platform down, wait, up and stay. + if (sa.DoPlatform(line, PlatformType.BlazeDwus, 0)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 132: + // Raise floor turbo. + if (sa.DoFloor(line, FloorMoveType.RaiseFloorTurbo)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 99: + // Blazing open door (blue). + case 134: + // Blazing open door (red). + case 136: + // Blazing open door (yellow). + if (sa.DoLockedDoor(line, VerticalDoorType.BlazeOpen, thing)) + { + specials.ChangeSwitchTexture(line, true); + } + break; + + case 138: + // Light turn on. + sa.LightTurnOn(line, 255); + specials.ChangeSwitchTexture(line, true); + break; + + case 139: + // Light turn Off. + sa.LightTurnOn(line, 35); + specials.ChangeSwitchTexture(line, true); + break; + } + + return true; + } + + + + //////////////////////////////////////////////////////////// + // Line crossing + //////////////////////////////////////////////////////////// + + /// + /// Called every time a thing origin is about to cross a line + /// with a non zero special. + /// + public void CrossSpecialLine(LineDef line, int side, Mobj thing) + { + // Triggers that other things can activate. + if (thing.Player == null) + { + // Things that should NOT trigger specials... + switch (thing.Type) + { + case MobjType.Rocket: + case MobjType.Plasma: + case MobjType.Bfg: + case MobjType.Troopshot: + case MobjType.Headshot: + case MobjType.Bruisershot: + return; + default: + break; + } + + var ok = false; + switch ((int)line.Special) + { + case 39: // TELEPORT TRIGGER + case 97: // TELEPORT RETRIGGER + case 125: // TELEPORT MONSTERONLY TRIGGER + case 126: // TELEPORT MONSTERONLY RETRIGGER + case 4: // RAISE DOOR + case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER + case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER + ok = true; + break; + } + if (!ok) + { + return; + } + } + + var sa = world.SectorAction; + + // Note: could use some const's here. + switch ((int)line.Special) + { + // TRIGGERS. + // All from here to RETRIGGERS. + case 2: + // Open door. + sa.DoDoor(line, VerticalDoorType.Open); + line.Special = 0; + break; + + case 3: + // Close door. + sa.DoDoor(line, VerticalDoorType.Close); + line.Special = 0; + break; + + case 4: + // Raise door. + sa.DoDoor(line, VerticalDoorType.Normal); + line.Special = 0; + break; + + case 5: + // Raise floor. + sa.DoFloor(line, FloorMoveType.RaiseFloor); + line.Special = 0; + break; + + case 6: + // Fast ceiling crush and raise. + sa.DoCeiling(line, CeilingMoveType.FastCrushAndRaise); + line.Special = 0; + break; + + case 8: + // Build stairs. + sa.BuildStairs(line, StairType.Build8); + line.Special = 0; + break; + + case 10: + // Platform down, wait, up and stay. + sa.DoPlatform(line, PlatformType.DownWaitUpStay, 0); + line.Special = 0; + break; + + case 12: + // Light turn on - brightest near. + sa.LightTurnOn(line, 0); + line.Special = 0; + break; + + case 13: + // Light turn on 255. + sa.LightTurnOn(line, 255); + line.Special = 0; + break; + + case 16: + // Close door 30. + sa.DoDoor(line, VerticalDoorType.Close30ThenOpen); + line.Special = 0; + break; + + case 17: + // Start light strobing. + sa.StartLightStrobing(line); + line.Special = 0; + break; + + case 19: + // Lower floor. + sa.DoFloor(line, FloorMoveType.LowerFloor); + line.Special = 0; + break; + + case 22: + // Raise floor to nearest height and change texture. + sa.DoPlatform(line, PlatformType.RaiseToNearestAndChange, 0); + line.Special = 0; + break; + + case 25: + // Ceiling crush and raise. + sa.DoCeiling(line, CeilingMoveType.CrushAndRaise); + line.Special = 0; + break; + + case 30: + // Raise floor to shortest texture height on either side of lines. + sa.DoFloor(line, FloorMoveType.RaiseToTexture); + line.Special = 0; + break; + + case 35: + // Lights very dark. + sa.LightTurnOn(line, 35); + line.Special = 0; + break; + + case 36: + // Lower floor (turbo). + sa.DoFloor(line, FloorMoveType.TurboLower); + line.Special = 0; + break; + + case 37: + // Lower and change. + sa.DoFloor(line, FloorMoveType.LowerAndChange); + line.Special = 0; + break; + + case 38: + // Lower floor to lowest. + sa.DoFloor(line, FloorMoveType.LowerFloorToLowest); + line.Special = 0; + break; + + case 39: + // Do teleport. + sa.Teleport(line, side, thing); + line.Special = 0; + break; + + case 40: + // Raise ceiling and lower floor. + sa.DoCeiling(line, CeilingMoveType.RaiseToHighest); + sa.DoFloor(line, FloorMoveType.LowerFloorToLowest); + line.Special = 0; + break; + + case 44: + // Ceiling crush. + sa.DoCeiling(line, CeilingMoveType.LowerAndCrush); + line.Special = 0; + break; + + case 52: + // Do exit. + world.ExitLevel(); + break; + + case 53: + // Perpetual platform raise. + sa.DoPlatform(line, PlatformType.PerpetualRaise, 0); + line.Special = 0; + break; + + case 54: + // Platform stop. + sa.StopPlatform(line); + line.Special = 0; + break; + + case 56: + // Raise floor crush. + sa.DoFloor(line, FloorMoveType.RaiseFloorCrush); + line.Special = 0; + break; + + case 57: + // Ceiling crush stop. + sa.CeilingCrushStop(line); + line.Special = 0; + break; + + case 58: + // Raise floor 24. + sa.DoFloor(line, FloorMoveType.RaiseFloor24); + line.Special = 0; + break; + + case 59: + // Raise floor 24 and change. + sa.DoFloor(line, FloorMoveType.RaiseFloor24AndChange); + line.Special = 0; + break; + + case 104: + // Turn lights off in sector (tag). + sa.TurnTagLightsOff(line); + line.Special = 0; + break; + + case 108: + // Blazing door raise (faster than turbo). + sa.DoDoor(line, VerticalDoorType.BlazeRaise); + line.Special = 0; + break; + + case 109: + // Blazing door open (faster than turbo). + sa.DoDoor(line, VerticalDoorType.BlazeOpen); + line.Special = 0; + break; + + case 100: + // Build stairs turbo 16. + sa.BuildStairs(line, StairType.Turbo16); + line.Special = 0; + break; + + case 110: + // Blazing door close (faster than turbo). + sa.DoDoor(line, VerticalDoorType.BlazeClose); + line.Special = 0; + break; + + case 119: + // Raise floor to nearest surrounding floor. + sa.DoFloor(line, FloorMoveType.RaiseFloorToNearest); + line.Special = 0; + break; + + case 121: + // Blazing platform down, wait, up and stay. + sa.DoPlatform(line, PlatformType.BlazeDwus, 0); + line.Special = 0; + break; + + case 124: + // Secret exit. + world.SecretExitLevel(); + break; + + case 125: + // Teleport monster only. + if (thing.Player == null) + { + sa.Teleport(line, side, thing); + line.Special = 0; + } + break; + + case 130: + // Raise floor turbo. + sa.DoFloor(line, FloorMoveType.RaiseFloorTurbo); + line.Special = 0; + break; + + case 141: + // Silent ceiling crush and raise. + sa.DoCeiling(line, CeilingMoveType.SilentCrushAndRaise); + line.Special = 0; + break; + + // RETRIGGERS. All from here till end. + case 72: + // Ceiling crush. + sa.DoCeiling(line, CeilingMoveType.LowerAndCrush); + break; + + case 73: + // Ceiling crush and raise. + sa.DoCeiling(line, CeilingMoveType.CrushAndRaise); + break; + + case 74: + // Ceiling crush stop. + sa.CeilingCrushStop(line); + break; + + case 75: + // Close door. + sa.DoDoor(line, VerticalDoorType.Close); + break; + + case 76: + // Close door 30. + sa.DoDoor(line, VerticalDoorType.Close30ThenOpen); + break; + + case 77: + // Fast ceiling crush and raise. + sa.DoCeiling(line, CeilingMoveType.FastCrushAndRaise); + break; + + case 79: + // Lights very dark. + sa.LightTurnOn(line, 35); + break; + + case 80: + // Light turn on - brightest near. + sa.LightTurnOn(line, 0); + break; + + case 81: + // Light turn on 255. + sa.LightTurnOn(line, 255); + break; + + case 82: + // Lower floor to lowest. + sa.DoFloor(line, FloorMoveType.LowerFloorToLowest); + break; + + case 83: + // Lower floor. + sa.DoFloor(line, FloorMoveType.LowerFloor); + break; + + case 84: + // Lower and change. + sa.DoFloor(line, FloorMoveType.LowerAndChange); + break; + + case 86: + // Open door. + sa.DoDoor(line, VerticalDoorType.Open); + break; + + case 87: + // Perpetual platform raise. + sa.DoPlatform(line, PlatformType.PerpetualRaise, 0); + break; + + case 88: + // Platform down, wait, up and stay. + sa.DoPlatform(line, PlatformType.DownWaitUpStay, 0); + break; + + case 89: + // Platform stop. + sa.StopPlatform(line); + break; + + case 90: + // Raise door. + sa.DoDoor(line, VerticalDoorType.Normal); + break; + + case 91: + // Raise floor. + sa.DoFloor(line, FloorMoveType.RaiseFloor); + break; + + case 92: + // Raise floor 24. + sa.DoFloor(line, FloorMoveType.RaiseFloor24); + break; + + case 93: + // Raise floor 24 and change. + sa.DoFloor(line, FloorMoveType.RaiseFloor24AndChange); + break; + + case 94: + // Raise Floor Crush + sa.DoFloor(line, FloorMoveType.RaiseFloorCrush); + break; + + case 95: + // Raise floor to nearest height and change texture. + sa.DoPlatform(line, PlatformType.RaiseToNearestAndChange, 0); + break; + + case 96: + // Raise floor to shortest texture height on either side of lines. + sa.DoFloor(line, FloorMoveType.RaiseToTexture); + break; + + case 97: + // Do Teleport. + sa.Teleport(line, side, thing); + break; + + case 98: + // Lower floor (turbo). + sa.DoFloor(line, FloorMoveType.TurboLower); + break; + + case 105: + // Blazing door raise (faster than turbo). + sa.DoDoor(line, VerticalDoorType.BlazeRaise); + break; + + case 106: + // Blazing door open (faster than turbo). + sa.DoDoor(line, VerticalDoorType.BlazeOpen); + break; + + case 107: + // Blazing door close (faster than turbo). + sa.DoDoor(line, VerticalDoorType.BlazeClose); + break; + + case 120: + // Blazing platform down, wait, up and stay. + sa.DoPlatform(line, PlatformType.BlazeDwus, 0); + break; + + case 126: + // Teleport monster only. + if (thing.Player == null) + { + sa.Teleport(line, side, thing); + } + break; + + case 128: + // Raise to nearest floor. + sa.DoFloor(line, FloorMoveType.RaiseFloorToNearest); + break; + + case 129: + // Raise floor turbo. + sa.DoFloor(line, FloorMoveType.RaiseFloorTurbo); + break; + } + } + + + + //////////////////////////////////////////////////////////// + // Line shoot + //////////////////////////////////////////////////////////// + + /// + /// Called when a thing shoots a special line. + /// + public void ShootSpecialLine(Mobj thing, LineDef line) + { + bool ok; + + // Impacts that other things can activate. + if (thing.Player == null) + { + ok = false; + switch ((int)line.Special) + { + case 46: + // Open door impact. + ok = true; + break; + } + if (!ok) + { + return; + } + } + + var sa = world.SectorAction; + var specials = world.Specials; + + switch ((int)line.Special) + { + case 24: + // Raise floor. + sa.DoFloor(line, FloorMoveType.RaiseFloor); + specials.ChangeSwitchTexture(line, false); + break; + + case 46: + // Open door. + sa.DoDoor(line, VerticalDoorType.Open); + specials.ChangeSwitchTexture(line, true); + break; + + case 47: + // Raise floor near and change. + sa.DoPlatform(line, PlatformType.RaiseToNearestAndChange, 0); + specials.ChangeSwitchTexture(line, false); + break; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Mobj.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Mobj.cs new file mode 100644 index 00000000..60a6504e --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Mobj.cs @@ -0,0 +1,573 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public class Mobj : Thinker + { + // + // NOTES: mobj_t + // + // mobj_ts are used to tell the refresh where to draw an image, + // tell the world simulation when objects are contacted, + // and tell the sound driver how to position a sound. + // + // The refresh uses the next and prev links to follow + // lists of things in sectors as they are being drawn. + // The sprite, frame, and angle elements determine which patch_t + // is used to draw the sprite if it is visible. + // The sprite and frame values are allmost allways set + // from state_t structures. + // The statescr.exe utility generates the states.h and states.c + // files that contain the sprite/frame numbers from the + // statescr.txt source file. + // The xyz origin point represents a point at the bottom middle + // of the sprite (between the feet of a biped). + // This is the default origin position for patch_ts grabbed + // with lumpy.exe. + // A walking creature will have its z equal to the floor + // it is standing on. + // + // The sound code uses the x,y, and subsector fields + // to do stereo positioning of any sound effited by the mobj_t. + // + // The play simulation uses the blocklinks, x,y,z, radius, height + // to determine when mobj_ts are touching each other, + // touching lines in the map, or hit by trace lines (gunshots, + // lines of sight, etc). + // The mobj_t->flags element has various bit flags + // used by the simulation. + // + // Every mobj_t is linked into a single sector + // based on its origin coordinates. + // The subsector_t is found with R_PointInSubsector(x,y), + // and the sector_t can be found with subsector->sector. + // The sector links are only used by the rendering code, + // the play simulation does not care about them at all. + // + // Any mobj_t that needs to be acted upon by something else + // in the play world (block movement, be shot, etc) will also + // need to be linked into the blockmap. + // If the thing has the MF_NOBLOCK flag set, it will not use + // the block links. It can still interact with other things, + // but only as the instigator (missiles will run into other + // things, but nothing can run into a missile). + // Each block in the grid is 128*128 units, and knows about + // every line_t that it contains a piece of, and every + // interactable mobj_t that has its origin contained. + // + // A valid mobj_t is a mobj_t that has the proper subsector_t + // filled in for its xy coordinates and is linked into the + // sector from which the subsector was made, or has the + // MF_NOSECTOR flag set (the subsector_t needs to be valid + // even if MF_NOSECTOR is set), and is linked into a blockmap + // block or has the MF_NOBLOCKMAP flag set. + // Links should only be modified by the P_[Un]SetThingPosition() + // functions. + // Do not change the MF_NO? flags while a thing is valid. + // + // Any questions? + // + + public static readonly Fixed OnFloorZ = Fixed.MinValue; + public static readonly Fixed OnCeilingZ = Fixed.MaxValue; + + private World world; + + // Info for drawing: position. + private Fixed x; + private Fixed y; + private Fixed z; + + // More list: links in sector (if needed). + private Mobj sectorNext; + private Mobj sectorPrev; + + // More drawing info: to determine current sprite. + private Angle angle; // Orientation. + private Sprite sprite; // Used to find patch_t and flip value. + private int frame; // Might be ORed with FF_FULLBRIGHT. + + // Interaction info, by BLOCKMAP. + // Links in blocks (if needed). + private Mobj blockNext; + private Mobj blockPrev; + + private Subsector subsector; + + // The closest interval over all contacted Sectors. + private Fixed floorZ; + private Fixed ceilingZ; + + // For movement checking. + private Fixed radius; + private Fixed height; + + // Momentums, used to update position. + private Fixed momX; + private Fixed momY; + private Fixed momZ; + + // If == validCount, already checked. + private int validCount; + + private MobjType type; + private MobjInfo info; + + private int tics; // State tic counter. + private MobjStateDef state; + private MobjFlags flags; + private int health; + + // Movement direction, movement generation (zig-zagging). + private Direction moveDir; + private int moveCount; // When 0, select a new dir. + + // Thing being chased / attacked (or null), + // also the originator for missiles. + private Mobj target; + + // Reaction time: if non 0, don't attack yet. + // Used by player to freeze a bit after teleporting. + private int reactionTime; + + // If >0, the target will be chased + // no matter what (even if shot). + private int threshold; + + // Additional info record for player avatars only. + // Only valid if type == MT_PLAYER + private Player player; + + // Player number last looked for. + private int lastLook; + + // For nightmare respawn. + private MapThing spawnPoint; + + // Thing being chased/attacked for tracers. + private Mobj tracer; + + public Mobj(World world) + { + this.world = world; + } + + public override void Run() + { + // Momentum movement. + if (momX != Fixed.Zero || momY != Fixed.Zero || + (flags & MobjFlags.SkullFly) != 0) + { + world.ThingMovement.XYMovement(this); + + if (ThinkerState == ThinkerState.Removed) + { + // Mobj was removed. + return; + } + } + + if ((z != floorZ) || momZ != Fixed.Zero) + { + world.ThingMovement.ZMovement(this); + + if (ThinkerState == ThinkerState.Removed) + { + // Mobj was removed. + return; + } + } + + // Cycle through states, + // calling action functions at transitions. + if (tics != -1) + { + tics--; + + // You can cycle through multiple states in a tic. + if (tics == 0) + { + if (!SetState(state.Next)) + { + // Freed itself. + return; + } + } + } + else + { + // Check for nightmare respawn. + if ((flags & MobjFlags.CountKill) == 0) + { + return; + } + + var options = world.Options; + if (!(options.Skill == GameSkill.Nightmare || options.RespawnMonsters)) + { + return; + } + + moveCount++; + + if (moveCount < 12 * 35) + { + return; + } + + if ((world.LevelTime & 31) != 0) + { + return; + } + + if (world.Random.Next() > 4) + { + return; + } + + NightmareRespawn(); + } + } + + public bool SetState(MobjState state) + { + do + { + if (state == MobjState.Null) + { + this.state = DoomInfo.States[(int)MobjState.Null]; + world.ThingAllocation.RemoveMobj(this); + return false; + } + + var st = DoomInfo.States[(int)state]; + this.state = st; + tics = GetTics(st); + sprite = st.Sprite; + frame = st.Frame; + + // Modified handling. + // Call action functions when the state is set. + if (st.MobjAction != null) + { + st.MobjAction(world, this); + } + + state = st.Next; + } + while (tics == 0); + + return true; + } + + private int GetTics(MobjStateDef state) + { + var options = world.Options; + if (options.FastMonsters || options.Skill == GameSkill.Nightmare) + { + if ((int)MobjState.SargRun1 <= state.Number && + state.Number <= (int)MobjState.SargPain2) + { + return state.Tics >> 1; + } + else + { + return state.Tics; + } + } + else + { + return state.Tics; + } + } + + private void NightmareRespawn() + { + MapThing sp; + if (spawnPoint != null) + { + sp = spawnPoint; + } + else + { + sp = MapThing.Empty; + } + + // Somthing is occupying it's position? + if (!world.ThingMovement.CheckPosition(this, sp.X, sp.Y)) + { + // No respwan. + return; + } + + var ta = world.ThingAllocation; + + // Spawn a teleport fog at old spot. + var fog1 = ta.SpawnMobj( + x, y, + subsector.Sector.FloorHeight, + MobjType.Tfog); + + // Initiate teleport sound. + world.StartSound(fog1, Sfx.TELEPT, SfxType.Misc); + + // Spawn a teleport fog at the new spot. + var ss = Geometry.PointInSubsector(sp.X, sp.Y, world.Map); + + var fog2 = ta.SpawnMobj( + sp.X, sp.Y, + ss.Sector.FloorHeight, MobjType.Tfog); + + world.StartSound(fog2, Sfx.TELEPT, SfxType.Misc); + + // Spawn the new monster. + Fixed z; + if ((info.Flags & MobjFlags.SpawnCeiling) != 0) + { + z = OnCeilingZ; + } + else + { + z = OnFloorZ; + } + + // Inherit attributes from deceased one. + var mobj = ta.SpawnMobj(sp.X, sp.Y, z, type); + mobj.SpawnPoint = spawnPoint; + mobj.Angle = sp.Angle; + + if ((sp.Flags & ThingFlags.Ambush) != 0) + { + mobj.Flags |= MobjFlags.Ambush; + } + + mobj.ReactionTime = 18; + + // Remove the old monster. + world.ThingAllocation.RemoveMobj(this); + } + + public World World => world; + + public Fixed X + { + get => x; + set => x = value; + } + + public Fixed Y + { + get => y; + set => y = value; + } + + public Fixed Z + { + get => z; + set => z = value; + } + + public Mobj SectorNext + { + get => sectorNext; + set => sectorNext = value; + } + + public Mobj SectorPrev + { + get => sectorPrev; + set => sectorPrev = value; + } + + public Angle Angle + { + get => angle; + set => angle = value; + } + + public Sprite Sprite + { + get => sprite; + set => sprite = value; + } + + public int Frame + { + get => frame; + set => frame = value; + } + + public Mobj BlockNext + { + get => blockNext; + set => blockNext = value; + } + + public Mobj BlockPrev + { + get => blockPrev; + set => blockPrev = value; + } + + public Subsector Subsector + { + get => subsector; + set => subsector = value; + } + + public Fixed FloorZ + { + get => floorZ; + set => floorZ = value; + } + + public Fixed CeilingZ + { + get => ceilingZ; + set => ceilingZ = value; + } + + public Fixed Radius + { + get => radius; + set => radius = value; + } + + public Fixed Height + { + get => height; + set => height = value; + } + + public Fixed MomX + { + get => momX; + set => momX = value; + } + + public Fixed MomY + { + get => momY; + set => momY = value; + } + + public Fixed MomZ + { + get => momZ; + set => momZ = value; + } + + public int ValidCount + { + get => validCount; + set => validCount = value; + } + + public MobjType Type + { + get => type; + set => type = value; + } + + public MobjInfo Info + { + get => info; + set => info = value; + } + + public int Tics + { + get => tics; + set => tics = value; + } + + public MobjStateDef State + { + get => state; + set => state = value; + } + + public MobjFlags Flags + { + get => flags; + set => flags = value; + } + + public int Health + { + get => health; + set => health = value; + } + + public Direction MoveDir + { + get => moveDir; + set => moveDir = value; + } + + public int MoveCount + { + get => moveCount; + set => moveCount = value; + } + + public Mobj Target + { + get => target; + set => target = value; + } + + public int ReactionTime + { + get => reactionTime; + set => reactionTime = value; + } + + public int Threshold + { + get => threshold; + set => threshold = value; + } + + public Player Player + { + get => player; + set => player = value; + } + + public int LastLook + { + get => lastLook; + set => lastLook = value; + } + + public MapThing SpawnPoint + { + get => spawnPoint; + set => spawnPoint = value; + } + + public Mobj Tracer + { + get => tracer; + set => tracer = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjFlags.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjFlags.cs new file mode 100644 index 00000000..61d95dac --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjFlags.cs @@ -0,0 +1,129 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + [Flags] + public enum MobjFlags + { + // Call P_SpecialThing when touched. + Special = 1, + + // Blocks. + Solid = 2, + + // Can be hit. + Shootable = 4, + + // Don't use the sector links (invisible but touchable). + NoSector = 8, + + // Don't use the blocklinks (inert but displayable). + NoBlockMap = 16, + + // Not to be activated by sound, deaf monster. + Ambush = 32, + + // Will try to attack right back. + JustHit = 64, + + // Will take at least one step before attacking. + JustAttacked = 128, + + // On level spawning (initial position), + // hang from ceiling instead of stand on floor. + SpawnCeiling = 256, + + // Don't apply gravity (every tic), + // that is, object will float, keeping current height + // or changing it actively. + NoGravity = 512, + + // Movement flags. + // This allows jumps from high places. + DropOff = 0x400, + + // For players, will pick up items. + PickUp = 0x800, + + // Player cheat. ??? + NoClip = 0x1000, + + // Player: keep info about sliding along walls. + Slide = 0x2000, + + // Allow moves to any height, no gravity. + // For active floaters, e.g. cacodemons, pain elementals. + Float = 0x4000, + + // Don't cross lines + // ??? or look at heights on teleport. + Teleport = 0x8000, + + // Don't hit same species, explode on block. + // Player missiles as well as fireballs of various kinds. + Missile = 0x10000, + + // Dropped by a demon, not level spawned. + // E.g. ammo clips dropped by dying former humans. + Dropped = 0x20000, + + // Use fuzzy draw (shadow demons or spectres), + // temporary player invisibility powerup. + Shadow = 0x40000, + + // Flag: don't bleed when shot (use puff), + // barrels and shootable furniture shall not bleed. + NoBlood = 0x80000, + + // Don't stop moving halfway off a step, + // that is, have dead bodies slide down all the way. + Corpse = 0x100000, + + // Floating to a height for a move, ??? + // don't auto float to target's height. + InFloat = 0x200000, + + // On kill, count this enemy object + // towards intermission kill total. + // Happy gathering. + CountKill = 0x400000, + + // On picking up, count this item object + // towards intermission item total. + CountItem = 0x800000, + + // Special handling: skull in flight. + // Neither a cacodemon nor a missile. + SkullFly = 0x1000000, + + // Don't spawn this object + // in death match mode (e.g. key cards). + NotDeathmatch = 0x2000000, + + // Player sprites in multiplayer modes are modified + // using an internal color lookup table for re-indexing. + // If 0x4 0x8 or 0xc, + // use a translation table for player colormaps + Translation = 0xc000000, + + // Hmm ???. + TransShift = 26 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjInfo.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjInfo.cs new file mode 100644 index 00000000..0ba50fdb --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjInfo.cs @@ -0,0 +1,236 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class MobjInfo + { + private int doomEdNum; + private MobjState spawnState; + private int spawnHealth; + private MobjState seeState; + private Sfx seeSound; + private int reactionTime; + private Sfx attackSound; + private MobjState painState; + private int painChance; + private Sfx painSound; + private MobjState meleeState; + private MobjState missileState; + private MobjState deathState; + private MobjState xdeathState; + private Sfx deathSound; + private int speed; + private Fixed radius; + private Fixed height; + private int mass; + private int damage; + private Sfx activeSound; + private MobjFlags flags; + private MobjState raiseState; + + public MobjInfo( + int doomEdNum, + MobjState spawnState, + int spawnHealth, + MobjState seeState, + Sfx seeSound, + int reactionTime, + Sfx attackSound, + MobjState painState, + int painChance, + Sfx painSound, + MobjState meleeState, + MobjState missileState, + MobjState deathState, + MobjState xdeathState, + Sfx deathSound, + int speed, + Fixed radius, + Fixed height, + int mass, + int damage, + Sfx activeSound, + MobjFlags flags, + MobjState raiseState) + { + this.doomEdNum = doomEdNum; + this.spawnState = spawnState; + this.spawnHealth = spawnHealth; + this.seeState = seeState; + this.seeSound = seeSound; + this.reactionTime = reactionTime; + this.attackSound = attackSound; + this.painState = painState; + this.painChance = painChance; + this.painSound = painSound; + this.meleeState = meleeState; + this.missileState = missileState; + this.deathState = deathState; + this.xdeathState = xdeathState; + this.deathSound = deathSound; + this.speed = speed; + this.radius = radius; + this.height = height; + this.mass = mass; + this.damage = damage; + this.activeSound = activeSound; + this.flags = flags; + this.raiseState = raiseState; + } + + public int DoomEdNum + { + get => doomEdNum; + set => doomEdNum = value; + } + + public MobjState SpawnState + { + get => spawnState; + set => spawnState = value; + } + + public int SpawnHealth + { + get => spawnHealth; + set => spawnHealth = value; + } + + public MobjState SeeState + { + get => seeState; + set => seeState = value; + } + + public Sfx SeeSound + { + get => seeSound; + set => seeSound = value; + } + + public int ReactionTime + { + get => reactionTime; + set => reactionTime = value; + } + + public Sfx AttackSound + { + get => attackSound; + set => attackSound = value; + } + + public MobjState PainState + { + get => painState; + set => painState = value; + } + + public int PainChance + { + get => painChance; + set => painChance = value; + } + + public Sfx PainSound + { + get => painSound; + set => painSound = value; + } + + public MobjState MeleeState + { + get => meleeState; + set => meleeState = value; + } + + public MobjState MissileState + { + get => missileState; + set => missileState = value; + } + + public MobjState DeathState + { + get => deathState; + set => deathState = value; + } + + public MobjState XdeathState + { + get => xdeathState; + set => xdeathState = value; + } + + public Sfx DeathSound + { + get => deathSound; + set => deathSound = value; + } + + public int Speed + { + get => speed; + set => speed = value; + } + + public Fixed Radius + { + get => radius; + set => radius = value; + } + + public Fixed Height + { + get => height; + set => height = value; + } + + public int Mass + { + get => mass; + set => mass = value; + } + + public int Damage + { + get => damage; + set => damage = value; + } + + public Sfx ActiveSound + { + get => activeSound; + set => activeSound = value; + } + + public MobjFlags Flags + { + get => flags; + set => flags = value; + } + + public MobjState Raisestate + { + get => raiseState; + set => raiseState = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjState.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjState.cs new file mode 100644 index 00000000..b5b31197 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjState.cs @@ -0,0 +1,992 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum MobjState + { + Null, + Lightdone, + Punch, + Punchdown, + Punchup, + Punch1, + Punch2, + Punch3, + Punch4, + Punch5, + Pistol, + Pistoldown, + Pistolup, + Pistol1, + Pistol2, + Pistol3, + Pistol4, + Pistolflash, + Sgun, + Sgundown, + Sgunup, + Sgun1, + Sgun2, + Sgun3, + Sgun4, + Sgun5, + Sgun6, + Sgun7, + Sgun8, + Sgun9, + Sgunflash1, + Sgunflash2, + Dsgun, + Dsgundown, + Dsgunup, + Dsgun1, + Dsgun2, + Dsgun3, + Dsgun4, + Dsgun5, + Dsgun6, + Dsgun7, + Dsgun8, + Dsgun9, + Dsgun10, + Dsnr1, + Dsnr2, + Dsgunflash1, + Dsgunflash2, + Chain, + Chaindown, + Chainup, + Chain1, + Chain2, + Chain3, + Chainflash1, + Chainflash2, + Missile, + Missiledown, + Missileup, + Missile1, + Missile2, + Missile3, + Missileflash1, + Missileflash2, + Missileflash3, + Missileflash4, + Saw, + Sawb, + Sawdown, + Sawup, + Saw1, + Saw2, + Saw3, + Plasma, + Plasmadown, + Plasmaup, + Plasma1, + Plasma2, + Plasmaflash1, + Plasmaflash2, + Bfg, + Bfgdown, + Bfgup, + Bfg1, + Bfg2, + Bfg3, + Bfg4, + Bfgflash1, + Bfgflash2, + Blood1, + Blood2, + Blood3, + Puff1, + Puff2, + Puff3, + Puff4, + Tball1, + Tball2, + Tballx1, + Tballx2, + Tballx3, + Rball1, + Rball2, + Rballx1, + Rballx2, + Rballx3, + Plasball, + Plasball2, + Plasexp, + Plasexp2, + Plasexp3, + Plasexp4, + Plasexp5, + Rocket, + Bfgshot, + Bfgshot2, + Bfgland, + Bfgland2, + Bfgland3, + Bfgland4, + Bfgland5, + Bfgland6, + Bfgexp, + Bfgexp2, + Bfgexp3, + Bfgexp4, + Explode1, + Explode2, + Explode3, + Tfog, + Tfog01, + Tfog02, + Tfog2, + Tfog3, + Tfog4, + Tfog5, + Tfog6, + Tfog7, + Tfog8, + Tfog9, + Tfog10, + Ifog, + Ifog01, + Ifog02, + Ifog2, + Ifog3, + Ifog4, + Ifog5, + Play, + PlayRun1, + PlayRun2, + PlayRun3, + PlayRun4, + PlayAtk1, + PlayAtk2, + PlayPain, + PlayPain2, + PlayDie1, + PlayDie2, + PlayDie3, + PlayDie4, + PlayDie5, + PlayDie6, + PlayDie7, + PlayXdie1, + PlayXdie2, + PlayXdie3, + PlayXdie4, + PlayXdie5, + PlayXdie6, + PlayXdie7, + PlayXdie8, + PlayXdie9, + PossStnd, + PossStnd2, + PossRun1, + PossRun2, + PossRun3, + PossRun4, + PossRun5, + PossRun6, + PossRun7, + PossRun8, + PossAtk1, + PossAtk2, + PossAtk3, + PossPain, + PossPain2, + PossDie1, + PossDie2, + PossDie3, + PossDie4, + PossDie5, + PossXdie1, + PossXdie2, + PossXdie3, + PossXdie4, + PossXdie5, + PossXdie6, + PossXdie7, + PossXdie8, + PossXdie9, + PossRaise1, + PossRaise2, + PossRaise3, + PossRaise4, + SposStnd, + SposStnd2, + SposRun1, + SposRun2, + SposRun3, + SposRun4, + SposRun5, + SposRun6, + SposRun7, + SposRun8, + SposAtk1, + SposAtk2, + SposAtk3, + SposPain, + SposPain2, + SposDie1, + SposDie2, + SposDie3, + SposDie4, + SposDie5, + SposXdie1, + SposXdie2, + SposXdie3, + SposXdie4, + SposXdie5, + SposXdie6, + SposXdie7, + SposXdie8, + SposXdie9, + SposRaise1, + SposRaise2, + SposRaise3, + SposRaise4, + SposRaise5, + VileStnd, + VileStnd2, + VileRun1, + VileRun2, + VileRun3, + VileRun4, + VileRun5, + VileRun6, + VileRun7, + VileRun8, + VileRun9, + VileRun10, + VileRun11, + VileRun12, + VileAtk1, + VileAtk2, + VileAtk3, + VileAtk4, + VileAtk5, + VileAtk6, + VileAtk7, + VileAtk8, + VileAtk9, + VileAtk10, + VileAtk11, + VileHeal1, + VileHeal2, + VileHeal3, + VilePain, + VilePain2, + VileDie1, + VileDie2, + VileDie3, + VileDie4, + VileDie5, + VileDie6, + VileDie7, + VileDie8, + VileDie9, + VileDie10, + Fire1, + Fire2, + Fire3, + Fire4, + Fire5, + Fire6, + Fire7, + Fire8, + Fire9, + Fire10, + Fire11, + Fire12, + Fire13, + Fire14, + Fire15, + Fire16, + Fire17, + Fire18, + Fire19, + Fire20, + Fire21, + Fire22, + Fire23, + Fire24, + Fire25, + Fire26, + Fire27, + Fire28, + Fire29, + Fire30, + Smoke1, + Smoke2, + Smoke3, + Smoke4, + Smoke5, + Tracer, + Tracer2, + Traceexp1, + Traceexp2, + Traceexp3, + SkelStnd, + SkelStnd2, + SkelRun1, + SkelRun2, + SkelRun3, + SkelRun4, + SkelRun5, + SkelRun6, + SkelRun7, + SkelRun8, + SkelRun9, + SkelRun10, + SkelRun11, + SkelRun12, + SkelFist1, + SkelFist2, + SkelFist3, + SkelFist4, + SkelMiss1, + SkelMiss2, + SkelMiss3, + SkelMiss4, + SkelPain, + SkelPain2, + SkelDie1, + SkelDie2, + SkelDie3, + SkelDie4, + SkelDie5, + SkelDie6, + SkelRaise1, + SkelRaise2, + SkelRaise3, + SkelRaise4, + SkelRaise5, + SkelRaise6, + Fatshot1, + Fatshot2, + Fatshotx1, + Fatshotx2, + Fatshotx3, + FattStnd, + FattStnd2, + FattRun1, + FattRun2, + FattRun3, + FattRun4, + FattRun5, + FattRun6, + FattRun7, + FattRun8, + FattRun9, + FattRun10, + FattRun11, + FattRun12, + FattAtk1, + FattAtk2, + FattAtk3, + FattAtk4, + FattAtk5, + FattAtk6, + FattAtk7, + FattAtk8, + FattAtk9, + FattAtk10, + FattPain, + FattPain2, + FattDie1, + FattDie2, + FattDie3, + FattDie4, + FattDie5, + FattDie6, + FattDie7, + FattDie8, + FattDie9, + FattDie10, + FattRaise1, + FattRaise2, + FattRaise3, + FattRaise4, + FattRaise5, + FattRaise6, + FattRaise7, + FattRaise8, + CposStnd, + CposStnd2, + CposRun1, + CposRun2, + CposRun3, + CposRun4, + CposRun5, + CposRun6, + CposRun7, + CposRun8, + CposAtk1, + CposAtk2, + CposAtk3, + CposAtk4, + CposPain, + CposPain2, + CposDie1, + CposDie2, + CposDie3, + CposDie4, + CposDie5, + CposDie6, + CposDie7, + CposXdie1, + CposXdie2, + CposXdie3, + CposXdie4, + CposXdie5, + CposXdie6, + CposRaise1, + CposRaise2, + CposRaise3, + CposRaise4, + CposRaise5, + CposRaise6, + CposRaise7, + TrooStnd, + TrooStnd2, + TrooRun1, + TrooRun2, + TrooRun3, + TrooRun4, + TrooRun5, + TrooRun6, + TrooRun7, + TrooRun8, + TrooAtk1, + TrooAtk2, + TrooAtk3, + TrooPain, + TrooPain2, + TrooDie1, + TrooDie2, + TrooDie3, + TrooDie4, + TrooDie5, + TrooXdie1, + TrooXdie2, + TrooXdie3, + TrooXdie4, + TrooXdie5, + TrooXdie6, + TrooXdie7, + TrooXdie8, + TrooRaise1, + TrooRaise2, + TrooRaise3, + TrooRaise4, + TrooRaise5, + SargStnd, + SargStnd2, + SargRun1, + SargRun2, + SargRun3, + SargRun4, + SargRun5, + SargRun6, + SargRun7, + SargRun8, + SargAtk1, + SargAtk2, + SargAtk3, + SargPain, + SargPain2, + SargDie1, + SargDie2, + SargDie3, + SargDie4, + SargDie5, + SargDie6, + SargRaise1, + SargRaise2, + SargRaise3, + SargRaise4, + SargRaise5, + SargRaise6, + HeadStnd, + HeadRun1, + HeadAtk1, + HeadAtk2, + HeadAtk3, + HeadPain, + HeadPain2, + HeadPain3, + HeadDie1, + HeadDie2, + HeadDie3, + HeadDie4, + HeadDie5, + HeadDie6, + HeadRaise1, + HeadRaise2, + HeadRaise3, + HeadRaise4, + HeadRaise5, + HeadRaise6, + Brball1, + Brball2, + Brballx1, + Brballx2, + Brballx3, + BossStnd, + BossStnd2, + BossRun1, + BossRun2, + BossRun3, + BossRun4, + BossRun5, + BossRun6, + BossRun7, + BossRun8, + BossAtk1, + BossAtk2, + BossAtk3, + BossPain, + BossPain2, + BossDie1, + BossDie2, + BossDie3, + BossDie4, + BossDie5, + BossDie6, + BossDie7, + BossRaise1, + BossRaise2, + BossRaise3, + BossRaise4, + BossRaise5, + BossRaise6, + BossRaise7, + Bos2Stnd, + Bos2Stnd2, + Bos2Run1, + Bos2Run2, + Bos2Run3, + Bos2Run4, + Bos2Run5, + Bos2Run6, + Bos2Run7, + Bos2Run8, + Bos2Atk1, + Bos2Atk2, + Bos2Atk3, + Bos2Pain, + Bos2Pain2, + Bos2Die1, + Bos2Die2, + Bos2Die3, + Bos2Die4, + Bos2Die5, + Bos2Die6, + Bos2Die7, + Bos2Raise1, + Bos2Raise2, + Bos2Raise3, + Bos2Raise4, + Bos2Raise5, + Bos2Raise6, + Bos2Raise7, + SkullStnd, + SkullStnd2, + SkullRun1, + SkullRun2, + SkullAtk1, + SkullAtk2, + SkullAtk3, + SkullAtk4, + SkullPain, + SkullPain2, + SkullDie1, + SkullDie2, + SkullDie3, + SkullDie4, + SkullDie5, + SkullDie6, + SpidStnd, + SpidStnd2, + SpidRun1, + SpidRun2, + SpidRun3, + SpidRun4, + SpidRun5, + SpidRun6, + SpidRun7, + SpidRun8, + SpidRun9, + SpidRun10, + SpidRun11, + SpidRun12, + SpidAtk1, + SpidAtk2, + SpidAtk3, + SpidAtk4, + SpidPain, + SpidPain2, + SpidDie1, + SpidDie2, + SpidDie3, + SpidDie4, + SpidDie5, + SpidDie6, + SpidDie7, + SpidDie8, + SpidDie9, + SpidDie10, + SpidDie11, + BspiStnd, + BspiStnd2, + BspiSight, + BspiRun1, + BspiRun2, + BspiRun3, + BspiRun4, + BspiRun5, + BspiRun6, + BspiRun7, + BspiRun8, + BspiRun9, + BspiRun10, + BspiRun11, + BspiRun12, + BspiAtk1, + BspiAtk2, + BspiAtk3, + BspiAtk4, + BspiPain, + BspiPain2, + BspiDie1, + BspiDie2, + BspiDie3, + BspiDie4, + BspiDie5, + BspiDie6, + BspiDie7, + BspiRaise1, + BspiRaise2, + BspiRaise3, + BspiRaise4, + BspiRaise5, + BspiRaise6, + BspiRaise7, + ArachPlaz, + ArachPlaz2, + ArachPlex, + ArachPlex2, + ArachPlex3, + ArachPlex4, + ArachPlex5, + CyberStnd, + CyberStnd2, + CyberRun1, + CyberRun2, + CyberRun3, + CyberRun4, + CyberRun5, + CyberRun6, + CyberRun7, + CyberRun8, + CyberAtk1, + CyberAtk2, + CyberAtk3, + CyberAtk4, + CyberAtk5, + CyberAtk6, + CyberPain, + CyberDie1, + CyberDie2, + CyberDie3, + CyberDie4, + CyberDie5, + CyberDie6, + CyberDie7, + CyberDie8, + CyberDie9, + CyberDie10, + PainStnd, + PainRun1, + PainRun2, + PainRun3, + PainRun4, + PainRun5, + PainRun6, + PainAtk1, + PainAtk2, + PainAtk3, + PainAtk4, + PainPain, + PainPain2, + PainDie1, + PainDie2, + PainDie3, + PainDie4, + PainDie5, + PainDie6, + PainRaise1, + PainRaise2, + PainRaise3, + PainRaise4, + PainRaise5, + PainRaise6, + SswvStnd, + SswvStnd2, + SswvRun1, + SswvRun2, + SswvRun3, + SswvRun4, + SswvRun5, + SswvRun6, + SswvRun7, + SswvRun8, + SswvAtk1, + SswvAtk2, + SswvAtk3, + SswvAtk4, + SswvAtk5, + SswvAtk6, + SswvPain, + SswvPain2, + SswvDie1, + SswvDie2, + SswvDie3, + SswvDie4, + SswvDie5, + SswvXdie1, + SswvXdie2, + SswvXdie3, + SswvXdie4, + SswvXdie5, + SswvXdie6, + SswvXdie7, + SswvXdie8, + SswvXdie9, + SswvRaise1, + SswvRaise2, + SswvRaise3, + SswvRaise4, + SswvRaise5, + Keenstnd, + Commkeen, + Commkeen2, + Commkeen3, + Commkeen4, + Commkeen5, + Commkeen6, + Commkeen7, + Commkeen8, + Commkeen9, + Commkeen10, + Commkeen11, + Commkeen12, + Keenpain, + Keenpain2, + Brain, + BrainPain, + BrainDie1, + BrainDie2, + BrainDie3, + BrainDie4, + Braineye, + Braineyesee, + Braineye1, + Spawn1, + Spawn2, + Spawn3, + Spawn4, + Spawnfire1, + Spawnfire2, + Spawnfire3, + Spawnfire4, + Spawnfire5, + Spawnfire6, + Spawnfire7, + Spawnfire8, + Brainexplode1, + Brainexplode2, + Brainexplode3, + Arm1, + Arm1A, + Arm2, + Arm2A, + Bar1, + Bar2, + Bexp, + Bexp2, + Bexp3, + Bexp4, + Bexp5, + Bbar1, + Bbar2, + Bbar3, + Bon1, + Bon1A, + Bon1B, + Bon1C, + Bon1D, + Bon1E, + Bon2, + Bon2A, + Bon2B, + Bon2C, + Bon2D, + Bon2E, + Bkey, + Bkey2, + Rkey, + Rkey2, + Ykey, + Ykey2, + Bskull, + Bskull2, + Rskull, + Rskull2, + Yskull, + Yskull2, + Stim, + Medi, + Soul, + Soul2, + Soul3, + Soul4, + Soul5, + Soul6, + Pinv, + Pinv2, + Pinv3, + Pinv4, + Pstr, + Pins, + Pins2, + Pins3, + Pins4, + Mega, + Mega2, + Mega3, + Mega4, + Suit, + Pmap, + Pmap2, + Pmap3, + Pmap4, + Pmap5, + Pmap6, + Pvis, + Pvis2, + Clip, + Ammo, + Rock, + Brok, + Cell, + Celp, + Shel, + Sbox, + Bpak, + Bfug, + Mgun, + Csaw, + Laun, + Plas, + Shot, + Shot2, + Colu, + Stalag, + Bloodytwitch, + Bloodytwitch2, + Bloodytwitch3, + Bloodytwitch4, + Deadtorso, + Deadbottom, + Headsonstick, + Gibs, + Headonastick, + Headcandles, + Headcandles2, + Deadstick, + Livestick, + Livestick2, + Meat2, + Meat3, + Meat4, + Meat5, + Stalagtite, + Tallgrncol, + Shrtgrncol, + Tallredcol, + Shrtredcol, + Candlestik, + Candelabra, + Skullcol, + Torchtree, + Bigtree, + Techpillar, + Evileye, + Evileye2, + Evileye3, + Evileye4, + Floatskull, + Floatskull2, + Floatskull3, + Heartcol, + Heartcol2, + Bluetorch, + Bluetorch2, + Bluetorch3, + Bluetorch4, + Greentorch, + Greentorch2, + Greentorch3, + Greentorch4, + Redtorch, + Redtorch2, + Redtorch3, + Redtorch4, + Btorchshrt, + Btorchshrt2, + Btorchshrt3, + Btorchshrt4, + Gtorchshrt, + Gtorchshrt2, + Gtorchshrt3, + Gtorchshrt4, + Rtorchshrt, + Rtorchshrt2, + Rtorchshrt3, + Rtorchshrt4, + Hangnoguts, + Hangbnobrain, + Hangtlookdn, + Hangtskull, + Hangtlookup, + Hangtnobrain, + Colongibs, + Smallpool, + Brainstem, + Techlamp, + Techlamp2, + Techlamp3, + Techlamp4, + Tech2Lamp, + Tech2Lamp2, + Tech2Lamp3, + Tech2Lamp4 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjStateDef.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjStateDef.cs new file mode 100644 index 00000000..69a67b4d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjStateDef.cs @@ -0,0 +1,110 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public class MobjStateDef + { + private int number; + private Sprite sprite; + private int frame; + private int tics; + private Action playerAction; + private Action mobjAction; + private MobjState next; + private int misc1; + private int misc2; + + public MobjStateDef( + int number, + Sprite sprite, + int frame, + int tics, + Action playerAction, + Action mobjAction, + MobjState next, + int misc1, + int misc2) + { + this.number = number; + this.sprite = sprite; + this.frame = frame; + this.tics = tics; + this.playerAction = playerAction; + this.mobjAction = mobjAction; + this.next = next; + this.misc1 = misc1; + this.misc2 = misc2; + } + + public int Number + { + get => number; + set => number = value; + } + + public Sprite Sprite + { + get => sprite; + set => sprite = value; + } + + public int Frame + { + get => frame; + set => frame = value; + } + + public int Tics + { + get => tics; + set => tics = value; + } + + public Action PlayerAction + { + get => playerAction; + set => playerAction = value; + } + + public Action MobjAction + { + get => mobjAction; + set => mobjAction = value; + } + + public MobjState Next + { + get => next; + set => next = value; + } + + public int Misc1 + { + get => misc1; + set => misc1 = value; + } + + public int Misc2 + { + get => misc2; + set => misc2 = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjType.cs new file mode 100644 index 00000000..dfa1cefc --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MobjType.cs @@ -0,0 +1,162 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum MobjType + { + Player, + Possessed, + Shotguy, + Vile, + Fire, + Undead, + Tracer, + Smoke, + Fatso, + Fatshot, + Chainguy, + Troop, + Sergeant, + Shadows, + Head, + Bruiser, + Bruisershot, + Knight, + Skull, + Spider, + Baby, + Cyborg, + Pain, + Wolfss, + Keen, + Bossbrain, + Bossspit, + Bosstarget, + Spawnshot, + Spawnfire, + Barrel, + Troopshot, + Headshot, + Rocket, + Plasma, + Bfg, + Arachplaz, + Puff, + Blood, + Tfog, + Ifog, + Teleportman, + Extrabfg, + Misc0, + Misc1, + Misc2, + Misc3, + Misc4, + Misc5, + Misc6, + Misc7, + Misc8, + Misc9, + Misc10, + Misc11, + Misc12, + Inv, + Misc13, + Ins, + Misc14, + Misc15, + Misc16, + Mega, + Clip, + Misc17, + Misc18, + Misc19, + Misc20, + Misc21, + Misc22, + Misc23, + Misc24, + Misc25, + Chaingun, + Misc26, + Misc27, + Misc28, + Shotgun, + Supershotgun, + Misc29, + Misc30, + Misc31, + Misc32, + Misc33, + Misc34, + Misc35, + Misc36, + Misc37, + Misc38, + Misc39, + Misc40, + Misc41, + Misc42, + Misc43, + Misc44, + Misc45, + Misc46, + Misc47, + Misc48, + Misc49, + Misc50, + Misc51, + Misc52, + Misc53, + Misc54, + Misc55, + Misc56, + Misc57, + Misc58, + Misc59, + Misc60, + Misc61, + Misc62, + Misc63, + Misc64, + Misc65, + Misc66, + Misc67, + Misc68, + Misc69, + Misc70, + Misc71, + Misc72, + Misc73, + Misc74, + Misc75, + Misc76, + Misc77, + Misc78, + Misc79, + Misc80, + Misc81, + Misc82, + Misc83, + Misc84, + Misc85, + Misc86 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MonsterBehavior.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MonsterBehavior.cs new file mode 100644 index 00000000..bee7abb9 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/MonsterBehavior.cs @@ -0,0 +1,2023 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class MonsterBehavior + { + private World world; + + public MonsterBehavior(World world) + { + this.world = world; + + InitVile(); + InitBossDeath(); + InitBrain(); + } + + + + //////////////////////////////////////////////////////////// + // Sleeping monster + //////////////////////////////////////////////////////////// + + private bool LookForPlayers(Mobj actor, bool allAround) + { + var players = world.Options.Players; + + var count = 0; + var stop = (actor.LastLook - 1) & 3; + + for (; ; actor.LastLook = (actor.LastLook + 1) & 3) + { + if (!players[actor.LastLook].InGame) + { + continue; + } + + if (count++ == 2 || actor.LastLook == stop) + { + // Done looking. + return false; + } + + var player = players[actor.LastLook]; + + if (player.Health <= 0) + { + // Player is dead. + continue; + } + + if (!world.VisibilityCheck.CheckSight(actor, player.Mobj)) + { + // Out of sight. + continue; + } + + if (!allAround) + { + var angle = Geometry.PointToAngle( + actor.X, actor.Y, + player.Mobj.X, player.Mobj.Y) - actor.Angle; + + if (angle > Angle.Ang90 && angle < Angle.Ang270) + { + var dist = Geometry.AproxDistance( + player.Mobj.X - actor.X, + player.Mobj.Y - actor.Y); + + // If real close, react anyway. + if (dist > WeaponBehavior.MeleeRange) + { + // Behind back. + continue; + } + } + } + + actor.Target = player.Mobj; + + return true; + } + } + + + public void Look(Mobj actor) + { + // Any shot will wake up. + actor.Threshold = 0; + + var target = actor.Subsector.Sector.SoundTarget; + + if (target != null && (target.Flags & MobjFlags.Shootable) != 0) + { + actor.Target = target; + + if ((actor.Flags & MobjFlags.Ambush) != 0) + { + if (world.VisibilityCheck.CheckSight(actor, actor.Target)) + { + goto seeYou; + } + } + else + { + goto seeYou; + } + } + + if (!LookForPlayers(actor, false)) + { + return; + } + + // Go into chase state. + seeYou: + if (actor.Info.SeeSound != 0) + { + int sound; + + switch (actor.Info.SeeSound) + { + case Sfx.POSIT1: + case Sfx.POSIT2: + case Sfx.POSIT3: + sound = (int)Sfx.POSIT1 + world.Random.Next() % 3; + break; + + case Sfx.BGSIT1: + case Sfx.BGSIT2: + sound = (int)Sfx.BGSIT1 + world.Random.Next() % 2; + break; + + default: + sound = (int)actor.Info.SeeSound; + break; + } + + if (actor.Type == MobjType.Spider || actor.Type == MobjType.Cyborg) + { + // Full volume for boss monsters. + world.StartSound(actor, (Sfx)sound, SfxType.Diffuse); + } + else + { + world.StartSound(actor, (Sfx)sound, SfxType.Voice); + } + } + + actor.SetState(actor.Info.SeeState); + } + + + + //////////////////////////////////////////////////////////// + // Monster AI + //////////////////////////////////////////////////////////// + + private static readonly Fixed[] xSpeed = + { + new Fixed(Fixed.FracUnit), + new Fixed(47000), + new Fixed(0), + new Fixed(-47000), + new Fixed(-Fixed.FracUnit), + new Fixed(-47000), + new Fixed(0), + new Fixed(47000) + }; + + private static readonly Fixed[] ySpeed = + { + new Fixed(0), + new Fixed(47000), + new Fixed(Fixed.FracUnit), + new Fixed(47000), + new Fixed(0), + new Fixed(-47000), + new Fixed(-Fixed.FracUnit), + new Fixed(-47000) + }; + + private bool Move(Mobj actor) + { + if (actor.MoveDir == Direction.None) + { + return false; + } + + if ((int)actor.MoveDir >= 8) + { + throw new Exception("Weird actor->movedir!"); + } + + var tryX = actor.X + actor.Info.Speed * xSpeed[(int)actor.MoveDir]; + var tryY = actor.Y + actor.Info.Speed * ySpeed[(int)actor.MoveDir]; + + var tm = world.ThingMovement; + + var tryOk = tm.TryMove(actor, tryX, tryY); + + if (!tryOk) + { + // Open any specials. + if ((actor.Flags & MobjFlags.Float) != 0 && tm.FloatOk) + { + // Must adjust height. + if (actor.Z < tm.CurrentFloorZ) + { + actor.Z += ThingMovement.FloatSpeed; + } + else + { + actor.Z -= ThingMovement.FloatSpeed; + } + + actor.Flags |= MobjFlags.InFloat; + + return true; + } + + if (tm.crossedSpecialCount == 0) + { + return false; + } + + actor.MoveDir = Direction.None; + var good = false; + while (tm.crossedSpecialCount-- > 0) + { + var line = tm.crossedSpecials[tm.crossedSpecialCount]; + // If the special is not a door that can be opened, + // return false. + if (world.MapInteraction.UseSpecialLine(actor, line, 0)) + { + good = true; + } + } + return good; + } + else + { + actor.Flags &= ~MobjFlags.InFloat; + } + + if ((actor.Flags & MobjFlags.Float) == 0) + { + actor.Z = actor.FloorZ; + } + + return true; + } + + + private bool TryWalk(Mobj actor) + { + if (!Move(actor)) + { + return false; + } + + actor.MoveCount = world.Random.Next() & 15; + + return true; + } + + + private static readonly Direction[] opposite = + { + Direction.west, + Direction.Southwest, + Direction.South, + Direction.Southeast, + Direction.East, + Direction.Northeast, + Direction.North, + Direction.Northwest, + Direction.None + }; + + private static readonly Direction[] diags = + { + Direction.Northwest, + Direction.Northeast, + Direction.Southwest, + Direction.Southeast + }; + + private readonly Direction[] choices = new Direction[3]; + + private void NewChaseDir(Mobj actor) + { + if (actor.Target == null) + { + throw new Exception("Called with no target."); + } + + var oldDir = actor.MoveDir; + var turnAround = opposite[(int)oldDir]; + + var deltaX = actor.Target.X - actor.X; + var deltaY = actor.Target.Y - actor.Y; + + if (deltaX > Fixed.FromInt(10)) + { + choices[1] = Direction.East; + } + else if (deltaX < Fixed.FromInt(-10)) + { + choices[1] = Direction.west; + } + else + { + choices[1] = Direction.None; + } + + if (deltaY < Fixed.FromInt(-10)) + { + choices[2] = Direction.South; + } + else if (deltaY > Fixed.FromInt(10)) + { + choices[2] = Direction.North; + } + else + { + choices[2] = Direction.None; + } + + // Try direct route. + if (choices[1] != Direction.None && choices[2] != Direction.None) + { + var a = (deltaY < Fixed.Zero) ? 1 : 0; + var b = (deltaX > Fixed.Zero) ? 1 : 0; + actor.MoveDir = diags[(a << 1) + b]; + + if (actor.MoveDir != turnAround && TryWalk(actor)) + { + return; + } + } + + // Try other directions. + if (world.Random.Next() > 200 || Fixed.Abs(deltaY) > Fixed.Abs(deltaX)) + { + var temp = choices[1]; + choices[1] = choices[2]; + choices[2] = temp; + } + + if (choices[1] == turnAround) + { + choices[1] = Direction.None; + } + + if (choices[2] == turnAround) + { + choices[2] = Direction.None; + } + + if (choices[1] != Direction.None) + { + actor.MoveDir = choices[1]; + + if (TryWalk(actor)) + { + // Either moved forward or attacked. + return; + } + } + + if (choices[2] != Direction.None) + { + actor.MoveDir = choices[2]; + + if (TryWalk(actor)) + { + return; + } + } + + // There is no direct path to the player, so pick another direction. + if (oldDir != Direction.None) + { + actor.MoveDir = oldDir; + + if (TryWalk(actor)) + { + return; + } + } + + // Randomly determine direction of search. + if ((world.Random.Next() & 1) != 0) + { + for (var dir = (int)Direction.East; dir <= (int)Direction.Southeast; dir++) + { + if ((Direction)dir != turnAround) + { + actor.MoveDir = (Direction)dir; + + if (TryWalk(actor)) + { + return; + } + } + } + } + else + { + for (var dir = (int)Direction.Southeast; dir != ((int)Direction.East - 1); dir--) + { + if ((Direction)dir != turnAround) + { + actor.MoveDir = (Direction)dir; + + if (TryWalk(actor)) + { + return; + } + } + } + } + + if (turnAround != Direction.None) + { + actor.MoveDir = turnAround; + + if (TryWalk(actor)) + { + return; + } + } + + // Can not move. + actor.MoveDir = Direction.None; + } + + + private bool CheckMeleeRange(Mobj actor) + { + if (actor.Target == null) + { + return false; + } + + var target = actor.Target; + + var dist = Geometry.AproxDistance(target.X - actor.X, target.Y - actor.Y); + + if (dist >= WeaponBehavior.MeleeRange - Fixed.FromInt(20) + target.Info.Radius) + { + return false; + } + + if (!world.VisibilityCheck.CheckSight(actor, actor.Target)) + { + return false; + } + + return true; + } + + + private bool CheckMissileRange(Mobj actor) + { + if (!world.VisibilityCheck.CheckSight(actor, actor.Target)) + { + return false; + } + + if ((actor.Flags & MobjFlags.JustHit) != 0) + { + // The target just hit the enemy, so fight back! + actor.Flags &= ~MobjFlags.JustHit; + + return true; + } + + if (actor.ReactionTime > 0) + { + // Do not attack yet + return false; + } + + // OPTIMIZE: + // Get this from a global checksight. + var dist = Geometry.AproxDistance( + actor.X - actor.Target.X, + actor.Y - actor.Target.Y) - Fixed.FromInt(64); + + if (actor.Info.MeleeState == 0) + { + // No melee attack, so fire more. + dist -= Fixed.FromInt(128); + } + + var attackDist = dist.Data >> 16; + + if (actor.Type == MobjType.Vile) + { + if (attackDist > 14 * 64) + { + // Too far away. + return false; + } + } + + if (actor.Type == MobjType.Undead) + { + if (attackDist < 196) + { + // Close for fist attack. + return false; + } + + attackDist >>= 1; + } + + + if (actor.Type == MobjType.Cyborg || + actor.Type == MobjType.Spider || + actor.Type == MobjType.Skull) + { + attackDist >>= 1; + } + + if (attackDist > 200) + { + attackDist = 200; + } + + if (actor.Type == MobjType.Cyborg && attackDist > 160) + { + attackDist = 160; + } + + if (world.Random.Next() < attackDist) + { + return false; + } + + return true; + } + + + public void Chase(Mobj actor) + { + if (actor.ReactionTime > 0) + { + actor.ReactionTime--; + } + + // Modify target threshold. + if (actor.Threshold > 0) + { + if (actor.Target == null || actor.Target.Health <= 0) + { + actor.Threshold = 0; + } + else + { + actor.Threshold--; + } + } + + // Turn towards movement direction if not there yet. + if ((int)actor.MoveDir < 8) + { + actor.Angle = new Angle((int)actor.Angle.Data & (7 << 29)); + + var delta = (int)(actor.Angle - new Angle((int)actor.MoveDir << 29)).Data; + + if (delta > 0) + { + actor.Angle -= new Angle(Angle.Ang90.Data / 2); + } + else if (delta < 0) + { + actor.Angle += new Angle(Angle.Ang90.Data / 2); + } + } + + if (actor.Target == null || (actor.Target.Flags & MobjFlags.Shootable) == 0) + { + // Look for a new target. + if (LookForPlayers(actor, true)) + { + // Got a new target. + return; + } + + actor.SetState(actor.Info.SpawnState); + + return; + } + + // Do not attack twice in a row. + if ((actor.Flags & MobjFlags.JustAttacked) != 0) + { + actor.Flags &= ~MobjFlags.JustAttacked; + + if (world.Options.Skill != GameSkill.Nightmare && + !world.Options.FastMonsters) + { + NewChaseDir(actor); + } + + return; + } + + // Check for melee attack. + if (actor.Info.MeleeState != 0 && CheckMeleeRange(actor)) + { + if (actor.Info.AttackSound != 0) + { + world.StartSound(actor, actor.Info.AttackSound, SfxType.Weapon); + } + + actor.SetState(actor.Info.MeleeState); + + return; + } + + // Check for missile attack. + if (actor.Info.MissileState != 0) + { + if (world.Options.Skill < GameSkill.Nightmare && + !world.Options.FastMonsters && + actor.MoveCount != 0) + { + goto noMissile; + } + + if (!CheckMissileRange(actor)) + { + goto noMissile; + } + + actor.SetState(actor.Info.MissileState); + actor.Flags |= MobjFlags.JustAttacked; + + return; + } + + noMissile: + // Possibly choose another target. + if (world.Options.NetGame && + actor.Threshold == 0 && + !world.VisibilityCheck.CheckSight(actor, actor.Target)) + { + if (LookForPlayers(actor, true)) + { + // Got a new target. + return; + } + } + + // Chase towards player. + if (--actor.MoveCount < 0 || !Move(actor)) + { + NewChaseDir(actor); + } + + // Make active sound. + if (actor.Info.ActiveSound != 0 && world.Random.Next() < 3) + { + world.StartSound(actor, actor.Info.ActiveSound, SfxType.Voice); + } + } + + + + //////////////////////////////////////////////////////////// + // Monster death + //////////////////////////////////////////////////////////// + + public void Pain(Mobj actor) + { + if (actor.Info.PainSound != 0) + { + world.StartSound(actor, actor.Info.PainSound, SfxType.Voice); + } + } + + public void Scream(Mobj actor) + { + int sound; + + switch (actor.Info.DeathSound) + { + case 0: + return; + + case Sfx.PODTH1: + case Sfx.PODTH2: + case Sfx.PODTH3: + sound = (int)Sfx.PODTH1 + world.Random.Next() % 3; + break; + + case Sfx.BGDTH1: + case Sfx.BGDTH2: + sound = (int)Sfx.BGDTH1 + world.Random.Next() % 2; + break; + + default: + sound = (int)actor.Info.DeathSound; + break; + } + + // Check for bosses. + if (actor.Type == MobjType.Spider || actor.Type == MobjType.Cyborg) + { + // Full volume. + world.StartSound(actor, (Sfx)sound, SfxType.Diffuse); + } + else + { + world.StartSound(actor, (Sfx)sound, SfxType.Voice); + } + } + + public void XScream(Mobj actor) + { + world.StartSound(actor, Sfx.SLOP, SfxType.Voice); + } + + public void Fall(Mobj actor) + { + // Actor is on ground, it can be walked over. + actor.Flags &= ~MobjFlags.Solid; + } + + + + //////////////////////////////////////////////////////////// + // Monster attack + //////////////////////////////////////////////////////////// + + public void FaceTarget(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + actor.Flags &= ~MobjFlags.Ambush; + + actor.Angle = Geometry.PointToAngle( + actor.X, actor.Y, + actor.Target.X, actor.Target.Y); + + var random = world.Random; + + if ((actor.Target.Flags & MobjFlags.Shadow) != 0) + { + actor.Angle += new Angle((random.Next() - random.Next()) << 21); + } + } + + + public void PosAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + var angle = actor.Angle; + var slope = world.Hitscan.AimLineAttack(actor, angle, WeaponBehavior.MissileRange); + + world.StartSound(actor, Sfx.PISTOL, SfxType.Weapon); + + var random = world.Random; + angle += new Angle((random.Next() - random.Next()) << 20); + var damage = ((random.Next() % 5) + 1) * 3; + + world.Hitscan.LineAttack(actor, angle, WeaponBehavior.MissileRange, slope, damage); + } + + + public void SPosAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + world.StartSound(actor, Sfx.SHOTGN, SfxType.Weapon); + + FaceTarget(actor); + + var center = actor.Angle; + var slope = world.Hitscan.AimLineAttack(actor, center, WeaponBehavior.MissileRange); + + var random = world.Random; + + for (var i = 0; i < 3; i++) + { + var angle = center + new Angle((random.Next() - random.Next()) << 20); + var damage = ((random.Next() % 5) + 1) * 3; + + world.Hitscan.LineAttack(actor, angle, WeaponBehavior.MissileRange, slope, damage); + } + } + + + public void CPosAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + world.StartSound(actor, Sfx.SHOTGN, SfxType.Weapon); + + FaceTarget(actor); + + var center = actor.Angle; + var slope = world.Hitscan.AimLineAttack(actor, center, WeaponBehavior.MissileRange); + + var random = world.Random; + var angle = center + new Angle((random.Next() - random.Next()) << 20); + var damage = ((random.Next() % 5) + 1) * 3; + + world.Hitscan.LineAttack(actor, angle, WeaponBehavior.MissileRange, slope, damage); + } + + + public void CPosRefire(Mobj actor) + { + // Keep firing unless target got out of sight. + FaceTarget(actor); + + if (world.Random.Next() < 40) + { + return; + } + + if (actor.Target == null || + actor.Target.Health <= 0 || + !world.VisibilityCheck.CheckSight(actor, actor.Target)) + { + actor.SetState(actor.Info.SeeState); + } + } + + + public void TroopAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + if (CheckMeleeRange(actor)) + { + world.StartSound(actor, Sfx.CLAW, SfxType.Weapon); + + var damage = (world.Random.Next() % 8 + 1) * 3; + world.ThingInteraction.DamageMobj(actor.Target, actor, actor, damage); + + return; + } + + // Launch a missile. + world.ThingAllocation.SpawnMissile(actor, actor.Target, MobjType.Troopshot); + } + + + public void SargAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + if (CheckMeleeRange(actor)) + { + var damage = ((world.Random.Next() % 10) + 1) * 4; + world.ThingInteraction.DamageMobj(actor.Target, actor, actor, damage); + } + } + + + public void HeadAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + if (CheckMeleeRange(actor)) + { + var damage = (world.Random.Next() % 6 + 1) * 10; + world.ThingInteraction.DamageMobj(actor.Target, actor, actor, damage); + + return; + } + + // Launch a missile. + world.ThingAllocation.SpawnMissile(actor, actor.Target, MobjType.Headshot); + } + + + public void BruisAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + if (CheckMeleeRange(actor)) + { + world.StartSound(actor, Sfx.CLAW, SfxType.Weapon); + + var damage = (world.Random.Next() % 8 + 1) * 10; + world.ThingInteraction.DamageMobj(actor.Target, actor, actor, damage); + + return; + } + + // Launch a missile. + world.ThingAllocation.SpawnMissile(actor, actor.Target, MobjType.Bruisershot); + } + + + private static readonly Fixed skullSpeed = Fixed.FromInt(20); + + public void SkullAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + var dest = actor.Target; + + actor.Flags |= MobjFlags.SkullFly; + + world.StartSound(actor, actor.Info.AttackSound, SfxType.Voice); + + FaceTarget(actor); + + var angle = actor.Angle; + actor.MomX = skullSpeed * Trig.Cos(angle); + actor.MomY = skullSpeed * Trig.Sin(angle); + + var dist = Geometry.AproxDistance(dest.X - actor.X, dest.Y - actor.Y); + + var num = (dest.Z + (dest.Height >> 1) - actor.Z).Data; + var den = dist.Data / skullSpeed.Data; + if (den < 1) + { + den = 1; + } + + actor.MomZ = new Fixed(num / den); + } + + + public void FatRaise(Mobj actor) + { + FaceTarget(actor); + + world.StartSound(actor, Sfx.MANATK, SfxType.Voice); + } + + + private static readonly Angle fatSpread = Angle.Ang90 / 8; + + public void FatAttack1(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + var ta = world.ThingAllocation; + + // Change direction to... + actor.Angle += fatSpread; + ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); + + var missile = ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); + missile.Angle += fatSpread; + var angle = missile.Angle; + missile.MomX = new Fixed(missile.Info.Speed) * Trig.Cos(angle); + missile.MomY = new Fixed(missile.Info.Speed) * Trig.Sin(angle); + } + + public void FatAttack2(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + var ta = world.ThingAllocation; + + // Now here choose opposite deviation. + actor.Angle -= fatSpread; + ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); + + var missile = ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); + missile.Angle -= fatSpread * 2; + var angle = missile.Angle; + missile.MomX = new Fixed(missile.Info.Speed) * Trig.Cos(angle); + missile.MomY = new Fixed(missile.Info.Speed) * Trig.Sin(angle); + } + + public void FatAttack3(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + var ta = world.ThingAllocation; + + var missile1 = ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); + missile1.Angle -= fatSpread / 2; + var angle1 = missile1.Angle; + missile1.MomX = new Fixed(missile1.Info.Speed) * Trig.Cos(angle1); + missile1.MomY = new Fixed(missile1.Info.Speed) * Trig.Sin(angle1); + + var missile2 = ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); + missile2.Angle += fatSpread / 2; + var angle2 = missile2.Angle; + missile2.MomX = new Fixed(missile2.Info.Speed) * Trig.Cos(angle2); + missile2.MomY = new Fixed(missile2.Info.Speed) * Trig.Sin(angle2); + } + + + public void BspiAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + // Launch a missile. + world.ThingAllocation.SpawnMissile(actor, actor.Target, MobjType.Arachplaz); + } + + + public void SpidRefire(Mobj actor) + { + // Keep firing unless target got out of sight. + FaceTarget(actor); + + if (world.Random.Next() < 10) + { + return; + } + + if (actor.Target == null || + actor.Target.Health <= 0 || + !world.VisibilityCheck.CheckSight(actor, actor.Target)) + { + actor.SetState(actor.Info.SeeState); + } + } + + + public void CyberAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + world.ThingAllocation.SpawnMissile(actor, actor.Target, MobjType.Rocket); + } + + + + //////////////////////////////////////////////////////////// + // Miscellaneous + //////////////////////////////////////////////////////////// + + public void Explode(Mobj actor) + { + world.ThingInteraction.RadiusAttack(actor, actor.Target, 128); + } + + + public void Metal(Mobj actor) + { + world.StartSound(actor, Sfx.METAL, SfxType.Footstep); + + Chase(actor); + } + + + public void BabyMetal(Mobj actor) + { + world.StartSound(actor, Sfx.BSPWLK, SfxType.Footstep); + + Chase(actor); + } + + + public void Hoof(Mobj actor) + { + world.StartSound(actor, Sfx.HOOF, SfxType.Footstep); + + Chase(actor); + } + + + + //////////////////////////////////////////////////////////// + // Arch vile + //////////////////////////////////////////////////////////// + + private Func vileCheckFunc; + private Mobj vileTargetCorpse; + private Fixed vileTryX; + private Fixed vileTryY; + + private void InitVile() + { + vileCheckFunc = VileCheck; + } + + + private bool VileCheck(Mobj thing) + { + if ((thing.Flags & MobjFlags.Corpse) == 0) + { + // Not a monster. + return true; + } + + if (thing.Tics != -1) + { + // Not lying still yet. + return true; + } + + if (thing.Info.Raisestate == MobjState.Null) + { + // Monster doesn't have a raise state. + return true; + } + + var maxDist = thing.Info.Radius + DoomInfo.MobjInfos[(int)MobjType.Vile].Radius; + + if (Fixed.Abs(thing.X - vileTryX) > maxDist || + Fixed.Abs(thing.Y - vileTryY) > maxDist) + { + // Not actually touching. + return true; + } + + vileTargetCorpse = thing; + vileTargetCorpse.MomX = vileTargetCorpse.MomY = Fixed.Zero; + vileTargetCorpse.Height <<= 2; + + var check = world.ThingMovement.CheckPosition( + vileTargetCorpse, + vileTargetCorpse.X, + vileTargetCorpse.Y); + + vileTargetCorpse.Height >>= 2; + + if (!check) + { + // Doesn't fit here. + return true; + } + + // Got one, so stop checking. + return false; + } + + + public void VileChase(Mobj actor) + { + if (actor.MoveDir != Direction.None) + { + // Check for corpses to raise. + vileTryX = actor.X + actor.Info.Speed * xSpeed[(int)actor.MoveDir]; + vileTryY = actor.Y + actor.Info.Speed * ySpeed[(int)actor.MoveDir]; + + var bm = world.Map.BlockMap; + + var maxRadius = GameConst.MaxThingRadius * 2; + var blockX1 = bm.GetBlockX(vileTryX - maxRadius); + var blockX2 = bm.GetBlockX(vileTryX + maxRadius); + var blockY1 = bm.GetBlockY(vileTryY - maxRadius); + var blockY2 = bm.GetBlockY(vileTryY + maxRadius); + + for (var bx = blockX1; bx <= blockX2; bx++) + { + for (var by = blockY1; by <= blockY2; by++) + { + // Call VileCheck to check whether object is a corpse that canbe raised. + if (!bm.IterateThings(bx, by, vileCheckFunc)) + { + // Got one! + var temp = actor.Target; + actor.Target = vileTargetCorpse; + FaceTarget(actor); + actor.Target = temp; + actor.SetState(MobjState.VileHeal1); + + world.StartSound(vileTargetCorpse, Sfx.SLOP, SfxType.Misc); + + var info = vileTargetCorpse.Info; + vileTargetCorpse.SetState(info.Raisestate); + vileTargetCorpse.Height <<= 2; + vileTargetCorpse.Flags = info.Flags; + vileTargetCorpse.Health = info.SpawnHealth; + vileTargetCorpse.Target = null; + + return; + } + } + } + } + + // Return to normal attack. + Chase(actor); + } + + + public void VileStart(Mobj actor) + { + world.StartSound(actor, Sfx.VILATK, SfxType.Weapon); + } + + + public void StartFire(Mobj actor) + { + world.StartSound(actor, Sfx.FLAMST, SfxType.Weapon); + + Fire(actor); + } + + + public void FireCrackle(Mobj actor) + { + world.StartSound(actor, Sfx.FLAME, SfxType.Weapon); + + Fire(actor); + } + + + public void Fire(Mobj actor) + { + var dest = actor.Tracer; + + if (dest == null) + { + return; + } + + // Don't move it if the vile lost sight. + if (!world.VisibilityCheck.CheckSight(actor.Target, dest)) + { + return; + } + + world.ThingMovement.UnsetThingPosition(actor); + + var angle = dest.Angle; + actor.X = dest.X + Fixed.FromInt(24) * Trig.Cos(angle); + actor.Y = dest.Y + Fixed.FromInt(24) * Trig.Sin(angle); + actor.Z = dest.Z; + + world.ThingMovement.SetThingPosition(actor); + } + + + public void VileTarget(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + var fog = world.ThingAllocation.SpawnMobj( + actor.Target.X, + actor.Target.X, + actor.Target.Z, + MobjType.Fire); + + actor.Tracer = fog; + fog.Target = actor; + fog.Tracer = actor.Target; + Fire(fog); + } + + + public void VileAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + if (!world.VisibilityCheck.CheckSight(actor, actor.Target)) + { + return; + } + + world.StartSound(actor, Sfx.BAREXP, SfxType.Weapon); + world.ThingInteraction.DamageMobj(actor.Target, actor, actor, 20); + actor.Target.MomZ = Fixed.FromInt(1000) / actor.Target.Info.Mass; + + var fire = actor.Tracer; + if (fire == null) + { + return; + } + + var angle = actor.Angle; + + // Move the fire between the vile and the player. + fire.X = actor.Target.X - Fixed.FromInt(24) * Trig.Cos(angle); + fire.Y = actor.Target.Y - Fixed.FromInt(24) * Trig.Sin(angle); + world.ThingInteraction.RadiusAttack(fire, actor, 70); + } + + + + //////////////////////////////////////////////////////////// + // Revenant + //////////////////////////////////////////////////////////// + + public void SkelMissile(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + // Missile spawns higher. + actor.Z += Fixed.FromInt(16); + + var missile = world.ThingAllocation.SpawnMissile(actor, actor.Target, MobjType.Tracer); + + // Back to normal. + actor.Z -= Fixed.FromInt(16); + + missile.X += missile.MomX; + missile.Y += missile.MomY; + missile.Tracer = actor.Target; + } + + + private static Angle traceAngle = new Angle(0xc000000); + + public void Tracer(Mobj actor) + { + if ((world.GameTic & 3) != 0) + { + return; + } + + // Spawn a puff of smoke behind the rocket. + world.Hitscan.SpawnPuff(actor.X, actor.Y, actor.Z); + + var smoke = world.ThingAllocation.SpawnMobj( + actor.X - actor.MomX, + actor.Y - actor.MomY, + actor.Z, + MobjType.Smoke); + + smoke.MomZ = Fixed.One; + smoke.Tics -= world.Random.Next() & 3; + if (smoke.Tics < 1) + { + smoke.Tics = 1; + } + + // Adjust direction. + var dest = actor.Tracer; + + if (dest == null || dest.Health <= 0) + { + return; + } + + // Change angle. + var exact = Geometry.PointToAngle( + actor.X, actor.Y, + dest.X, dest.Y); + + if (exact != actor.Angle) + { + if (exact - actor.Angle > Angle.Ang180) + { + actor.Angle -= traceAngle; + if (exact - actor.Angle < Angle.Ang180) + { + actor.Angle = exact; + } + } + else + { + actor.Angle += traceAngle; + if (exact - actor.Angle > Angle.Ang180) + { + actor.Angle = exact; + } + } + } + + exact = actor.Angle; + actor.MomX = new Fixed(actor.Info.Speed) * Trig.Cos(exact); + actor.MomY = new Fixed(actor.Info.Speed) * Trig.Sin(exact); + + // Change slope. + var dist = Geometry.AproxDistance( + dest.X - actor.X, + dest.Y - actor.Y); + + var num = (dest.Z + Fixed.FromInt(40) - actor.Z).Data; + var den = dist.Data / actor.Info.Speed; + if (den < 1) + { + den = 1; + } + + var slope = new Fixed(num / den); + + if (slope < actor.MomZ) + { + actor.MomZ -= Fixed.One / 8; + } + else + { + actor.MomZ += Fixed.One / 8; + } + } + + + public void SkelWhoosh(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + world.StartSound(actor, Sfx.SKESWG, SfxType.Weapon); + } + + + public void SkelFist(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + if (CheckMeleeRange(actor)) + { + var damage = ((world.Random.Next() % 10) + 1) * 6; + world.StartSound(actor, Sfx.SKEPCH, SfxType.Weapon); + world.ThingInteraction.DamageMobj(actor.Target, actor, actor, damage); + } + } + + + + //////////////////////////////////////////////////////////// + // Pain elemental + //////////////////////////////////////////////////////////// + + private void PainShootSkull(Mobj actor, Angle angle) + { + // Count total number of skull currently on the level. + var count = 0; + + foreach (var thinker in world.Thinkers) + { + var mobj = thinker as Mobj; + if (mobj != null && mobj.Type == MobjType.Skull) + { + count++; + } + } + + // If there are allready 20 skulls on the level, + // don't spit another one. + if (count > 20) + { + return; + } + + // Okay, there's playe for another one. + + var preStep = Fixed.FromInt(4) + + 3 * (actor.Info.Radius + DoomInfo.MobjInfos[(int)MobjType.Skull].Radius) / 2; + + var x = actor.X + preStep * Trig.Cos(angle); + var y = actor.Y + preStep * Trig.Sin(angle); + var z = actor.Z + Fixed.FromInt(8); + + var skull = world.ThingAllocation.SpawnMobj(x, y, z, MobjType.Skull); + + // Check for movements. + if (!world.ThingMovement.TryMove(skull, skull.X, skull.Y)) + { + // Kill it immediately. + world.ThingInteraction.DamageMobj(skull, actor, actor, 10000); + return; + } + + skull.Target = actor.Target; + + SkullAttack(skull); + } + + + public void PainAttack(Mobj actor) + { + if (actor.Target == null) + { + return; + } + + FaceTarget(actor); + + PainShootSkull(actor, actor.Angle); + } + + + public void PainDie(Mobj actor) + { + Fall(actor); + + PainShootSkull(actor, actor.Angle + Angle.Ang90); + PainShootSkull(actor, actor.Angle + Angle.Ang180); + PainShootSkull(actor, actor.Angle + Angle.Ang270); + } + + + + //////////////////////////////////////////////////////////// + // Boss death + //////////////////////////////////////////////////////////// + + private LineDef junk; + + private void InitBossDeath() + { + var v = new Vertex(Fixed.Zero, Fixed.Zero); + junk = new LineDef(v, v, 0, 0, 0, null, null); + } + + + public void BossDeath(Mobj actor) + { + var options = world.Options; + if (options.GameMode == GameMode.Commercial) + { + if (options.Map != 7) + { + return; + } + + if ((actor.Type != MobjType.Fatso) && (actor.Type != MobjType.Baby)) + { + return; + } + } + else + { + switch (options.Episode) + { + case 1: + if (options.Map != 8) + { + return; + } + + if (actor.Type != MobjType.Bruiser) + { + return; + } + + break; + + case 2: + if (options.Map != 8) + { + return; + } + + if (actor.Type != MobjType.Cyborg) + { + return; + } + + break; + + case 3: + if (options.Map != 8) + { + return; + } + + if (actor.Type != MobjType.Spider) + { + return; + } + + break; + + case 4: + switch (options.Map) + { + case 6: + if (actor.Type != MobjType.Cyborg) + { + return; + } + + break; + + case 8: + if (actor.Type != MobjType.Spider) + { + return; + } + + break; + + default: + return; + } + break; + + default: + if (options.Map != 8) + { + return; + } + + break; + } + } + + // Make sure there is a player alive for victory. + var players = world.Options.Players; + int i; + for (i = 0; i < Player.MaxPlayerCount; i++) + { + if (players[i].InGame && players[i].Health > 0) + { + break; + } + } + + if (i == Player.MaxPlayerCount) + { + // No one left alive, so do not end game. + return; + } + + // Scan the remaining thinkers to see if all bosses are dead. + foreach (var thinker in world.Thinkers) + { + var mo2 = thinker as Mobj; + if (mo2 == null) + { + continue; + } + + if (mo2 != actor && mo2.Type == actor.Type && mo2.Health > 0) + { + // Other boss not dead. + return; + } + } + + // Victory! + if (options.GameMode == GameMode.Commercial) + { + if (options.Map == 7) + { + if (actor.Type == MobjType.Fatso) + { + junk.Tag = 666; + world.SectorAction.DoFloor(junk, FloorMoveType.LowerFloorToLowest); + return; + } + + if (actor.Type == MobjType.Baby) + { + junk.Tag = 667; + world.SectorAction.DoFloor(junk, FloorMoveType.RaiseToTexture); + return; + } + } + } + else + { + switch (options.Episode) + { + case 1: + junk.Tag = 666; + world.SectorAction.DoFloor(junk, FloorMoveType.LowerFloorToLowest); + return; + + case 4: + switch (options.Map) + { + case 6: + junk.Tag = 666; + world.SectorAction.DoDoor(junk, VerticalDoorType.BlazeOpen); + return; + + case 8: + junk.Tag = 666; + world.SectorAction.DoFloor(junk, FloorMoveType.LowerFloorToLowest); + return; + } + break; + } + } + + world.ExitLevel(); + } + + + public void KeenDie(Mobj actor) + { + Fall(actor); + + // scan the remaining thinkers + // to see if all Keens are dead + foreach (var thinker in world.Thinkers) + { + var mo2 = thinker as Mobj; + if (mo2 == null) + { + continue; + } + + if (mo2 != actor && mo2.Type == actor.Type && mo2.Health > 0) + { + // other Keen not dead + return; + } + } + + junk.Tag = 666; + world.SectorAction.DoDoor(junk, VerticalDoorType.Open); + } + + + + //////////////////////////////////////////////////////////// + // Icon of sin + //////////////////////////////////////////////////////////// + + private Mobj[] brainTargets; + private int brainTargetCount; + private int currentBrainTarget; + private bool easy; + + private void InitBrain() + { + brainTargets = new Mobj[32]; + brainTargetCount = 0; + currentBrainTarget = 0; + easy = false; + } + + + public void BrainAwake(Mobj actor) + { + // Find all the target spots. + brainTargetCount = 0; + currentBrainTarget = 0; + + foreach (var thinker in world.Thinkers) + { + var mobj = thinker as Mobj; + if (mobj == null) + { + // Not a mobj. + continue; + } + + if (mobj.Type == MobjType.Bosstarget) + { + brainTargets[brainTargetCount] = mobj; + brainTargetCount++; + } + } + + world.StartSound(actor, Sfx.BOSSIT, SfxType.Diffuse); + } + + + public void BrainPain(Mobj actor) + { + world.StartSound(actor, Sfx.BOSPN, SfxType.Diffuse); + } + + + public void BrainScream(Mobj actor) + { + var random = world.Random; + + for (var x = actor.X - Fixed.FromInt(196); x < actor.X + Fixed.FromInt(320); x += Fixed.FromInt(8)) + { + var y = actor.Y - Fixed.FromInt(320); + var z = new Fixed(128) + random.Next() * Fixed.FromInt(2); + + var explosion = world.ThingAllocation.SpawnMobj(x, y, z, MobjType.Rocket); + explosion.MomZ = new Fixed(random.Next() * 512); + explosion.SetState(MobjState.Brainexplode1); + explosion.Tics -= random.Next() & 7; + if (explosion.Tics < 1) + { + explosion.Tics = 1; + } + } + + world.StartSound(actor, Sfx.BOSDTH, SfxType.Diffuse); + } + + + public void BrainExplode(Mobj actor) + { + var random = world.Random; + + var x = actor.X + new Fixed((random.Next() - random.Next()) * 2048); + var y = actor.Y; + var z = new Fixed(128) + random.Next() * Fixed.FromInt(2); + + var explosion = world.ThingAllocation.SpawnMobj(x, y, z, MobjType.Rocket); + explosion.MomZ = new Fixed(random.Next() * 512); + explosion.SetState(MobjState.Brainexplode1); + explosion.Tics -= random.Next() & 7; + if (explosion.Tics < 1) + { + explosion.Tics = 1; + } + } + + + public void BrainDie(Mobj actor) + { + world.ExitLevel(); + } + + + public void BrainSpit(Mobj actor) + { + easy = !easy; + if (world.Options.Skill <= GameSkill.Easy && (!easy)) + { + return; + } + + // If the game is reconstructed from a savedata, brain targets might be cleared. + // If so, re-initialize them to avoid crash. + if (brainTargetCount == 0) + { + BrainAwake(actor); + } + + // Shoot a cube at current target. + var target = brainTargets[currentBrainTarget]; + currentBrainTarget = (currentBrainTarget + 1) % brainTargetCount; + + // Spawn brain missile. + var missile = world.ThingAllocation.SpawnMissile(actor, target, MobjType.Spawnshot); + missile.Target = target; + missile.ReactionTime = ((target.Y - actor.Y).Data / missile.MomY.Data) / missile.State.Tics; + + world.StartSound(actor, Sfx.BOSPIT, SfxType.Diffuse); + } + + + public void SpawnSound(Mobj actor) + { + world.StartSound(actor, Sfx.BOSCUB, SfxType.Misc); + SpawnFly(actor); + } + + + public void SpawnFly(Mobj actor) + { + if (--actor.ReactionTime > 0) + { + // Still flying. + return; + } + + var target = actor.Target; + + // If the game is reconstructed from a savedata, the target might be null. + // If so, use own position to spawn the monster. + if (target == null) + { + target = actor; + actor.Z = actor.Subsector.Sector.FloorHeight; + } + + var ta = world.ThingAllocation; + + // First spawn teleport fog. + var fog = ta.SpawnMobj(target.X, target.Y, target.Z, MobjType.Spawnfire); + world.StartSound(fog, Sfx.TELEPT, SfxType.Misc); + + // Randomly select monster to spawn. + var r = world.Random.Next(); + + // Probability distribution (kind of :), decreasing likelihood. + MobjType type; + if (r < 50) + { + type = MobjType.Troop; + } + else if (r < 90) + { + type = MobjType.Sergeant; + } + else if (r < 120) + { + type = MobjType.Shadows; + } + else if (r < 130) + { + type = MobjType.Pain; + } + else if (r < 160) + { + type = MobjType.Head; + } + else if (r < 162) + { + type = MobjType.Vile; + } + else if (r < 172) + { + type = MobjType.Undead; + } + else if (r < 192) + { + type = MobjType.Baby; + } + else if (r < 222) + { + type = MobjType.Fatso; + } + else if (r < 246) + { + type = MobjType.Knight; + } + else + { + type = MobjType.Bruiser; + } + + var monster = ta.SpawnMobj(target.X, target.Y, target.Z, type); + if (LookForPlayers(monster, true)) + { + monster.SetState(monster.Info.SeeState); + } + + // Telefrag anything in this spot. + world.ThingMovement.TeleportMove(monster, monster.X, monster.Y); + + // Remove self (i.e., cube). + world.ThingAllocation.RemoveMobj(actor); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PathTraversal.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PathTraversal.cs new file mode 100644 index 00000000..d585581d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PathTraversal.cs @@ -0,0 +1,373 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class PathTraversal + { + private World world; + + private Intercept[] intercepts; + private int interceptCount; + + private bool earlyOut; + + private DivLine target; + private DivLine trace; + + private Func lineInterceptFunc; + private Func thingInterceptFunc; + + public PathTraversal(World world) + { + this.world = world; + + intercepts = new Intercept[256]; + for (var i = 0; i < intercepts.Length; i++) + { + intercepts[i] = new Intercept(); + } + + target = new DivLine(); + trace = new DivLine(); + + lineInterceptFunc = AddLineIntercepts; + thingInterceptFunc = AddThingIntercepts; + } + + /// + /// Looks for lines in the given block that intercept the given trace + /// to add to the intercepts list. + /// A line is crossed if its endpoints are on opposite sidesof the trace. + /// Returns true if earlyOut and a solid line hit. + /// + private bool AddLineIntercepts(LineDef line) + { + int s1; + int s2; + + // Avoid precision problems with two routines. + if (trace.Dx > Fixed.FromInt(16) || + trace.Dy > Fixed.FromInt(16) || + trace.Dx < -Fixed.FromInt(16) || + trace.Dy < -Fixed.FromInt(16)) + { + s1 = Geometry.PointOnDivLineSide(line.Vertex1.X, line.Vertex1.Y, trace); + s2 = Geometry.PointOnDivLineSide(line.Vertex2.X, line.Vertex2.Y, trace); + } + else + { + s1 = Geometry.PointOnLineSide(trace.X, trace.Y, line); + s2 = Geometry.PointOnLineSide(trace.X + trace.Dx, trace.Y + trace.Dy, line); + } + + if (s1 == s2) + { + // Line isn't crossed. + return true; + } + + // Hit the line. + target.MakeFrom(line); + + var frac = InterceptVector(trace, target); + + if (frac < Fixed.Zero) + { + // Behind source. + return true; + } + + // Try to early out the check. + if (earlyOut && frac < Fixed.One && line.BackSector == null) + { + // Stop checking. + return false; + } + + intercepts[interceptCount].Make(frac, line); + interceptCount++; + + // Continue. + return true; + } + + /// + /// Looks for things that intercept the given trace. + /// + private bool AddThingIntercepts(Mobj thing) + { + var tracePositive = (trace.Dx.Data ^ trace.Dy.Data) > 0; + + Fixed x1; + Fixed y1; + Fixed x2; + Fixed y2; + + // Check a corner to corner crossection for hit. + if (tracePositive) + { + x1 = thing.X - thing.Radius; + y1 = thing.Y + thing.Radius; + + x2 = thing.X + thing.Radius; + y2 = thing.Y - thing.Radius; + } + else + { + x1 = thing.X - thing.Radius; + y1 = thing.Y - thing.Radius; + + x2 = thing.X + thing.Radius; + y2 = thing.Y + thing.Radius; + } + + var s1 = Geometry.PointOnDivLineSide(x1, y1, trace); + var s2 = Geometry.PointOnDivLineSide(x2, y2, trace); + + if (s1 == s2) + { + // Line isn't crossed. + return true; + } + + target.X = x1; + target.Y = y1; + target.Dx = x2 - x1; + target.Dy = y2 - y1; + + var frac = InterceptVector(trace, target); + + if (frac < Fixed.Zero) + { + // Behind source. + return true; + } + + intercepts[interceptCount].Make(frac, thing); + interceptCount++; + + // Keep going. + return true; + } + + /// + /// Returns the fractional intercept point along the first divline. + /// This is only called by the addthings and addlines traversers. + /// + private Fixed InterceptVector(DivLine v2, DivLine v1) + { + var den = (v1.Dy >> 8) * v2.Dx - (v1.Dx >> 8) * v2.Dy; + + if (den == Fixed.Zero) + { + return Fixed.Zero; + } + + var num = ((v1.X - v2.X) >> 8) * v1.Dy + ((v2.Y - v1.Y) >> 8) * v1.Dx; + + var frac = num / den; + + return frac; + } + + /// + /// Returns true if the traverser function returns true for all lines. + /// + private bool TraverseIntercepts(Func func, Fixed maxFrac) + { + var count = interceptCount; + + Intercept intercept = null; + + while (count-- > 0) + { + var dist = Fixed.MaxValue; + for (var i = 0; i < interceptCount; i++) + { + if (intercepts[i].Frac < dist) + { + dist = intercepts[i].Frac; + intercept = intercepts[i]; + } + } + + if (dist > maxFrac) + { + // Checked everything in range. + return true; + } + + if (!func(intercept)) + { + // Don't bother going farther. + return false; + } + + intercept.Frac = Fixed.MaxValue; + } + + // Everything was traversed. + return true; + } + + /// + /// Traces a line from x1, y1 to x2, y2, calling the traverser function for each. + /// Returns true if the traverser function returns true for all lines. + /// + public bool PathTraverse(Fixed x1, Fixed y1, Fixed x2, Fixed y2, PathTraverseFlags flags, Func trav) + { + earlyOut = (flags & PathTraverseFlags.EarlyOut) != 0; + + var validCount = world.GetNewValidCount(); + + var bm = world.Map.BlockMap; + + interceptCount = 0; + + if (((x1 - bm.OriginX).Data & (BlockMap.BlockSize.Data - 1)) == 0) + { + // Don't side exactly on a line. + x1 += Fixed.One; + } + + if (((y1 - bm.OriginY).Data & (BlockMap.BlockSize.Data - 1)) == 0) + { + // Don't side exactly on a line. + y1 += Fixed.One; + } + + trace.X = x1; + trace.Y = y1; + trace.Dx = x2 - x1; + trace.Dy = y2 - y1; + + x1 -= bm.OriginX; + y1 -= bm.OriginY; + + var blockX1 = x1.Data >> BlockMap.FracToBlockShift; + var blockY1 = y1.Data >> BlockMap.FracToBlockShift; + + x2 -= bm.OriginX; + y2 -= bm.OriginY; + + var blockX2 = x2.Data >> BlockMap.FracToBlockShift; + var blockY2 = y2.Data >> BlockMap.FracToBlockShift; + + Fixed stepX; + Fixed stepY; + + Fixed partial; + + int blockStepX; + int blockStepY; + + if (blockX2 > blockX1) + { + blockStepX = 1; + partial = new Fixed(Fixed.FracUnit - ((x1.Data >> BlockMap.BlockToFracShift) & (Fixed.FracUnit - 1))); + stepY = (y2 - y1) / Fixed.Abs(x2 - x1); + } + else if (blockX2 < blockX1) + { + blockStepX = -1; + partial = new Fixed((x1.Data >> BlockMap.BlockToFracShift) & (Fixed.FracUnit - 1)); + stepY = (y2 - y1) / Fixed.Abs(x2 - x1); + } + else + { + blockStepX = 0; + partial = Fixed.One; + stepY = Fixed.FromInt(256); + } + + var interceptY = new Fixed(y1.Data >> BlockMap.BlockToFracShift) + (partial * stepY); + + + if (blockY2 > blockY1) + { + blockStepY = 1; + partial = new Fixed(Fixed.FracUnit - ((y1.Data >> BlockMap.BlockToFracShift) & (Fixed.FracUnit - 1))); + stepX = (x2 - x1) / Fixed.Abs(y2 - y1); + } + else if (blockY2 < blockY1) + { + blockStepY = -1; + partial = new Fixed((y1.Data >> BlockMap.BlockToFracShift) & (Fixed.FracUnit - 1)); + stepX = (x2 - x1) / Fixed.Abs(y2 - y1); + } + else + { + blockStepY = 0; + partial = Fixed.One; + stepX = Fixed.FromInt(256); + } + + var interceptX = new Fixed(x1.Data >> BlockMap.BlockToFracShift) + (partial * stepX); + + // Step through map blocks. + // Count is present to prevent a round off error from skipping the break. + var bx = blockX1; + var by = blockY1; + + for (var count = 0; count < 64; count++) + { + if ((flags & PathTraverseFlags.AddLines) != 0) + { + if (!bm.IterateLines(bx, by, lineInterceptFunc, validCount)) + { + // Early out. + return false; + } + } + + if ((flags & PathTraverseFlags.AddThings) != 0) + { + if (!bm.IterateThings(bx, by, thingInterceptFunc)) + { + // Early out. + return false; + } + } + + if (bx == blockX2 && by == blockY2) + { + break; + } + + if ((interceptY.ToIntFloor()) == by) + { + interceptY += stepY; + bx += blockStepX; + } + else if ((interceptX.ToIntFloor()) == bx) + { + interceptX += stepX; + by += blockStepY; + } + + } + + // Go through the sorted list. + return TraverseIntercepts(trav, Fixed.One); + } + + public DivLine Trace => trace; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PathTraverseFlags.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PathTraverseFlags.cs new file mode 100644 index 00000000..e5da2bdc --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PathTraverseFlags.cs @@ -0,0 +1,29 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + [Flags] + public enum PathTraverseFlags + { + AddLines = 1, + AddThings = 2, + EarlyOut = 4 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Platform.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Platform.cs new file mode 100644 index 00000000..3ec14e14 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Platform.cs @@ -0,0 +1,196 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class Platform : Thinker + { + private World world; + + private Sector sector; + private Fixed speed; + private Fixed low; + private Fixed high; + private int wait; + private int count; + private PlatformState status; + private PlatformState oldStatus; + private bool crush; + private int tag; + private PlatformType type; + + public Platform(World world) + { + this.world = world; + } + + public override void Run() + { + var sa = world.SectorAction; + + SectorActionResult result; + + switch (status) + { + case PlatformState.Up: + result = sa.MovePlane(sector, speed, high, crush, 0, 1); + + if (type == PlatformType.RaiseAndChange || + type == PlatformType.RaiseToNearestAndChange) + { + if (((world.LevelTime + sector.Number) & 7) == 0) + { + world.StartSound(sector.SoundOrigin, Sfx.STNMOV, SfxType.Misc); + } + } + + if (result == SectorActionResult.Crushed && !crush) + { + count = wait; + status = PlatformState.Down; + world.StartSound(sector.SoundOrigin, Sfx.PSTART, SfxType.Misc); + } + else + { + if (result == SectorActionResult.PastDestination) + { + count = wait; + status = PlatformState.Waiting; + world.StartSound(sector.SoundOrigin, Sfx.PSTOP, SfxType.Misc); + + switch (type) + { + case PlatformType.BlazeDwus: + case PlatformType.DownWaitUpStay: + sa.RemoveActivePlatform(this); + break; + + case PlatformType.RaiseAndChange: + case PlatformType.RaiseToNearestAndChange: + sa.RemoveActivePlatform(this); + break; + + default: + break; + } + } + } + + break; + + case PlatformState.Down: + result = sa.MovePlane(sector, speed, low, false, 0, -1); + + if (result == SectorActionResult.PastDestination) + { + count = wait; + status = PlatformState.Waiting; + world.StartSound(sector.SoundOrigin, Sfx.PSTOP, SfxType.Misc); + } + + break; + + case PlatformState.Waiting: + if (--count == 0) + { + if (sector.FloorHeight == low) + { + status = PlatformState.Up; + } + else + { + status = PlatformState.Down; + } + world.StartSound(sector.SoundOrigin, Sfx.PSTART, SfxType.Misc); + } + + break; + + case PlatformState.InStasis: + break; + } + } + + public Sector Sector + { + get => sector; + set => sector = value; + } + + public Fixed Speed + { + get => speed; + set => speed = value; + } + + public Fixed Low + { + get => low; + set => low = value; + } + + public Fixed High + { + get => high; + set => high = value; + } + + public int Wait + { + get => wait; + set => wait = value; + } + + public int Count + { + get => count; + set => count = value; + } + + public PlatformState Status + { + get => status; + set => status = value; + } + + public PlatformState OldStatus + { + get => oldStatus; + set => oldStatus = value; + } + + public bool Crush + { + get => crush; + set => crush = value; + } + + public int Tag + { + get => tag; + set => tag = value; + } + + public PlatformType Type + { + get => type; + set => type = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlatformState.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlatformState.cs new file mode 100644 index 00000000..a22e784a --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlatformState.cs @@ -0,0 +1,29 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum PlatformState + { + Up, + Down, + Waiting, + InStasis + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlatformType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlatformType.cs new file mode 100644 index 00000000..a7369132 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlatformType.cs @@ -0,0 +1,30 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum PlatformType + { + PerpetualRaise, + DownWaitUpStay, + RaiseAndChange, + RaiseToNearestAndChange, + BlazeDwus + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlayerBehavior.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlayerBehavior.cs new file mode 100644 index 00000000..51eae22b --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlayerBehavior.cs @@ -0,0 +1,659 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class PlayerBehavior + { + public static readonly int[] ForwardMove = + { + 0x19, + 0x32 + }; + + public static readonly int[] SideMove = + { + 0x18, + 0x28 + }; + + public static readonly int[] AngleTurn = + { + 640, + 1280, + 320 // For slow turn. + }; + + public static readonly int MaxMove = ForwardMove[1]; + public static readonly int SlowTurnTics = 6; + + + + private World world; + + public PlayerBehavior(World world) + { + this.world = world; + } + + + + //////////////////////////////////////////////////////////// + // Player movement + //////////////////////////////////////////////////////////// + + /// + /// Called every frame to update player state. + /// + public void PlayerThink(Player player) + { + if (player.MessageTime > 0) + { + player.MessageTime--; + } + + if ((player.Cheats & CheatFlags.NoClip) != 0) + { + player.Mobj.Flags |= MobjFlags.NoClip; + } + else + { + player.Mobj.Flags &= ~MobjFlags.NoClip; + } + + // Chain saw run forward. + var cmd = player.Cmd; + if ((player.Mobj.Flags & MobjFlags.JustAttacked) != 0) + { + cmd.AngleTurn = 0; + cmd.ForwardMove = 0xC800 / 512; + cmd.SideMove = 0; + player.Mobj.Flags &= ~MobjFlags.JustAttacked; + } + + if (player.PlayerState == PlayerState.Dead) + { + DeathThink(player); + return; + } + + // Move around. + // Reactiontime is used to prevent movement for a bit after a teleport. + if (player.Mobj.ReactionTime > 0) + { + player.Mobj.ReactionTime--; + } + else + { + MovePlayer(player); + } + + CalcHeight(player); + + if (player.Mobj.Subsector.Sector.Special != 0) + { + PlayerInSpecialSector(player); + } + + // Check for weapon change. + + // A special event has no other buttons. + if ((cmd.Buttons & TicCmdButtons.Special) != 0) + { + cmd.Buttons = 0; + } + + if ((cmd.Buttons & TicCmdButtons.Change) != 0) + { + // The actual changing of the weapon is done when the weapon psprite can do it. + // Not in the middle of an attack. + var newWeapon = (cmd.Buttons & TicCmdButtons.WeaponMask) >> TicCmdButtons.WeaponShift; + + if (newWeapon == (int)WeaponType.Fist && + player.WeaponOwned[(int)WeaponType.Chainsaw] && + !(player.ReadyWeapon == WeaponType.Chainsaw && player.Powers[(int)PowerType.Strength] != 0)) + { + newWeapon = (int)WeaponType.Chainsaw; + } + + if ((world.Options.GameMode == GameMode.Commercial) && + newWeapon == (int)WeaponType.Shotgun && + player.WeaponOwned[(int)WeaponType.SuperShotgun] && + player.ReadyWeapon != WeaponType.SuperShotgun) + { + newWeapon = (int)WeaponType.SuperShotgun; + } + + if (player.WeaponOwned[newWeapon] && + newWeapon != (int)player.ReadyWeapon) + { + // Do not go to plasma or BFG in shareware, even if cheated. + if ((newWeapon != (int)WeaponType.Plasma && newWeapon != (int)WeaponType.Bfg) || + (world.Options.GameMode != GameMode.Shareware)) + { + player.PendingWeapon = (WeaponType)newWeapon; + } + } + } + + // Check for use. + if ((cmd.Buttons & TicCmdButtons.Use) != 0) + { + if (!player.UseDown) + { + world.MapInteraction.UseLines(player); + player.UseDown = true; + } + } + else + { + player.UseDown = false; + } + + // Cycle player sprites. + MovePlayerSprites(player); + + // Counters, time dependend power ups. + + // Strength counts up to diminish fade. + if (player.Powers[(int)PowerType.Strength] != 0) + { + player.Powers[(int)PowerType.Strength]++; + } + + if (player.Powers[(int)PowerType.Invulnerability] > 0) + { + player.Powers[(int)PowerType.Invulnerability]--; + } + + if (player.Powers[(int)PowerType.Invisibility] > 0) + { + if (--player.Powers[(int)PowerType.Invisibility] == 0) + { + player.Mobj.Flags &= ~MobjFlags.Shadow; + } + } + + if (player.Powers[(int)PowerType.Infrared] > 0) + { + player.Powers[(int)PowerType.Infrared]--; + } + + if (player.Powers[(int)PowerType.IronFeet] > 0) + { + player.Powers[(int)PowerType.IronFeet]--; + } + + if (player.DamageCount > 0) + { + player.DamageCount--; + } + + if (player.BonusCount > 0) + { + player.BonusCount--; + } + + // Handling colormaps. + if (player.Powers[(int)PowerType.Invulnerability] > 0) + { + if (player.Powers[(int)PowerType.Invulnerability] > 4 * 32 || + (player.Powers[(int)PowerType.Invulnerability] & 8) != 0) + { + player.FixedColorMap = ColorMap.Inverse; + } + else + { + player.FixedColorMap = 0; + } + } + else if (player.Powers[(int)PowerType.Infrared] > 0) + { + if (player.Powers[(int)PowerType.Infrared] > 4 * 32 || + (player.Powers[(int)PowerType.Infrared] & 8) != 0) + { + // Almost full bright. + player.FixedColorMap = 1; + } + else + { + player.FixedColorMap = 0; + } + } + else + { + player.FixedColorMap = 0; + } + } + + + private static readonly Fixed maxBob = new Fixed(0x100000); + + private bool onGround; + + /// + /// Move the player according to TicCmd. + /// + public void MovePlayer(Player player) + { + var cmd = player.Cmd; + + player.Mobj.Angle += new Angle(cmd.AngleTurn << 16); + + // Do not let the player control movement if not onground. + onGround = (player.Mobj.Z <= player.Mobj.FloorZ); + + if (cmd.ForwardMove != 0 && onGround) + { + Thrust(player, player.Mobj.Angle, new Fixed(cmd.ForwardMove * 2048)); + } + + if (cmd.SideMove != 0 && onGround) + { + Thrust(player, player.Mobj.Angle - Angle.Ang90, new Fixed(cmd.SideMove * 2048)); + } + + if ((cmd.ForwardMove != 0 || cmd.SideMove != 0) && + player.Mobj.State == DoomInfo.States[(int)MobjState.Play]) + { + player.Mobj.SetState(MobjState.PlayRun1); + } + } + + + /// + /// Calculate the walking / running height adjustment. + /// + public void CalcHeight(Player player) + { + // Regular movement bobbing. + // It needs to be calculated for gun swing even if not on ground. + player.Bob = player.Mobj.MomX * player.Mobj.MomX + player.Mobj.MomY * player.Mobj.MomY; + player.Bob >>= 2; + if (player.Bob > maxBob) + { + player.Bob = maxBob; + } + + if ((player.Cheats & CheatFlags.NoMomentum) != 0 || !onGround) + { + player.ViewZ = player.Mobj.Z + Player.NormalViewHeight; + + if (player.ViewZ > player.Mobj.CeilingZ - Fixed.FromInt(4)) + { + player.ViewZ = player.Mobj.CeilingZ - Fixed.FromInt(4); + } + + player.ViewZ = player.Mobj.Z + player.ViewHeight; + + return; + } + + var angle = (Trig.FineAngleCount / 20 * world.LevelTime) & Trig.FineMask; + + var bob = (player.Bob / 2) * Trig.Sin(angle); + + // Move viewheight. + if (player.PlayerState == PlayerState.Live) + { + player.ViewHeight += player.DeltaViewHeight; + + if (player.ViewHeight > Player.NormalViewHeight) + { + player.ViewHeight = Player.NormalViewHeight; + player.DeltaViewHeight = Fixed.Zero; + } + + if (player.ViewHeight < Player.NormalViewHeight / 2) + { + player.ViewHeight = Player.NormalViewHeight / 2; + + if (player.DeltaViewHeight <= Fixed.Zero) + { + player.DeltaViewHeight = new Fixed(1); + } + } + + if (player.DeltaViewHeight != Fixed.Zero) + { + player.DeltaViewHeight += Fixed.One / 4; + + if (player.DeltaViewHeight == Fixed.Zero) + { + player.DeltaViewHeight = new Fixed(1); + } + } + } + + player.ViewZ = player.Mobj.Z + player.ViewHeight + bob; + + if (player.ViewZ > player.Mobj.CeilingZ - Fixed.FromInt(4)) + { + player.ViewZ = player.Mobj.CeilingZ - Fixed.FromInt(4); + } + } + + + /// + /// Moves the given origin along a given angle. + /// + public void Thrust(Player player, Angle angle, Fixed move) + { + player.Mobj.MomX += move * Trig.Cos(angle); + player.Mobj.MomY += move * Trig.Sin(angle); + } + + + /// + /// Called every tic frame that the player origin is in a special sector. + /// + private void PlayerInSpecialSector(Player player) + { + var sector = player.Mobj.Subsector.Sector; + + // Falling, not all the way down yet? + if (player.Mobj.Z != sector.FloorHeight) + { + return; + } + + var ti = world.ThingInteraction; + + // Has hitten ground. + switch ((int)sector.Special) + { + case 5: + // Hell slime damage. + if (player.Powers[(int)PowerType.IronFeet] == 0) + { + if ((world.LevelTime & 0x1f) == 0) + { + ti.DamageMobj(player.Mobj, null, null, 10); + } + } + break; + + case 7: + // Nukage damage. + if (player.Powers[(int)PowerType.IronFeet] == 0) + { + if ((world.LevelTime & 0x1f) == 0) + { + ti.DamageMobj(player.Mobj, null, null, 5); + } + } + break; + + case 16: + // Super hell slime damage. + case 4: + // Strobe hurt. + if (player.Powers[(int)PowerType.IronFeet] == 0 || (world.Random.Next() < 5)) + { + if ((world.LevelTime & 0x1f) == 0) + { + ti.DamageMobj(player.Mobj, null, null, 20); + } + } + break; + + case 9: + // Secret sector. + player.SecretCount++; + sector.Special = 0; + break; + + case 11: + // Exit super damage for E1M8 finale. + player.Cheats &= ~CheatFlags.GodMode; + if ((world.LevelTime & 0x1f) == 0) + { + ti.DamageMobj(player.Mobj, null, null, 20); + } + if (player.Health <= 10) + { + world.ExitLevel(); + } + break; + + default: + throw new Exception("Unknown sector special: " + (int)sector.Special); + } + } + + + private static Angle ang5 = new Angle(Angle.Ang90.Data / 18); + + /// + /// Fall on your face when dying. + /// Decrease POV height to floor height. + /// + private void DeathThink(Player player) + { + MovePlayerSprites(player); + + // Fall to the ground. + if (player.ViewHeight > Fixed.FromInt(6)) + { + player.ViewHeight -= Fixed.One; + } + + if (player.ViewHeight < Fixed.FromInt(6)) + { + player.ViewHeight = Fixed.FromInt(6); + } + + player.DeltaViewHeight = Fixed.Zero; + onGround = (player.Mobj.Z <= player.Mobj.FloorZ); + CalcHeight(player); + + if (player.Attacker != null && player.Attacker != player.Mobj) + { + var angle = Geometry.PointToAngle( + player.Mobj.X, player.Mobj.Y, + player.Attacker.X, player.Attacker.Y); + + var delta = angle - player.Mobj.Angle; + + if (delta < ang5 || delta.Data > (-ang5).Data) + { + // Looking at killer, so fade damage flash down. + player.Mobj.Angle = angle; + + if (player.DamageCount > 0) + { + player.DamageCount--; + } + } + else if (delta < Angle.Ang180) + { + player.Mobj.Angle += ang5; + } + else + { + player.Mobj.Angle -= ang5; + } + } + else if (player.DamageCount > 0) + { + player.DamageCount--; + } + + if ((player.Cmd.Buttons & TicCmdButtons.Use) != 0) + { + player.PlayerState = PlayerState.Reborn; + } + } + + + + //////////////////////////////////////////////////////////// + // Player's weapon sprites + //////////////////////////////////////////////////////////// + + /// + /// Called at start of level for each player. + /// + public void SetupPlayerSprites(Player player) + { + // Remove all psprites. + for (var i = 0; i < (int)PlayerSprite.Count; i++) + { + player.PlayerSprites[i].State = null; + } + + // Spawn the gun. + player.PendingWeapon = player.ReadyWeapon; + BringUpWeapon(player); + } + + /// + /// Starts bringing the pending weapon up from the bottom of the screen. + /// + public void BringUpWeapon(Player player) + { + if (player.PendingWeapon == WeaponType.NoChange) + { + player.PendingWeapon = player.ReadyWeapon; + } + + if (player.PendingWeapon == WeaponType.Chainsaw) + { + world.StartSound(player.Mobj, Sfx.SAWUP, SfxType.Weapon); + } + + var newState = DoomInfo.WeaponInfos[(int)player.PendingWeapon].UpState; + + player.PendingWeapon = WeaponType.NoChange; + player.PlayerSprites[(int)PlayerSprite.Weapon].Sy = WeaponBehavior.WeaponBottom; + + SetPlayerSprite(player, PlayerSprite.Weapon, newState); + } + + /// + /// Change the player's weapon sprite. + /// + public void SetPlayerSprite(Player player, PlayerSprite position, MobjState state) + { + var psp = player.PlayerSprites[(int)position]; + + do + { + if (state == MobjState.Null) + { + // Object removed itself. + psp.State = null; + break; + } + + var stateDef = DoomInfo.States[(int)state]; + psp.State = stateDef; + psp.Tics = stateDef.Tics; // Could be 0. + + if (stateDef.Misc1 != 0) + { + // Coordinate set. + psp.Sx = Fixed.FromInt(stateDef.Misc1); + psp.Sy = Fixed.FromInt(stateDef.Misc2); + } + + // Call action routine. + // Modified handling. + if (stateDef.PlayerAction != null) + { + stateDef.PlayerAction(world, player, psp); + if (psp.State == null) + { + break; + } + } + + state = psp.State.Next; + + } while (psp.Tics == 0); + // An initial state of 0 could cycle through. + } + + /// + /// Called every tic by player thinking routine. + /// + private void MovePlayerSprites(Player player) + { + for (var i = 0; i < (int)PlayerSprite.Count; i++) + { + var psp = player.PlayerSprites[i]; + + MobjStateDef stateDef; + + // A null state means not active. + if ((stateDef = psp.State) != null) + { + // Drop tic count and possibly change state. + + // A -1 tic count never changes. + if (psp.Tics != -1) + { + psp.Tics--; + if (psp.Tics == 0) + { + SetPlayerSprite(player, (PlayerSprite)i, psp.State.Next); + } + } + } + } + + player.PlayerSprites[(int)PlayerSprite.Flash].Sx = player.PlayerSprites[(int)PlayerSprite.Weapon].Sx; + player.PlayerSprites[(int)PlayerSprite.Flash].Sy = player.PlayerSprites[(int)PlayerSprite.Weapon].Sy; + } + + /// + /// Player died, so put the weapon away. + /// + public void DropWeapon(Player player) + { + SetPlayerSprite( + player, + PlayerSprite.Weapon, + DoomInfo.WeaponInfos[(int)player.ReadyWeapon].DownState); + } + + + + //////////////////////////////////////////////////////////// + // Miscellaneous + //////////////////////////////////////////////////////////// + + /// + /// Play the player's death sound. + /// + public void PlayerScream(Mobj player) + { + // Default death sound. + var sound = Sfx.PLDETH; + + if ((world.Options.GameMode == GameMode.Commercial) && (player.Health < -50)) + { + // If the player dies less than -50% without gibbing. + sound = Sfx.PDIEHI; + } + + world.StartSound(player, sound, SfxType.Voice); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlayerSprite.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlayerSprite.cs new file mode 100644 index 00000000..b5804f52 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlayerSprite.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum PlayerSprite + { + Weapon, + Flash, + Count + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlayerSpriteDef.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlayerSpriteDef.cs new file mode 100644 index 00000000..4760664f --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PlayerSpriteDef.cs @@ -0,0 +1,61 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class PlayerSpriteDef + { + private MobjStateDef state; + private int tics; + private Fixed sx; + private Fixed sy; + + public void Clear() + { + state = null; + tics = 0; + sx = Fixed.Zero; + sy = Fixed.Zero; + } + + public MobjStateDef State + { + get => state; + set => state = value; + } + + public int Tics + { + get => tics; + set => tics = value; + } + + public Fixed Sx + { + get => sx; + set => sx = value; + } + + public Fixed Sy + { + get => sy; + set => sy = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PowerType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PowerType.cs new file mode 100644 index 00000000..4b2bda6a --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/PowerType.cs @@ -0,0 +1,33 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum PowerType + { + Invulnerability, + Strength, + Invisibility, + IronFeet, + AllMap, + Infrared, + + Count + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/SectorAction.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/SectorAction.cs new file mode 100644 index 00000000..7d503928 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/SectorAction.cs @@ -0,0 +1,1767 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class SectorAction + { + // + // SECTOR HEIGHT CHANGING + // After modifying a sectors floor or ceiling height, + // call this routine to adjust the positions + // of all things that touch the sector. + // + // If anything doesn't fit anymore, true will be returned. + // If crunch is true, they will take damage + // as they are being crushed. + // If Crunch is false, you should set the sector height back + // the way it was and call P_ChangeSector again + // to undo the changes. + // + + private World world; + + public SectorAction(World world) + { + this.world = world; + + InitSectorChange(); + } + + + + private bool crushChange; + private bool noFit; + private Func crushThingFunc; + + private void InitSectorChange() + { + crushThingFunc = CrushThing; + } + + private bool ThingHeightClip(Mobj thing) + { + var onFloor = (thing.Z == thing.FloorZ); + + var tm = world.ThingMovement; + + tm.CheckPosition(thing, thing.X, thing.Y); + // What about stranding a monster partially off an edge? + + thing.FloorZ = tm.CurrentFloorZ; + thing.CeilingZ = tm.CurrentCeilingZ; + + if (onFloor) + { + // Walking monsters rise and fall with the floor. + thing.Z = thing.FloorZ; + } + else + { + // Don't adjust a floating monster unless forced to. + if (thing.Z + thing.Height > thing.CeilingZ) + { + thing.Z = thing.CeilingZ - thing.Height; + } + } + + if (thing.CeilingZ - thing.FloorZ < thing.Height) + { + return false; + } + + return true; + } + + private bool CrushThing(Mobj thing) + { + if (ThingHeightClip(thing)) + { + // Keep checking. + return true; + } + + // Crunch bodies to giblets. + if (thing.Health <= 0) + { + thing.SetState(MobjState.Gibs); + thing.Flags &= ~MobjFlags.Solid; + thing.Height = Fixed.Zero; + thing.Radius = Fixed.Zero; + + // Keep checking. + return true; + } + + // Crunch dropped items. + if ((thing.Flags & MobjFlags.Dropped) != 0) + { + world.ThingAllocation.RemoveMobj(thing); + + // Keep checking. + return true; + } + + if ((thing.Flags & MobjFlags.Shootable) == 0) + { + // Assume it is bloody gibs or something. + return true; + } + + noFit = true; + + if (crushChange && (world.LevelTime & 3) == 0) + { + world.ThingInteraction.DamageMobj(thing, null, null, 10); + + // Spray blood in a random direction. + var blood = world.ThingAllocation.SpawnMobj( + thing.X, + thing.Y, + thing.Z + thing.Height / 2, + MobjType.Blood); + + var random = world.Random; + blood.MomX = new Fixed((random.Next() - random.Next()) << 12); + blood.MomY = new Fixed((random.Next() - random.Next()) << 12); + } + + // Keep checking (crush other things). + return true; + } + + private bool ChangeSector(Sector sector, bool crunch) + { + noFit = false; + crushChange = crunch; + + var bm = world.Map.BlockMap; + var blockBox = sector.BlockBox; + + // Re-check heights for all things near the moving sector. + for (var x = blockBox.Left(); x <= blockBox.Right(); x++) + { + for (var y = blockBox.Bottom(); y <= blockBox.Top(); y++) + { + bm.IterateThings(x, y, crushThingFunc); + } + } + + return noFit; + } + + /// + /// Move a plane (floor or ceiling) and check for crushing. + /// + public SectorActionResult MovePlane( + Sector sector, + Fixed speed, + Fixed dest, + bool crush, + int floorOrCeiling, + int direction) + { + switch (floorOrCeiling) + { + case 0: + // Floor. + switch (direction) + { + case -1: + // Down. + if (sector.FloorHeight - speed < dest) + { + var lastPos = sector.FloorHeight; + sector.FloorHeight = dest; + if (ChangeSector(sector, crush)) + { + sector.FloorHeight = lastPos; + ChangeSector(sector, crush); + } + + return SectorActionResult.PastDestination; + } + else + { + var lastPos = sector.FloorHeight; + sector.FloorHeight -= speed; + if (ChangeSector(sector, crush)) + { + sector.FloorHeight = lastPos; + ChangeSector(sector, crush); + + return SectorActionResult.Crushed; + } + } + + break; + + case 1: + // Up. + if (sector.FloorHeight + speed > dest) + { + var lastPos = sector.FloorHeight; + sector.FloorHeight = dest; + if (ChangeSector(sector, crush)) + { + sector.FloorHeight = lastPos; + ChangeSector(sector, crush); + } + + return SectorActionResult.PastDestination; + } + else + { + // Could get crushed. + var lastPos = sector.FloorHeight; + sector.FloorHeight += speed; + if (ChangeSector(sector, crush)) + { + if (crush) + { + return SectorActionResult.Crushed; + } + sector.FloorHeight = lastPos; + ChangeSector(sector, crush); + + return SectorActionResult.Crushed; + } + } + + break; + } + break; + + case 1: + // Ceiling. + switch (direction) + { + case -1: + // Down. + if (sector.CeilingHeight - speed < dest) + { + var lastPos = sector.CeilingHeight; + sector.CeilingHeight = dest; + if (ChangeSector(sector, crush)) + { + sector.CeilingHeight = lastPos; + ChangeSector(sector, crush); + } + + return SectorActionResult.PastDestination; + } + else + { + // Could get crushed. + var lastPos = sector.CeilingHeight; + sector.CeilingHeight -= speed; + if (ChangeSector(sector, crush)) + { + if (crush) + { + return SectorActionResult.Crushed; + } + sector.CeilingHeight = lastPos; + ChangeSector(sector, crush); + + return SectorActionResult.Crushed; + } + } + + break; + + case 1: + // UP + if (sector.CeilingHeight + speed > dest) + { + var lastPos = sector.CeilingHeight; + sector.CeilingHeight = dest; + if (ChangeSector(sector, crush)) + { + sector.CeilingHeight = lastPos; + ChangeSector(sector, crush); + } + + return SectorActionResult.PastDestination; + } + else + { + sector.CeilingHeight += speed; + ChangeSector(sector, crush); + } + + break; + } + + break; + } + + return SectorActionResult.OK; + } + + private Sector GetNextSector(LineDef line, Sector sector) + { + if ((line.Flags & LineFlags.TwoSided) == 0) + { + return null; + } + + if (line.FrontSector == sector) + { + return line.BackSector; + } + + return line.FrontSector; + } + + private Fixed FindLowestFloorSurrounding(Sector sector) + { + var floor = sector.FloorHeight; + + for (var i = 0; i < sector.Lines.Length; i++) + { + var check = sector.Lines[i]; + + var other = GetNextSector(check, sector); + if (other == null) + { + continue; + } + + if (other.FloorHeight < floor) + { + floor = other.FloorHeight; + } + } + + return floor; + } + + private Fixed FindHighestFloorSurrounding(Sector sector) + { + var floor = Fixed.FromInt(-500); + + for (var i = 0; i < sector.Lines.Length; i++) + { + var check = sector.Lines[i]; + + var other = GetNextSector(check, sector); + if (other == null) + { + continue; + } + + if (other.FloorHeight > floor) + { + floor = other.FloorHeight; + } + } + + return floor; + } + + private Fixed FindLowestCeilingSurrounding(Sector sector) + { + var height = Fixed.MaxValue; + + for (var i = 0; i < sector.Lines.Length; i++) + { + var check = sector.Lines[i]; + + var other = GetNextSector(check, sector); + if (other == null) + { + continue; + } + + if (other.CeilingHeight < height) + { + height = other.CeilingHeight; + } + } + + return height; + } + + private Fixed FindHighestCeilingSurrounding(Sector sector) + { + var height = Fixed.Zero; + + for (var i = 0; i < sector.Lines.Length; i++) + { + var check = sector.Lines[i]; + + var other = GetNextSector(check, sector); + if (other == null) + { + continue; + } + + if (other.CeilingHeight > height) + { + height = other.CeilingHeight; + } + } + + return height; + } + + private int FindSectorFromLineTag(LineDef line, int start) + { + var sectors = world.Map.Sectors; + + for (var i = start + 1; i < sectors.Length; i++) + { + if (sectors[i].Tag == line.Tag) + { + return i; + } + } + + return -1; + } + + + + //////////////////////////////////////////////////////////// + // Door + //////////////////////////////////////////////////////////// + + private static readonly Fixed doorSpeed = Fixed.FromInt(2); + private static readonly int doorWait = 150; + + /// + /// Open a door manually, no tag value. + /// + public void DoLocalDoor(LineDef line, Mobj thing) + { + // Check for locks. + var player = thing.Player; + + switch ((int)line.Special) + { + // Blue Lock. + case 26: + case 32: + if (player == null) + { + return; + } + + if (!player.Cards[(int)CardType.BlueCard] && + !player.Cards[(int)CardType.BlueSkull]) + { + player.SendMessage(DoomInfo.Strings.PD_BLUEK); + world.StartSound(player.Mobj, Sfx.OOF, SfxType.Voice); + return; + } + break; + + // Yellow Lock. + case 27: + case 34: + if (player == null) + { + return; + } + + if (!player.Cards[(int)CardType.YellowCard] && + !player.Cards[(int)CardType.YellowSkull]) + { + player.SendMessage(DoomInfo.Strings.PD_YELLOWK); + world.StartSound(player.Mobj, Sfx.OOF, SfxType.Voice); + return; + } + break; + + // Red Lock. + case 28: + case 33: + if (player == null) + { + return; + } + + if (!player.Cards[(int)CardType.RedCard] && + !player.Cards[(int)CardType.RedSkull]) + { + player.SendMessage(DoomInfo.Strings.PD_REDK); + world.StartSound(player.Mobj, Sfx.OOF, SfxType.Voice); + return; + } + break; + } + + var sector = line.BackSide.Sector; + + // If the sector has an active thinker, use it. + if (sector.SpecialData != null) + { + var door = (VerticalDoor)sector.SpecialData; + switch ((int)line.Special) + { + // Only for "raise" doors, not "open"s. + case 1: + case 26: + case 27: + case 28: + case 117: + if (door.Direction == -1) + { + // Go back up. + door.Direction = 1; + } + else + { + if (thing.Player == null) + { + // Bad guys never close doors. + return; + } + + // Start going down immediately. + door.Direction = -1; + } + return; + } + } + + // For proper sound. + switch ((int)line.Special) + { + // Blazing door raise. + case 117: + + // Blazing door open. + case 118: + world.StartSound(sector.SoundOrigin, Sfx.BDOPN, SfxType.Misc); + break; + + // Normal door sound. + case 1: + case 31: + world.StartSound(sector.SoundOrigin, Sfx.DOROPN, SfxType.Misc); + break; + + // Locked door sound. + default: + world.StartSound(sector.SoundOrigin, Sfx.DOROPN, SfxType.Misc); + break; + } + + // New door thinker. + var newDoor = new VerticalDoor(world); + world.Thinkers.Add(newDoor); + sector.SpecialData = newDoor; + newDoor.Sector = sector; + newDoor.Direction = 1; + newDoor.Speed = doorSpeed; + newDoor.TopWait = doorWait; + + switch ((int)line.Special) + { + case 1: + case 26: + case 27: + case 28: + newDoor.Type = VerticalDoorType.Normal; + break; + + case 31: + case 32: + case 33: + case 34: + newDoor.Type = VerticalDoorType.Open; + line.Special = 0; + break; + + // Blazing door raise. + case 117: + newDoor.Type = VerticalDoorType.BlazeRaise; + newDoor.Speed = doorSpeed * 4; + break; + + // Blazing door open. + case 118: + newDoor.Type = VerticalDoorType.BlazeOpen; + line.Special = 0; + newDoor.Speed = doorSpeed * 4; + break; + } + + // Find the top and bottom of the movement range. + newDoor.TopHeight = FindLowestCeilingSurrounding(sector); + newDoor.TopHeight -= Fixed.FromInt(4); + } + + public bool DoDoor(LineDef line, VerticalDoorType type) + { + var sectors = world.Map.Sectors; + var setcorNumber = -1; + var result = false; + + while ((setcorNumber = FindSectorFromLineTag(line, setcorNumber)) >= 0) + { + var sector = sectors[setcorNumber]; + if (sector.SpecialData != null) + { + continue; + } + + result = true; + + // New door thinker. + var door = new VerticalDoor(world); + world.Thinkers.Add(door); + sector.SpecialData = door; + door.Sector = sector; + door.Type = type; + door.TopWait = doorWait; + door.Speed = doorSpeed; + + switch (type) + { + case VerticalDoorType.BlazeClose: + door.TopHeight = FindLowestCeilingSurrounding(sector); + door.TopHeight -= Fixed.FromInt(4); + door.Direction = -1; + door.Speed = doorSpeed * 4; + world.StartSound(door.Sector.SoundOrigin, Sfx.BDCLS, SfxType.Misc); + break; + + case VerticalDoorType.Close: + door.TopHeight = FindLowestCeilingSurrounding(sector); + door.TopHeight -= Fixed.FromInt(4); + door.Direction = -1; + world.StartSound(door.Sector.SoundOrigin, Sfx.DORCLS, SfxType.Misc); + break; + + case VerticalDoorType.Close30ThenOpen: + door.TopHeight = sector.CeilingHeight; + door.Direction = -1; + world.StartSound(door.Sector.SoundOrigin, Sfx.DORCLS, SfxType.Misc); + break; + + case VerticalDoorType.BlazeRaise: + case VerticalDoorType.BlazeOpen: + door.Direction = 1; + door.TopHeight = FindLowestCeilingSurrounding(sector); + door.TopHeight -= Fixed.FromInt(4); + door.Speed = doorSpeed * 4; + if (door.TopHeight != sector.CeilingHeight) + { + world.StartSound(door.Sector.SoundOrigin, Sfx.BDOPN, SfxType.Misc); + } + break; + + case VerticalDoorType.Normal: + case VerticalDoorType.Open: + door.Direction = 1; + door.TopHeight = FindLowestCeilingSurrounding(sector); + door.TopHeight -= Fixed.FromInt(4); + if (door.TopHeight != sector.CeilingHeight) + { + world.StartSound(door.Sector.SoundOrigin, Sfx.DOROPN, SfxType.Misc); + } + break; + + default: + break; + } + + } + + return result; + } + + public bool DoLockedDoor(LineDef line, VerticalDoorType type, Mobj thing) + { + var player = thing.Player; + if (player == null) + { + return false; + } + + switch ((int)line.Special) + { + // Blue Lock. + case 99: + case 133: + if (player == null) + { + return false; + } + if (!player.Cards[(int)CardType.BlueCard] && + !player.Cards[(int)CardType.BlueSkull]) + { + player.SendMessage(DoomInfo.Strings.PD_BLUEO); + world.StartSound(player.Mobj, Sfx.OOF, SfxType.Voice); + return false; + } + break; + + // Red Lock. + case 134: + case 135: + if (player == null) + { + return false; + } + if (!player.Cards[(int)CardType.RedCard] && + !player.Cards[(int)CardType.RedSkull]) + { + player.SendMessage(DoomInfo.Strings.PD_REDO); + world.StartSound(player.Mobj, Sfx.OOF, SfxType.Voice); + return false; + } + break; + + // Yellow Lock. + case 136: + case 137: + if (player == null) + { + return false; + } + if (!player.Cards[(int)CardType.YellowCard] && + !player.Cards[(int)CardType.YellowSkull]) + { + player.SendMessage(DoomInfo.Strings.PD_YELLOWO); + world.StartSound(player.Mobj, Sfx.OOF, SfxType.Voice); + return false; + } + break; + } + + return DoDoor(line, type); + } + + + + //////////////////////////////////////////////////////////// + // Platform + //////////////////////////////////////////////////////////// + + // In plutonia MAP23, number of adjoining sectors can be 44. + private static readonly int maxAdjoiningSectorCount = 64; + private Fixed[] heightList = new Fixed[maxAdjoiningSectorCount]; + + private Fixed FindNextHighestFloor(Sector sector, Fixed currentHeight) + { + var height = currentHeight; + var h = 0; + + for (var i = 0; i < sector.Lines.Length; i++) + { + var check = sector.Lines[i]; + + var other = GetNextSector(check, sector); + if (other == null) + { + continue; + } + + if (other.FloorHeight > height) + { + heightList[h++] = other.FloorHeight; + } + + // Check for overflow. + if (h >= heightList.Length) + { + // Exit. + throw new Exception("Too many adjoining sectors!"); + } + } + + // Find lowest height in list. + if (h == 0) + { + return currentHeight; + } + + var min = heightList[0]; + + // Range checking? + for (var i = 1; i < h; i++) + { + if (heightList[i] < min) + { + min = heightList[i]; + } + } + + return min; + } + + + private static readonly int platformWait = 3; + private static readonly Fixed platformSpeed = Fixed.One; + + public bool DoPlatform(LineDef line, PlatformType type, int amount) + { + // Activate all plats that are in stasis. + switch (type) + { + case PlatformType.PerpetualRaise: + ActivateInStasis(line.Tag); + break; + + default: + break; + } + + var sectors = world.Map.Sectors; + var sectorNumber = -1; + var result = false; + + while ((sectorNumber = FindSectorFromLineTag(line, sectorNumber)) >= 0) + { + var sector = sectors[sectorNumber]; + if (sector.SpecialData != null) + { + continue; + } + + result = true; + + // Find lowest and highest floors around sector. + var plat = new Platform(world); + world.Thinkers.Add(plat); + plat.Type = type; + plat.Sector = sector; + plat.Sector.SpecialData = plat; + plat.Crush = false; + plat.Tag = line.Tag; + + switch (type) + { + case PlatformType.RaiseToNearestAndChange: + plat.Speed = platformSpeed / 2; + sector.FloorFlat = line.FrontSide.Sector.FloorFlat; + plat.High = FindNextHighestFloor(sector, sector.FloorHeight); + plat.Wait = 0; + plat.Status = PlatformState.Up; + // No more damage, if applicable. + sector.Special = 0; + world.StartSound(sector.SoundOrigin, Sfx.STNMOV, SfxType.Misc); + break; + + case PlatformType.RaiseAndChange: + plat.Speed = platformSpeed / 2; + sector.FloorFlat = line.FrontSide.Sector.FloorFlat; + plat.High = sector.FloorHeight + amount * Fixed.One; + plat.Wait = 0; + plat.Status = PlatformState.Up; + world.StartSound(sector.SoundOrigin, Sfx.STNMOV, SfxType.Misc); + break; + + case PlatformType.DownWaitUpStay: + plat.Speed = platformSpeed * 4; + plat.Low = FindLowestFloorSurrounding(sector); + if (plat.Low > sector.FloorHeight) + { + plat.Low = sector.FloorHeight; + } + plat.High = sector.FloorHeight; + plat.Wait = 35 * platformWait; + plat.Status = PlatformState.Down; + world.StartSound(sector.SoundOrigin, Sfx.PSTART, SfxType.Misc); + break; + + case PlatformType.BlazeDwus: + plat.Speed = platformSpeed * 8; + plat.Low = FindLowestFloorSurrounding(sector); + if (plat.Low > sector.FloorHeight) + { + plat.Low = sector.FloorHeight; + } + plat.High = sector.FloorHeight; + plat.Wait = 35 * platformWait; + plat.Status = PlatformState.Down; + world.StartSound(sector.SoundOrigin, Sfx.PSTART, SfxType.Misc); + break; + + case PlatformType.PerpetualRaise: + plat.Speed = platformSpeed; + plat.Low = FindLowestFloorSurrounding(sector); + if (plat.Low > sector.FloorHeight) + { + plat.Low = sector.FloorHeight; + } + plat.High = FindHighestFloorSurrounding(sector); + if (plat.High < sector.FloorHeight) + { + plat.High = sector.FloorHeight; + } + plat.Wait = 35 * platformWait; + plat.Status = (PlatformState)(world.Random.Next() & 1); + world.StartSound(sector.SoundOrigin, Sfx.PSTART, SfxType.Misc); + break; + } + + AddActivePlatform(plat); + } + + return result; + } + + + private static readonly int maxPlatformCount = 60; + private Platform[] activePlatforms = new Platform[maxPlatformCount]; + + public void ActivateInStasis(int tag) + { + for (var i = 0; i < activePlatforms.Length; i++) + { + if (activePlatforms[i] != null && + activePlatforms[i].Tag == tag && + activePlatforms[i].Status == PlatformState.InStasis) + { + activePlatforms[i].Status = activePlatforms[i].OldStatus; + activePlatforms[i].ThinkerState = ThinkerState.Active; + } + } + } + + public void StopPlatform(LineDef line) + { + for (var j = 0; j < activePlatforms.Length; j++) + { + if (activePlatforms[j] != null && + activePlatforms[j].Status != PlatformState.InStasis && + activePlatforms[j].Tag == line.Tag) + { + activePlatforms[j].OldStatus = activePlatforms[j].Status; + activePlatforms[j].Status = PlatformState.InStasis; + activePlatforms[j].ThinkerState = ThinkerState.InStasis; + } + } + } + + public void AddActivePlatform(Platform platform) + { + for (var i = 0; i < activePlatforms.Length; i++) + { + if (activePlatforms[i] == null) + { + activePlatforms[i] = platform; + + return; + } + } + + throw new Exception("Too many active platforms!"); + } + + public void RemoveActivePlatform(Platform platform) + { + for (var i = 0; i < activePlatforms.Length; i++) + { + if (platform == activePlatforms[i]) + { + activePlatforms[i].Sector.SpecialData = null; + world.Thinkers.Remove(activePlatforms[i]); + activePlatforms[i] = null; + return; + } + } + + throw new Exception("The platform was not found!"); + } + + + + //////////////////////////////////////////////////////////// + // Floor + //////////////////////////////////////////////////////////// + + private static readonly Fixed floorSpeed = Fixed.One; + + public bool DoFloor(LineDef line, FloorMoveType type) + { + var sectors = world.Map.Sectors; + var sectorNumber = -1; + var result = false; + + while ((sectorNumber = FindSectorFromLineTag(line, sectorNumber)) >= 0) + { + var sector = sectors[sectorNumber]; + + // Already moving? If so, keep going... + if (sector.SpecialData != null) + { + continue; + } + + result = true; + + // New floor thinker. + var floor = new FloorMove(world); + world.Thinkers.Add(floor); + sector.SpecialData = floor; + floor.Type = type; + floor.Crush = false; + + switch (type) + { + case FloorMoveType.LowerFloor: + floor.Direction = -1; + floor.Sector = sector; + floor.Speed = floorSpeed; + floor.FloorDestHeight = FindHighestFloorSurrounding(sector); + break; + + case FloorMoveType.LowerFloorToLowest: + floor.Direction = -1; + floor.Sector = sector; + floor.Speed = floorSpeed; + floor.FloorDestHeight = FindLowestFloorSurrounding(sector); + break; + + case FloorMoveType.TurboLower: + floor.Direction = -1; + floor.Sector = sector; + floor.Speed = floorSpeed * 4; + floor.FloorDestHeight = FindHighestFloorSurrounding(sector); + if (floor.FloorDestHeight != sector.FloorHeight) + { + floor.FloorDestHeight += Fixed.FromInt(8); + } + break; + + case FloorMoveType.RaiseFloorCrush: + case FloorMoveType.RaiseFloor: + if (type == FloorMoveType.RaiseFloorCrush) + { + floor.Crush = true; + } + floor.Direction = 1; + floor.Sector = sector; + floor.Speed = floorSpeed; + floor.FloorDestHeight = FindLowestCeilingSurrounding(sector); + if (floor.FloorDestHeight > sector.CeilingHeight) + { + floor.FloorDestHeight = sector.CeilingHeight; + } + floor.FloorDestHeight -= Fixed.FromInt(8) * (type == FloorMoveType.RaiseFloorCrush ? 1 : 0); + break; + + case FloorMoveType.RaiseFloorTurbo: + floor.Direction = 1; + floor.Sector = sector; + floor.Speed = floorSpeed * 4; + floor.FloorDestHeight = FindNextHighestFloor(sector, sector.FloorHeight); + break; + + case FloorMoveType.RaiseFloorToNearest: + floor.Direction = 1; + floor.Sector = sector; + floor.Speed = floorSpeed; + floor.FloorDestHeight = FindNextHighestFloor(sector, sector.FloorHeight); + break; + + case FloorMoveType.RaiseFloor24: + floor.Direction = 1; + floor.Sector = sector; + floor.Speed = floorSpeed; + floor.FloorDestHeight = floor.Sector.FloorHeight + Fixed.FromInt(24); + break; + + case FloorMoveType.RaiseFloor512: + floor.Direction = 1; + floor.Sector = sector; + floor.Speed = floorSpeed; + floor.FloorDestHeight = floor.Sector.FloorHeight + Fixed.FromInt(512); + break; + + case FloorMoveType.RaiseFloor24AndChange: + floor.Direction = 1; + floor.Sector = sector; + floor.Speed = floorSpeed; + floor.FloorDestHeight = floor.Sector.FloorHeight + Fixed.FromInt(24); + sector.FloorFlat = line.FrontSector.FloorFlat; + sector.Special = line.FrontSector.Special; + break; + + case FloorMoveType.RaiseToTexture: + var min = int.MaxValue; + floor.Direction = 1; + floor.Sector = sector; + floor.Speed = floorSpeed; + var textures = world.Map.Textures; + for (var i = 0; i < sector.Lines.Length; i++) + { + if ((sector.Lines[i].Flags & LineFlags.TwoSided) != 0) + { + var frontSide = sector.Lines[i].FrontSide; + if (frontSide.BottomTexture >= 0) + { + if (textures[frontSide.BottomTexture].Height < min) + { + min = textures[frontSide.BottomTexture].Height; + } + } + var backSide = sector.Lines[i].BackSide; + if (backSide.BottomTexture >= 0) + { + if (textures[backSide.BottomTexture].Height < min) + { + min = textures[backSide.BottomTexture].Height; + } + } + } + } + floor.FloorDestHeight = floor.Sector.FloorHeight + Fixed.FromInt(min); + break; + + case FloorMoveType.LowerAndChange: + floor.Direction = -1; + floor.Sector = sector; + floor.Speed = floorSpeed; + floor.FloorDestHeight = FindLowestFloorSurrounding(sector); + floor.Texture = sector.FloorFlat; + for (var i = 0; i < sector.Lines.Length; i++) + { + if ((sector.Lines[i].Flags & LineFlags.TwoSided) != 0) + { + if (sector.Lines[i].FrontSide.Sector.Number == sectorNumber) + { + sector = sector.Lines[i].BackSide.Sector; + if (sector.FloorHeight == floor.FloorDestHeight) + { + floor.Texture = sector.FloorFlat; + floor.NewSpecial = sector.Special; + break; + } + } + else + { + sector = sector.Lines[i].FrontSide.Sector; + if (sector.FloorHeight == floor.FloorDestHeight) + { + floor.Texture = sector.FloorFlat; + floor.NewSpecial = sector.Special; + break; + } + } + } + } + break; + } + } + + return result; + } + + + public bool BuildStairs(LineDef line, StairType type) + { + var sectors = world.Map.Sectors; + var sectorNumber = -1; + var result = false; + + while ((sectorNumber = FindSectorFromLineTag(line, sectorNumber)) >= 0) + { + var sector = sectors[sectorNumber]; + + // Already moving? If so, keep going... + if (sector.SpecialData != null) + { + continue; + } + + result = true; + + // New floor thinker. + var floor = new FloorMove(world); + world.Thinkers.Add(floor); + sector.SpecialData = floor; + floor.Direction = 1; + floor.Sector = sector; + + Fixed speed; + Fixed stairSize; + switch (type) + { + case StairType.Build8: + speed = floorSpeed / 4; + stairSize = Fixed.FromInt(8); + break; + case StairType.Turbo16: + speed = floorSpeed * 4; + stairSize = Fixed.FromInt(16); + break; + default: + throw new Exception("Unknown stair type!"); + } + + floor.Speed = speed; + var height = sector.FloorHeight + stairSize; + floor.FloorDestHeight = height; + + var texture = sector.FloorFlat; + + // Find next sector to raise. + // 1. Find 2-sided line with same sector side[0]. + // 2. Other side is the next sector to raise. + bool ok; + do + { + ok = false; + + for (var i = 0; i < sector.Lines.Length; i++) + { + if (((sector.Lines[i]).Flags & LineFlags.TwoSided) == 0) + { + continue; + } + + var target = (sector.Lines[i]).FrontSector; + var newSectorNumber = target.Number; + + if (sectorNumber != newSectorNumber) + { + continue; + } + + target = (sector.Lines[i]).BackSector; + newSectorNumber = target.Number; + + if (target.FloorFlat != texture) + { + continue; + } + + height += stairSize; + + if (target.SpecialData != null) + { + continue; + } + + sector = target; + sectorNumber = newSectorNumber; + floor = new FloorMove(world); + + world.Thinkers.Add(floor); + + sector.SpecialData = floor; + floor.Direction = 1; + floor.Sector = sector; + floor.Speed = speed; + floor.FloorDestHeight = height; + ok = true; + break; + } + } while (ok); + } + + return result; + } + + + + //////////////////////////////////////////////////////////// + // Ceiling + //////////////////////////////////////////////////////////// + + public bool DoCeiling(LineDef line, CeilingMoveType type) + { + // Reactivate in-stasis ceilings...for certain types. + switch (type) + { + case CeilingMoveType.FastCrushAndRaise: + case CeilingMoveType.SilentCrushAndRaise: + case CeilingMoveType.CrushAndRaise: + ActivateInStasisCeiling(line); + break; + + default: + break; + } + + var sectors = world.Map.Sectors; + var sectorNumber = -1; + var result = false; + + while ((sectorNumber = FindSectorFromLineTag(line, sectorNumber)) >= 0) + { + var sector = sectors[sectorNumber]; + if (sector.SpecialData != null) + { + continue; + } + + result = true; + + // New door thinker. + var ceiling = new CeilingMove(world); + world.Thinkers.Add(ceiling); + sector.SpecialData = ceiling; + ceiling.Sector = sector; + ceiling.Crush = false; + + switch (type) + { + case CeilingMoveType.FastCrushAndRaise: + ceiling.Crush = true; + ceiling.TopHeight = sector.CeilingHeight; + ceiling.BottomHeight = sector.FloorHeight + Fixed.FromInt(8); + ceiling.Direction = -1; + ceiling.Speed = CeilingSpeed * 2; + break; + + case CeilingMoveType.SilentCrushAndRaise: + case CeilingMoveType.CrushAndRaise: + case CeilingMoveType.LowerAndCrush: + case CeilingMoveType.LowerToFloor: + if (type == CeilingMoveType.SilentCrushAndRaise + || type == CeilingMoveType.CrushAndRaise) + { + ceiling.Crush = true; + ceiling.TopHeight = sector.CeilingHeight; + } + ceiling.BottomHeight = sector.FloorHeight; + if (type != CeilingMoveType.LowerToFloor) + { + ceiling.BottomHeight += Fixed.FromInt(8); + } + ceiling.Direction = -1; + ceiling.Speed = CeilingSpeed; + break; + + case CeilingMoveType.RaiseToHighest: + ceiling.TopHeight = FindHighestCeilingSurrounding(sector); + ceiling.Direction = 1; + ceiling.Speed = CeilingSpeed; + break; + } + + ceiling.Tag = sector.Tag; + ceiling.Type = type; + AddActiveCeiling(ceiling); + } + + return result; + } + + + public static readonly Fixed CeilingSpeed = Fixed.One; + public static readonly int CeilingWwait = 150; + + private static readonly int maxCeilingCount = 30; + + private CeilingMove[] activeCeilings = new CeilingMove[maxCeilingCount]; + + public void AddActiveCeiling(CeilingMove ceiling) + { + for (var i = 0; i < activeCeilings.Length; i++) + { + if (activeCeilings[i] == null) + { + activeCeilings[i] = ceiling; + + return; + } + } + } + + public void RemoveActiveCeiling(CeilingMove ceiling) + { + for (var i = 0; i < activeCeilings.Length; i++) + { + if (activeCeilings[i] == ceiling) + { + activeCeilings[i].Sector.SpecialData = null; + world.Thinkers.Remove(activeCeilings[i]); + activeCeilings[i] = null; + break; + } + } + } + + public bool CheckActiveCeiling(CeilingMove ceiling) + { + if (ceiling == null) + { + return false; + } + + for (var i = 0; i < activeCeilings.Length; i++) + { + if (activeCeilings[i] == ceiling) + { + return true; + } + } + + return false; + } + + public void ActivateInStasisCeiling(LineDef line) + { + for (var i = 0; i < activeCeilings.Length; i++) + { + if (activeCeilings[i] != null && + activeCeilings[i].Tag == line.Tag && + activeCeilings[i].Direction == 0) + { + activeCeilings[i].Direction = activeCeilings[i].OldDirection; + activeCeilings[i].ThinkerState = ThinkerState.Active; + } + } + } + + public bool CeilingCrushStop(LineDef line) + { + var result = false; + + for (var i = 0; i < activeCeilings.Length; i++) + { + if (activeCeilings[i] != null && + activeCeilings[i].Tag == line.Tag && + activeCeilings[i].Direction != 0) + { + activeCeilings[i].OldDirection = activeCeilings[i].Direction; + activeCeilings[i].ThinkerState = ThinkerState.InStasis; + activeCeilings[i].Direction = 0; + result = true; + } + } + + return result; + } + + + + //////////////////////////////////////////////////////////// + // Teleport + //////////////////////////////////////////////////////////// + + public bool Teleport(LineDef line, int side, Mobj thing) + { + // Don't teleport missiles. + if ((thing.Flags & MobjFlags.Missile) != 0) + { + return false; + } + + // Don't teleport if hit back of line, so you can get out of teleporter. + if (side == 1) + { + return false; + } + + var sectors = world.Map.Sectors; + var tag = line.Tag; + + for (var i = 0; i < sectors.Length; i++) + { + if (sectors[i].Tag == tag) + { + foreach (var thinker in world.Thinkers) + { + var dest = thinker as Mobj; + + if (dest == null) + { + // Not a mobj. + continue; + } + + if (dest.Type != MobjType.Teleportman) + { + // Not a teleportman. + continue; + } + + var sector = dest.Subsector.Sector; + + if (sector.Number != i) + { + // Wrong sector. + continue; + } + + var oldX = thing.X; + var oldY = thing.Y; + var oldZ = thing.Z; + + if (!world.ThingMovement.TeleportMove(thing, dest.X, dest.Y)) + { + return false; + } + + // This compatibility fix is based on Chocolate Doom's implementation. + if (world.Options.GameVersion != GameVersion.Final) + { + thing.Z = thing.FloorZ; + } + + if (thing.Player != null) + { + thing.Player.ViewZ = thing.Z + thing.Player.ViewHeight; + } + + var ta = world.ThingAllocation; + + // Spawn teleport fog at source position. + var fog1 = ta.SpawnMobj( + oldX, + oldY, + oldZ, + MobjType.Tfog); + world.StartSound(fog1, Sfx.TELEPT, SfxType.Misc); + + // Destination position. + var angle = dest.Angle; + var fog2 = ta.SpawnMobj( + dest.X + 20 * Trig.Cos(angle), + dest.Y + 20 * Trig.Sin(angle), + thing.Z, + MobjType.Tfog); + world.StartSound(fog2, Sfx.TELEPT, SfxType.Misc); + + if (thing.Player != null) + { + // Don't move for a bit. + thing.ReactionTime = 18; + } + + thing.Angle = dest.Angle; + thing.MomX = thing.MomY = thing.MomZ = Fixed.Zero; + + return true; + } + } + } + + return false; + } + + + + //////////////////////////////////////////////////////////// + // Lighting + //////////////////////////////////////////////////////////// + + public void TurnTagLightsOff(LineDef line) + { + var sectors = world.Map.Sectors; + + for (var i = 0; i < sectors.Length; i++) + { + var sector = sectors[i]; + + if (sector.Tag == line.Tag) + { + var min = sector.LightLevel; + + for (var j = 0; j < sector.Lines.Length; j++) + { + var target = GetNextSector(sector.Lines[j], sector); + if (target == null) + { + continue; + } + + if (target.LightLevel < min) + { + min = target.LightLevel; + } + } + + sector.LightLevel = min; + } + } + } + + public void LightTurnOn(LineDef line, int bright) + { + var sectors = world.Map.Sectors; + + for (var i = 0; i < sectors.Length; i++) + { + var sector = sectors[i]; + + if (sector.Tag == line.Tag) + { + // bright = 0 means to search for highest light level surrounding sector. + if (bright == 0) + { + for (var j = 0; j < sector.Lines.Length; j++) + { + var target = GetNextSector(sector.Lines[j], sector); + if (target == null) + { + continue; + } + + if (target.LightLevel > bright) + { + bright = target.LightLevel; + } + } + } + + sector.LightLevel = bright; + } + } + } + + + public void StartLightStrobing(LineDef line) + { + var sectors = world.Map.Sectors; + var sectorNumber = -1; + + while ((sectorNumber = FindSectorFromLineTag(line, sectorNumber)) >= 0) + { + var sector = sectors[sectorNumber]; + + if (sector.SpecialData != null) + { + continue; + } + + world.LightingChange.SpawnStrobeFlash(sector, StrobeFlash.SlowDark, false); + } + } + + + + //////////////////////////////////////////////////////////// + // Miscellaneous + //////////////////////////////////////////////////////////// + + public bool DoDonut(LineDef line) + { + var sectors = world.Map.Sectors; + var sectorNumber = -1; + var result = false; + + while ((sectorNumber = FindSectorFromLineTag(line, sectorNumber)) >= 0) + { + var s1 = sectors[sectorNumber]; + + // Already moving? If so, keep going... + if (s1.SpecialData != null) + { + continue; + } + + result = true; + + var s2 = GetNextSector(s1.Lines[0], s1); + + // + // The code below is based on Chocolate Doom's implementation. + // + + if (s2 == null) + { + break; + } + + for (var i = 0; i < s2.Lines.Length; i++) + { + var s3 = s2.Lines[i].BackSector; + + if (s3 == s1) + { + continue; + } + + if (s3 == null) + { + // Undefined behavior in Vanilla Doom. + return result; + } + + var thinkers = world.Thinkers; + + // Spawn rising slime. + var floor1 = new FloorMove(world); + thinkers.Add(floor1); + s2.SpecialData = floor1; + floor1.Type = FloorMoveType.DonutRaise; + floor1.Crush = false; + floor1.Direction = 1; + floor1.Sector = s2; + floor1.Speed = floorSpeed / 2; + floor1.Texture = s3.FloorFlat; + floor1.NewSpecial = 0; + floor1.FloorDestHeight = s3.FloorHeight; + + // Spawn lowering donut-hole. + var floor2 = new FloorMove(world); + thinkers.Add(floor2); + s1.SpecialData = floor2; + floor2.Type = FloorMoveType.LowerFloor; + floor2.Crush = false; + floor2.Direction = -1; + floor2.Sector = s1; + floor2.Speed = floorSpeed / 2; + floor2.FloorDestHeight = s3.FloorHeight; + + break; + } + } + + return result; + } + + + public void SpawnDoorCloseIn30(Sector sector) + { + var door = new VerticalDoor(world); + + world.Thinkers.Add(door); + + sector.SpecialData = door; + sector.Special = 0; + + door.Sector = sector; + door.Direction = 0; + door.Type = VerticalDoorType.Normal; + door.Speed = doorSpeed; + door.TopCountDown = 30 * 35; + } + + public void SpawnDoorRaiseIn5Mins(Sector sector) + { + var door = new VerticalDoor(world); + + world.Thinkers.Add(door); + + sector.SpecialData = door; + sector.Special = 0; + + door.Sector = sector; + door.Direction = 2; + door.Type = VerticalDoorType.RaiseIn5Mins; + door.Speed = doorSpeed; + door.TopHeight = FindLowestCeilingSurrounding(sector); + door.TopHeight -= Fixed.FromInt(4); + door.TopWait = doorWait; + door.TopCountDown = 5 * 60 * 35; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/SectorActionResult.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/SectorActionResult.cs new file mode 100644 index 00000000..602481a3 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/SectorActionResult.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum SectorActionResult + { + OK, + Crushed, + PastDestination + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Specials.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Specials.cs new file mode 100644 index 00000000..47e973d3 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Specials.cs @@ -0,0 +1,332 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public class Specials + { + private static readonly int maxButtonCount = 32; + private static readonly int buttonTime = 35; + + private World world; + + private bool levelTimer; + private int levelTimeCount; + + private Button[] buttonList; + + private int[] textureTranslation; + private int[] flatTranslation; + + private LineDef[] scrollLines; + + public Specials(World world) + { + this.world = world; + + levelTimer = false; + + buttonList = new Button[maxButtonCount]; + for (var i = 0; i < buttonList.Length; i++) + { + buttonList[i] = new Button(); + } + + textureTranslation = new int[world.Map.Textures.Count]; + for (var i = 0; i < textureTranslation.Length; i++) + { + textureTranslation[i] = i; + } + + flatTranslation = new int[world.Map.Flats.Count]; + for (var i = 0; i < flatTranslation.Length; i++) + { + flatTranslation[i] = i; + } + } + + /// + /// After the map has been loaded, scan for specials that spawn thinkers. + /// + public void SpawnSpecials(int levelTimeCount) + { + levelTimer = true; + this.levelTimeCount = levelTimeCount; + SpawnSpecials(); + } + + /// + /// After the map has been loaded, scan for specials that spawn thinkers. + /// + public void SpawnSpecials() + { + // Init special sectors. + var lc = world.LightingChange; + var sa = world.SectorAction; + foreach (var sector in world.Map.Sectors) + { + if (sector.Special == 0) + { + continue; + } + + switch ((int)sector.Special) + { + case 1: + // Flickering lights. + lc.SpawnLightFlash(sector); + break; + + case 2: + // Strobe fast. + lc.SpawnStrobeFlash(sector, StrobeFlash.FastDark, false); + break; + + case 3: + // Strobe slow. + lc.SpawnStrobeFlash(sector, StrobeFlash.SlowDark, false); + break; + + case 4: + // Strobe fast / death slime. + lc.SpawnStrobeFlash(sector, StrobeFlash.FastDark, false); + sector.Special = (SectorSpecial)4; + break; + + case 8: + // Glowing light. + lc.SpawnGlowingLight(sector); + break; + case 9: + // Secret sector. + world.TotalSecrets++; + break; + + case 10: + // Door close in 30 seconds. + sa.SpawnDoorCloseIn30(sector); + break; + + case 12: + // Sync strobe slow. + lc.SpawnStrobeFlash(sector, StrobeFlash.SlowDark, true); + break; + + case 13: + // Sync strobe fast. + lc.SpawnStrobeFlash(sector, StrobeFlash.FastDark, true); + break; + + case 14: + // Door raise in 5 minutes. + sa.SpawnDoorRaiseIn5Mins(sector); + break; + + case 17: + lc.SpawnFireFlicker(sector); + break; + } + } + + var scrollList = new List(); + foreach (var line in world.Map.Lines) + { + switch ((int)line.Special) + { + case 48: + // Texture scroll. + scrollList.Add(line); + break; + } + } + scrollLines = scrollList.ToArray(); + } + + public void ChangeSwitchTexture(LineDef line, bool useAgain) + { + if (!useAgain) + { + line.Special = 0; + } + + var frontSide = line.FrontSide; + var topTexture = frontSide.TopTexture; + var middleTexture = frontSide.MiddleTexture; + var bottomTexture = frontSide.BottomTexture; + + var sound = Sfx.SWTCHN; + + // Exit switch? + if ((int)line.Special == 11) + { + sound = Sfx.SWTCHX; + } + + var switchList = world.Map.Textures.SwitchList; + + for (var i = 0; i < switchList.Length; i++) + { + if (switchList[i] == topTexture) + { + world.StartSound(line.SoundOrigin, sound, SfxType.Misc); + frontSide.TopTexture = switchList[i ^ 1]; + + if (useAgain) + { + StartButton(line, ButtonPosition.Top, switchList[i], buttonTime); + } + + return; + } + else + { + if (switchList[i] == middleTexture) + { + world.StartSound(line.SoundOrigin, sound, SfxType.Misc); + frontSide.MiddleTexture = switchList[i ^ 1]; + + if (useAgain) + { + StartButton(line, ButtonPosition.Middle, switchList[i], buttonTime); + } + + return; + } + else + { + if (switchList[i] == bottomTexture) + { + world.StartSound(line.SoundOrigin, sound, SfxType.Misc); + frontSide.BottomTexture = switchList[i ^ 1]; + + if (useAgain) + { + StartButton(line, ButtonPosition.Bottom, switchList[i], buttonTime); + } + + return; + } + } + } + } + } + + private void StartButton(LineDef line, ButtonPosition w, int texture, int time) + { + // See if button is already pressed. + for (var i = 0; i < maxButtonCount; i++) + { + if (buttonList[i].Timer != 0 && buttonList[i].Line == line) + { + return; + } + } + + for (var i = 0; i < maxButtonCount; i++) + { + if (buttonList[i].Timer == 0) + { + buttonList[i].Line = line; + buttonList[i].Position = w; + buttonList[i].Texture = texture; + buttonList[i].Timer = time; + buttonList[i].SoundOrigin = line.SoundOrigin; + return; + } + } + + throw new Exception("No button slots left!"); + } + + /// + /// Animate planes, scroll walls, etc. + /// + public void Update() + { + // Level timer. + if (levelTimer) + { + levelTimeCount--; + if (levelTimeCount == 0) + { + world.ExitLevel(); + } + } + + // Animate flats and textures globally. + var animations = world.Map.Animation.Animations; + for (var k = 0; k < animations.Length; k++) + { + var anim = animations[k]; + for (var i = anim.BasePic; i < anim.BasePic + anim.NumPics; i++) + { + var pic = anim.BasePic + ((world.LevelTime / anim.Speed + i) % anim.NumPics); + if (anim.IsTexture) + { + textureTranslation[i] = pic; + } + else + { + flatTranslation[i] = pic; + } + } + } + + // Animate line specials. + foreach (var line in scrollLines) + { + line.FrontSide.TextureOffset += Fixed.One; + } + + // Do buttons. + for (var i = 0; i < maxButtonCount; i++) + { + if (buttonList[i].Timer > 0) + { + buttonList[i].Timer--; + + if (buttonList[i].Timer == 0) + { + switch (buttonList[i].Position) + { + case ButtonPosition.Top: + buttonList[i].Line.FrontSide.TopTexture = buttonList[i].Texture; + break; + + case ButtonPosition.Middle: + buttonList[i].Line.FrontSide.MiddleTexture = buttonList[i].Texture; + break; + + case ButtonPosition.Bottom: + buttonList[i].Line.FrontSide.BottomTexture = buttonList[i].Texture; + break; + } + + world.StartSound(buttonList[i].SoundOrigin, Sfx.SWTCHN, SfxType.Misc, 50); + buttonList[i].Clear(); + } + } + } + } + + public int[] TextureTranslation => textureTranslation; + public int[] FlatTranslation => flatTranslation; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/StairType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/StairType.cs new file mode 100644 index 00000000..538b6e0e --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/StairType.cs @@ -0,0 +1,30 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum StairType + { + // Slowly build by 8. + Build8, + + // quickly build by 16. + Turbo16 + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/StatusBar.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/StatusBar.cs new file mode 100644 index 00000000..cecb8c89 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/StatusBar.cs @@ -0,0 +1,301 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class StatusBar + { + private World world; + + // Used for appopriately pained face. + private int oldHealth; + + // Used for evil grin. + private bool[] oldWeaponsOwned; + + // Count until face changes. + private int faceCount; + + // Current face index. + private int faceIndex; + + // A random number per tick. + private int randomNumber; + + private int priority; + + private int lastAttackDown; + private int lastPainOffset; + + private DoomRandom random; + + public StatusBar(World world) + { + this.world = world; + + oldHealth = -1; + oldWeaponsOwned = new bool[DoomInfo.WeaponInfos.Length]; + Array.Copy( + world.ConsolePlayer.WeaponOwned, + oldWeaponsOwned, + DoomInfo.WeaponInfos.Length); + faceCount = 0; + faceIndex = 0; + randomNumber = 0; + priority = 0; + lastAttackDown = -1; + lastPainOffset = 0; + + random = new DoomRandom(); + } + + public void Reset() + { + oldHealth = -1; + Array.Copy( + world.ConsolePlayer.WeaponOwned, + oldWeaponsOwned, + DoomInfo.WeaponInfos.Length); + faceCount = 0; + faceIndex = 0; + randomNumber = 0; + priority = 0; + lastAttackDown = -1; + lastPainOffset = 0; + } + + public void Update() + { + randomNumber = random.Next(); + UpdateFace(); + } + + private void UpdateFace() + { + var player = world.ConsolePlayer; + + if (priority < 10) + { + // Dead. + if (player.Health == 0) + { + priority = 9; + faceIndex = Face.DeadIndex; + faceCount = 1; + } + } + + if (priority < 9) + { + if (player.BonusCount != 0) + { + // Picking up bonus. + var doEvilGrin = false; + + for (var i = 0; i < DoomInfo.WeaponInfos.Length; i++) + { + if (oldWeaponsOwned[i] != player.WeaponOwned[i]) + { + doEvilGrin = true; + oldWeaponsOwned[i] = player.WeaponOwned[i]; + } + } + + if (doEvilGrin) + { + // Evil grin if just picked up weapon. + priority = 8; + faceCount = Face.EvilGrinDuration; + faceIndex = CalcPainOffset() + Face.EvilGrinOffset; + } + } + } + + if (priority < 8) + { + if (player.DamageCount != 0 && + player.Attacker != null && + player.Attacker != player.Mobj) + { + // Being attacked. + priority = 7; + + if (player.Health - oldHealth > Face.MuchPain) + { + faceCount = Face.TurnDuration; + faceIndex = CalcPainOffset() + Face.OuchOffset; + } + else + { + var attackerAngle = Geometry.PointToAngle( + player.Mobj.X, player.Mobj.Y, + player.Attacker.X, player.Attacker.Y); + + Angle diff; + bool right; + if (attackerAngle > player.Mobj.Angle) + { + // Whether right or left. + diff = attackerAngle - player.Mobj.Angle; + right = diff > Angle.Ang180; + } + else + { + // Whether left or right. + diff = player.Mobj.Angle - attackerAngle; + right = diff <= Angle.Ang180; + } + + faceCount = Face.TurnDuration; + faceIndex = CalcPainOffset(); + + if (diff < Angle.Ang45) + { + // Head-on. + faceIndex += Face.RampageOffset; + } + else if (right) + { + // Turn face right. + faceIndex += Face.TurnOffset; + } + else + { + // Turn face left. + faceIndex += Face.TurnOffset + 1; + } + } + } + } + + if (priority < 7) + { + // Getting hurt because of your own damn stupidity. + if (player.DamageCount != 0) + { + if (player.Health - oldHealth > Face.MuchPain) + { + priority = 7; + faceCount = Face.TurnDuration; + faceIndex = CalcPainOffset() + Face.OuchOffset; + } + else + { + priority = 6; + faceCount = Face.TurnDuration; + faceIndex = CalcPainOffset() + Face.RampageOffset; + } + } + } + + if (priority < 6) + { + // Rapid firing. + if (player.AttackDown) + { + if (lastAttackDown == -1) + { + lastAttackDown = Face.RampageDelay; + } + else if (--lastAttackDown == 0) + { + priority = 5; + faceIndex = CalcPainOffset() + Face.RampageOffset; + faceCount = 1; + lastAttackDown = 1; + } + } + else + { + lastAttackDown = -1; + } + } + + if (priority < 5) + { + // Invulnerability. + if ((player.Cheats & CheatFlags.GodMode) != 0 || + player.Powers[(int)PowerType.Invulnerability] != 0) + { + priority = 4; + + faceIndex = Face.GodIndex; + faceCount = 1; + } + } + + // Look left or look right if the facecount has timed out. + if (faceCount == 0) + { + faceIndex = CalcPainOffset() + (randomNumber % 3); + faceCount = Face.StraightFaceDuration; + priority = 0; + } + + faceCount--; + } + + private int CalcPainOffset() + { + var player = world.Options.Players[world.Options.ConsolePlayer]; + + var health = player.Health > 100 ? 100 : player.Health; + + if (health != oldHealth) + { + lastPainOffset = Face.Stride * + (((100 - health) * Face.PainFaceCount) / 101); + oldHealth = health; + } + + return lastPainOffset; + } + + public int FaceIndex => faceIndex; + + + + public static class Face + { + public static readonly int PainFaceCount = 5; + public static readonly int StraightFaceCount = 3; + public static readonly int TurnFaceCount = 2; + public static readonly int SpecialFaceCount = 3; + + public static readonly int Stride = StraightFaceCount + TurnFaceCount + SpecialFaceCount; + public static readonly int ExtraFaceCount = 2; + public static readonly int FaceCount = Stride * PainFaceCount + ExtraFaceCount; + + public static readonly int TurnOffset = StraightFaceCount; + public static readonly int OuchOffset = TurnOffset + TurnFaceCount; + public static readonly int EvilGrinOffset = OuchOffset + 1; + public static readonly int RampageOffset = EvilGrinOffset + 1; + public static readonly int GodIndex = PainFaceCount * Stride; + public static readonly int DeadIndex = GodIndex + 1; + + public static readonly int EvilGrinDuration = (2 * GameConst.TicRate); + public static readonly int StraightFaceDuration = (GameConst.TicRate / 2); + public static readonly int TurnDuration = (1 * GameConst.TicRate); + public static readonly int OuchDuration = (1 * GameConst.TicRate); + public static readonly int RampageDelay = (2 * GameConst.TicRate); + + public static readonly int MuchPain = 20; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/StrobeFlash.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/StrobeFlash.cs new file mode 100644 index 00000000..210cd5b6 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/StrobeFlash.cs @@ -0,0 +1,97 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class StrobeFlash : Thinker + { + public static readonly int StrobeBright = 5; + public static readonly int FastDark = 15; + public static readonly int SlowDark = 35; + + private World world; + + private Sector sector; + private int count; + private int minLight; + private int maxLight; + private int darkTime; + private int brightTime; + + public StrobeFlash(World world) + { + this.world = world; + } + + public override void Run() + { + if (--count > 0) + { + return; + } + + if (sector.LightLevel == minLight) + { + sector.LightLevel = maxLight; + count = brightTime; + } + else + { + sector.LightLevel = minLight; + count = darkTime; + } + } + + public Sector Sector + { + get => sector; + set => sector = value; + } + + public int Count + { + get => count; + set => count = value; + } + + public int MinLight + { + get => minLight; + set => minLight = value; + } + + public int MaxLight + { + get => maxLight; + set => maxLight = value; + } + + public int DarkTime + { + get => darkTime; + set => darkTime = value; + } + + public int BrightTime + { + get => brightTime; + set => brightTime = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThingAllocation.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThingAllocation.cs new file mode 100644 index 00000000..050985e7 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThingAllocation.cs @@ -0,0 +1,726 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class ThingAllocation + { + private World world; + + public ThingAllocation(World world) + { + this.world = world; + + InitSpawnMapThing(); + InitMultiPlayerRespawn(); + InitRespawnSpecials(); + } + + + + //////////////////////////////////////////////////////////// + // Spawn functions for level start + //////////////////////////////////////////////////////////// + + private MapThing[] playerStarts; + private List deathmatchStarts; + + private void InitSpawnMapThing() + { + playerStarts = new MapThing[Player.MaxPlayerCount]; + deathmatchStarts = new List(); + } + + /// + /// Spawn a mobj at the mapthing. + /// + public void SpawnMapThing(MapThing mt) + { + // Count deathmatch start positions. + if (mt.Type == 11) + { + if (deathmatchStarts.Count < 10) + { + deathmatchStarts.Add(mt); + } + + return; + } + + // Check for players specially. + if (mt.Type <= 4) + { + var playerNumber = mt.Type - 1; + + // This check is neccesary in Plutonia MAP12, + // which contains an unknown thing with type 0. + if (playerNumber < 0) + { + return; + } + + // Save spots for respawning in network games. + playerStarts[playerNumber] = mt; + + if (world.Options.Deathmatch == 0) + { + SpawnPlayer(mt); + } + + return; + } + + if (mt.Type == 11 || mt.Type <= 4) + { + return; + } + + // Check for apropriate skill level. + if (!world.Options.NetGame && ((int)mt.Flags & 16) != 0) + { + return; + } + + int bit; + if (world.Options.Skill == GameSkill.Baby) + { + bit = 1; + } + else if (world.Options.Skill == GameSkill.Nightmare) + { + bit = 4; + } + else + { + bit = 1 << ((int)world.Options.Skill - 1); + } + + if (((int)mt.Flags & bit) == 0) + { + return; + } + + // Find which type to spawn. + int i; + for (i = 0; i < DoomInfo.MobjInfos.Length; i++) + { + if (mt.Type == DoomInfo.MobjInfos[i].DoomEdNum) + { + break; + } + } + + if (i == DoomInfo.MobjInfos.Length) + { + throw new Exception("Unknown type!"); + } + + // Don't spawn keycards and players in deathmatch. + if (world.Options.Deathmatch != 0 && + (DoomInfo.MobjInfos[i].Flags & MobjFlags.NotDeathmatch) != 0) + { + return; + } + + // Don't spawn any monsters if -nomonsters. + if (world.Options.NoMonsters && + (i == (int)MobjType.Skull || + (DoomInfo.MobjInfos[i].Flags & MobjFlags.CountKill) != 0)) + { + return; + } + + // Spawn it. + Fixed x = mt.X; + Fixed y = mt.Y; + Fixed z; + if ((DoomInfo.MobjInfos[i].Flags & MobjFlags.SpawnCeiling) != 0) + { + z = Mobj.OnCeilingZ; + } + else + { + z = Mobj.OnFloorZ; + } + + var mobj = SpawnMobj(x, y, z, (MobjType)i); + + mobj.SpawnPoint = mt; + + if (mobj.Tics > 0) + { + mobj.Tics = 1 + (world.Random.Next() % mobj.Tics); + } + + if ((mobj.Flags & MobjFlags.CountKill) != 0) + { + world.TotalKills++; + } + + if ((mobj.Flags & MobjFlags.CountItem) != 0) + { + world.TotalItems++; + } + + mobj.Angle = mt.Angle; + + if ((mt.Flags & ThingFlags.Ambush) != 0) + { + mobj.Flags |= MobjFlags.Ambush; + } + } + + /// + /// Called when a player is spawned on the level. + /// Most of the player structure stays unchanged between levels. + /// + public void SpawnPlayer(MapThing mt) + { + var players = world.Options.Players; + var playerNumber = mt.Type - 1; + + // Not playing? + if (!players[playerNumber].InGame) + { + return; + } + + var player = players[playerNumber]; + + if (player.PlayerState == PlayerState.Reborn) + { + players[playerNumber].Reborn(); + } + + var x = mt.X; + var y = mt.Y; + var z = Mobj.OnFloorZ; + var mobj = SpawnMobj(x, y, z, MobjType.Player); + + if (mt.Type - 1 == world.Options.ConsolePlayer) + { + world.StatusBar.Reset(); + world.Options.Sound.SetListener(mobj); + } + + // Set color translations for player sprites. + if (playerNumber >= 1) + { + mobj.Flags |= (MobjFlags)((mt.Type - 1) << (int)MobjFlags.TransShift); + } + + mobj.Angle = mt.Angle; + mobj.Player = player; + mobj.Health = player.Health; + + player.Mobj = mobj; + player.PlayerState = PlayerState.Live; + player.Refire = 0; + player.Message = null; + player.MessageTime = 0; + player.DamageCount = 0; + player.BonusCount = 0; + player.ExtraLight = 0; + player.FixedColorMap = 0; + player.ViewHeight = Player.NormalViewHeight; + + // Setup gun psprite. + world.PlayerBehavior.SetupPlayerSprites(player); + + // Give all cards in death match mode. + if (world.Options.Deathmatch != 0) + { + for (var i = 0; i < (int)CardType.Count; i++) + { + player.Cards[i] = true; + } + } + } + + public IReadOnlyList PlayerStarts => playerStarts; + public IReadOnlyList DeathmatchStarts => deathmatchStarts; + + + + //////////////////////////////////////////////////////////// + // Thing spawn functions for the middle of a game + //////////////////////////////////////////////////////////// + + /// + /// Spawn a mobj at the given position as the given type. + /// + public Mobj SpawnMobj(Fixed x, Fixed y, Fixed z, MobjType type) + { + var mobj = new Mobj(world); + + var info = DoomInfo.MobjInfos[(int)type]; + + mobj.Type = type; + mobj.Info = info; + mobj.X = x; + mobj.Y = y; + mobj.Radius = info.Radius; + mobj.Height = info.Height; + mobj.Flags = info.Flags; + mobj.Health = info.SpawnHealth; + + if (world.Options.Skill != GameSkill.Nightmare) + { + mobj.ReactionTime = info.ReactionTime; + } + + mobj.LastLook = world.Random.Next() % Player.MaxPlayerCount; + + // Do not set the state with P_SetMobjState, + // because action routines can not be called yet. + var st = DoomInfo.States[(int)info.SpawnState]; + + mobj.State = st; + mobj.Tics = st.Tics; + mobj.Sprite = st.Sprite; + mobj.Frame = st.Frame; + + // Set subsector and/or block links. + world.ThingMovement.SetThingPosition(mobj); + + mobj.FloorZ = mobj.Subsector.Sector.FloorHeight; + mobj.CeilingZ = mobj.Subsector.Sector.CeilingHeight; + + if (z == Mobj.OnFloorZ) + { + mobj.Z = mobj.FloorZ; + } + else if (z == Mobj.OnCeilingZ) + { + mobj.Z = mobj.CeilingZ - mobj.Info.Height; + } + else + { + mobj.Z = z; + } + + world.Thinkers.Add(mobj); + + return mobj; + } + + /// + /// Remove the mobj from the level. + /// + public void RemoveMobj(Mobj mobj) + { + var tm = world.ThingMovement; + + if ((mobj.Flags & MobjFlags.Special) != 0 && + (mobj.Flags & MobjFlags.Dropped) == 0 && + (mobj.Type != MobjType.Inv) && + (mobj.Type != MobjType.Ins)) + { + itemRespawnQue[itemQueHead] = mobj.SpawnPoint; + itemRespawnTime[itemQueHead] = world.LevelTime; + itemQueHead = (itemQueHead + 1) & (itemQueSize - 1); + + // Lose one off the end? + if (itemQueHead == itemQueTail) + { + itemQueTail = (itemQueTail + 1) & (itemQueSize - 1); + } + } + + // Unlink from sector and block lists. + tm.UnsetThingPosition(mobj); + + // Stop any playing sound. + world.StopSound(mobj); + + // Free block. + world.Thinkers.Remove(mobj); + } + + /// + /// Get the speed of the given missile type. + /// Some missiles have different speeds according to the game setting. + /// + private int GetMissileSpeed(MobjType type) + { + if (world.Options.FastMonsters || world.Options.Skill == GameSkill.Nightmare) + { + switch (type) + { + case MobjType.Bruisershot: + case MobjType.Headshot: + case MobjType.Troopshot: + return 20 * Fixed.FracUnit; + default: + return DoomInfo.MobjInfos[(int)type].Speed; + } + } + else + { + return DoomInfo.MobjInfos[(int)type].Speed; + } + } + + /// + /// Moves the missile forward a bit and possibly explodes it right there. + /// + private void CheckMissileSpawn(Mobj missile) + { + missile.Tics -= world.Random.Next() & 3; + if (missile.Tics < 1) + { + missile.Tics = 1; + } + + // Move a little forward so an angle can be computed if it immediately explodes. + missile.X += (missile.MomX >> 1); + missile.Y += (missile.MomY >> 1); + missile.Z += (missile.MomZ >> 1); + + if (!world.ThingMovement.TryMove(missile, missile.X, missile.Y)) + { + world.ThingInteraction.ExplodeMissile(missile); + } + } + + /// + /// Shoot a missile from the source to the destination. + /// For monsters. + /// + public Mobj SpawnMissile(Mobj source, Mobj dest, MobjType type) + { + var missile = SpawnMobj( + source.X, + source.Y, + source.Z + Fixed.FromInt(32), type); + + if (missile.Info.SeeSound != 0) + { + world.StartSound(missile, missile.Info.SeeSound, SfxType.Misc); + } + + // Where it came from? + missile.Target = source; + + var angle = Geometry.PointToAngle( + source.X, source.Y, + dest.X, dest.Y); + + // Fuzzy player. + if ((dest.Flags & MobjFlags.Shadow) != 0) + { + var random = world.Random; + angle += new Angle((random.Next() - random.Next()) << 20); + } + + var speed = GetMissileSpeed(missile.Type); + + missile.Angle = angle; + missile.MomX = new Fixed(speed) * Trig.Cos(angle); + missile.MomY = new Fixed(speed) * Trig.Sin(angle); + + var dist = Geometry.AproxDistance( + dest.X - source.X, + dest.Y - source.Y); + + var num = (dest.Z - source.Z).Data; + var den = (dist / speed).Data; + if (den < 1) + { + den = 1; + } + + missile.MomZ = new Fixed(num / den); + + CheckMissileSpawn(missile); + + return missile; + } + + /// + /// Shoot a missile from the source. + /// For players. + /// + public void SpawnPlayerMissile(Mobj source, MobjType type) + { + var hs = world.Hitscan; + + // See which target is to be aimed at. + var angle = source.Angle; + var slope = hs.AimLineAttack(source, angle, Fixed.FromInt(16 * 64)); + + if (hs.LineTarget == null) + { + angle += new Angle(1 << 26); + slope = hs.AimLineAttack(source, angle, Fixed.FromInt(16 * 64)); + + if (hs.LineTarget == null) + { + angle -= new Angle(2 << 26); + slope = hs.AimLineAttack(source, angle, Fixed.FromInt(16 * 64)); + } + + if (hs.LineTarget == null) + { + angle = source.Angle; + slope = Fixed.Zero; + } + } + + var x = source.X; + var y = source.Y; + var z = source.Z + Fixed.FromInt(32); + + var missile = SpawnMobj(x, y, z, type); + + if (missile.Info.SeeSound != 0) + { + world.StartSound(missile, missile.Info.SeeSound, SfxType.Misc); + } + + missile.Target = source; + missile.Angle = angle; + missile.MomX = new Fixed(missile.Info.Speed) * Trig.Cos(angle); + missile.MomY = new Fixed(missile.Info.Speed) * Trig.Sin(angle); + missile.MomZ = new Fixed(missile.Info.Speed) * slope; + + CheckMissileSpawn(missile); + } + + + + //////////////////////////////////////////////////////////// + // Multi-player related functions + //////////////////////////////////////////////////////////// + + private static readonly int bodyQueSize = 32; + private int bodyQueSlot; + private Mobj[] bodyQue; + + private void InitMultiPlayerRespawn() + { + bodyQueSlot = 0; + bodyQue = new Mobj[bodyQueSize]; + } + + /// + /// Returns false if the player cannot be respawned at the given + /// mapthing spot because something is occupying it. + /// + public bool CheckSpot(int playernum, MapThing mthing) + { + var players = world.Options.Players; + + if (players[playernum].Mobj == null) + { + // First spawn of level, before corpses. + for (var i = 0; i < playernum; i++) + { + if (players[i].Mobj.X == mthing.X && players[i].Mobj.Y == mthing.Y) + { + return false; + } + } + return true; + } + + var x = mthing.X; + var y = mthing.Y; + + if (!world.ThingMovement.CheckPosition(players[playernum].Mobj, x, y)) + { + return false; + } + + // Flush an old corpse if needed. + if (bodyQueSlot >= bodyQueSize) + { + RemoveMobj(bodyQue[bodyQueSlot % bodyQueSize]); + } + bodyQue[bodyQueSlot % bodyQueSize] = players[playernum].Mobj; + bodyQueSlot++; + + // Spawn a teleport fog. + var subsector = Geometry.PointInSubsector(x, y, world.Map); + + var angle = (Angle.Ang45.Data >> Trig.AngleToFineShift) * + ((int)Math.Round(mthing.Angle.ToDegree()) / 45); + + // + // The code below to reproduce respawn fog bug in deathmath + // is based on Chocolate Doom's implementation. + // + + Fixed xa; + Fixed ya; + switch (angle) + { + case 4096: // -4096: + xa = Trig.Tan(2048); // finecosine[-4096] + ya = Trig.Tan(0); // finesine[-4096] + break; + case 5120: // -3072: + xa = Trig.Tan(3072); // finecosine[-3072] + ya = Trig.Tan(1024); // finesine[-3072] + break; + case 6144: // -2048: + xa = Trig.Sin(0); // finecosine[-2048] + ya = Trig.Tan(2048); // finesine[-2048] + break; + case 7168: // -1024: + xa = Trig.Sin(1024); // finecosine[-1024] + ya = Trig.Tan(3072); // finesine[-1024] + break; + case 0: + case 1024: + case 2048: + case 3072: + xa = Trig.Cos((int)angle); + ya = Trig.Sin((int)angle); + break; + default: + throw new Exception("Unexpected angle: " + angle); + } + + var mo = SpawnMobj( + x + 20 * xa, y + 20 * ya, + subsector.Sector.FloorHeight, + MobjType.Tfog); + + if (!world.FirstTicIsNotYetDone) + { + // Don't start sound on first frame. + world.StartSound(mo, Sfx.TELEPT, SfxType.Misc); + } + + return true; + } + + /// + /// Spawns a player at one of the random death match spots. + /// Called at level load and each death. + /// + public void DeathMatchSpawnPlayer(int playerNumber) + { + var selections = deathmatchStarts.Count; + if (selections < 4) + { + throw new Exception("Only " + selections + " deathmatch spots, 4 required"); + } + + var random = world.Random; + for (var j = 0; j < 20; j++) + { + var i = random.Next() % selections; + if (CheckSpot(playerNumber, deathmatchStarts[i])) + { + deathmatchStarts[i].Type = playerNumber + 1; + SpawnPlayer(deathmatchStarts[i]); + return; + } + } + + // No good spot, so the player will probably get stuck . + SpawnPlayer(playerStarts[playerNumber]); + } + + + + //////////////////////////////////////////////////////////// + // Item respawn + //////////////////////////////////////////////////////////// + + private static readonly int itemQueSize = 128; + private MapThing[] itemRespawnQue; + private int[] itemRespawnTime; + private int itemQueHead; + private int itemQueTail; + + private void InitRespawnSpecials() + { + itemRespawnQue = new MapThing[itemQueSize]; + itemRespawnTime = new int[itemQueSize]; + itemQueHead = 0; + itemQueTail = 0; + } + + /// + /// Respawn items if the game mode is altdeath. + /// + public void RespawnSpecials() + { + // Only respawn items in deathmatch. + if (world.Options.Deathmatch != 2) + { + return; + } + + // Nothing left to respawn? + if (itemQueHead == itemQueTail) + { + return; + } + + // Wait at least 30 seconds. + if (world.LevelTime - itemRespawnTime[itemQueTail] < 30 * 35) + { + return; + } + + var mthing = itemRespawnQue[itemQueTail]; + + var x = mthing.X; + var y = mthing.Y; + + // Spawn a teleport fog at the new spot. + var ss = Geometry.PointInSubsector(x, y, world.Map); + var mo = SpawnMobj(x, y, ss.Sector.FloorHeight, MobjType.Ifog); + world.StartSound(mo, Sfx.ITMBK, SfxType.Misc); + + int i; + // Find which type to spawn. + for (i = 0; i < DoomInfo.MobjInfos.Length; i++) + { + if (mthing.Type == DoomInfo.MobjInfos[i].DoomEdNum) + { + break; + } + } + + // Spawn it. + Fixed z; + if ((DoomInfo.MobjInfos[i].Flags & MobjFlags.SpawnCeiling) != 0) + { + z = Mobj.OnCeilingZ; + } + else + { + z = Mobj.OnFloorZ; + } + + mo = SpawnMobj(x, y, z, (MobjType)i); + mo.SpawnPoint = mthing; + mo.Angle = mthing.Angle; + + // Pull it from the que. + itemQueTail = (itemQueTail + 1) & (itemQueSize - 1); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThingInteraction.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThingInteraction.cs new file mode 100644 index 00000000..b3e50d49 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThingInteraction.cs @@ -0,0 +1,402 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class ThingInteraction + { + private World world; + + public ThingInteraction(World world) + { + this.world = world; + + InitRadiusAttack(); + } + + + /// + /// Called when the target is killed. + /// + public void KillMobj(Mobj source, Mobj target) + { + target.Flags &= ~(MobjFlags.Shootable | MobjFlags.Float | MobjFlags.SkullFly); + + if (target.Type != MobjType.Skull) + { + target.Flags &= ~MobjFlags.NoGravity; + } + + target.Flags |= MobjFlags.Corpse | MobjFlags.DropOff; + target.Height = new Fixed(target.Height.Data >> 2); + + if (source != null && source.Player != null) + { + // Count for intermission. + if ((target.Flags & MobjFlags.CountKill) != 0) + { + source.Player.KillCount++; + } + + if (target.Player != null) + { + source.Player.Frags[target.Player.Number]++; + } + } + else if (!world.Options.NetGame && (target.Flags & MobjFlags.CountKill) != 0) + { + // Count all monster deaths, even those caused by other monsters. + world.Options.Players[0].KillCount++; + } + + if (target.Player != null) + { + // Count environment kills against you. + if (source == null) + { + target.Player.Frags[target.Player.Number]++; + } + + target.Flags &= ~MobjFlags.Solid; + target.Player.PlayerState = PlayerState.Dead; + world.PlayerBehavior.DropWeapon(target.Player); + + var am = world.AutoMap; + + if (target.Player.Number == world.Options.ConsolePlayer && am.Visible) + { + // Don't die in auto map, switch view prior to dying. + am.Close(); + } + } + + if (target.Health < -target.Info.SpawnHealth && target.Info.XdeathState != 0) + { + target.SetState(target.Info.XdeathState); + } + else + { + target.SetState(target.Info.DeathState); + } + + target.Tics -= world.Random.Next() & 3; + if (target.Tics < 1) + { + target.Tics = 1; + } + + // Drop stuff. + // This determines the kind of object spawned during the death frame of a thing. + MobjType item; + switch (target.Type) + { + case MobjType.Wolfss: + case MobjType.Possessed: + item = MobjType.Clip; + break; + + case MobjType.Shotguy: + item = MobjType.Shotgun; + break; + + case MobjType.Chainguy: + item = MobjType.Chaingun; + break; + + default: + return; + } + + var mo = world.ThingAllocation.SpawnMobj(target.X, target.Y, Mobj.OnFloorZ, item); + + // Special versions of items. + mo.Flags |= MobjFlags.Dropped; + } + + + private static readonly int baseThreshold = 100; + + /// + /// Damages both enemies and players. + /// "inflictor" is the thing that caused the damage creature + /// or missile, can be null (slime, etc). + /// "source" is the thing to target after taking damage creature + /// or null. + /// Source and inflictor are the same for melee attacks. + /// Source can be null for slime, barrel explosions and other + /// environmental stuff. + /// + public void DamageMobj(Mobj target, Mobj inflictor, Mobj source, int damage) + { + if ((target.Flags & MobjFlags.Shootable) == 0) + { + // Shouldn't happen... + return; + } + + if (target.Health <= 0) + { + return; + } + + if ((target.Flags & MobjFlags.SkullFly) != 0) + { + target.MomX = target.MomY = target.MomZ = Fixed.Zero; + } + + var player = target.Player; + if (player != null && world.Options.Skill == GameSkill.Baby) + { + // Take half damage in trainer mode. + damage >>= 1; + } + + // Some close combat weapons should not inflict thrust and + // push the victim out of reach, thus kick away unless using the chainsaw. + var notChainsawAttack = + source == null || + source.Player == null || + source.Player.ReadyWeapon != WeaponType.Chainsaw; + + if (inflictor != null && (target.Flags & MobjFlags.NoClip) == 0 && notChainsawAttack) + { + var ang = Geometry.PointToAngle( + inflictor.X, + inflictor.Y, + target.X, + target.Y); + + var thrust = new Fixed(damage * (Fixed.FracUnit >> 3) * 100 / target.Info.Mass); + + // Make fall forwards sometimes. + if (damage < 40 && + damage > target.Health && + target.Z - inflictor.Z > Fixed.FromInt(64) && + (world.Random.Next() & 1) != 0) + { + ang += Angle.Ang180; + thrust *= 4; + } + + target.MomX += thrust * Trig.Cos(ang); + target.MomY += thrust * Trig.Sin(ang); + } + + // Player specific. + if (player != null) + { + // End of game hell hack. + if (target.Subsector.Sector.Special == (SectorSpecial)11 && damage >= target.Health) + { + damage = target.Health - 1; + } + + // Below certain threshold, ignore damage in GOD mode, or with INVUL power. + if (damage < 1000 && ((player.Cheats & CheatFlags.GodMode) != 0 || + player.Powers[(int)PowerType.Invulnerability] > 0)) + { + return; + } + + int saved; + + if (player.ArmorType != 0) + { + if (player.ArmorType == 1) + { + saved = damage / 3; + } + else + { + saved = damage / 2; + } + + if (player.ArmorPoints <= saved) + { + // Armor is used up. + saved = player.ArmorPoints; + player.ArmorType = 0; + } + + player.ArmorPoints -= saved; + damage -= saved; + } + + // Mirror mobj health here for Dave. + player.Health -= damage; + if (player.Health < 0) + { + player.Health = 0; + } + + player.Attacker = source; + + // Add damage after armor / invuln. + player.DamageCount += damage; + + if (player.DamageCount > 100) + { + // Teleport stomp does 10k points... + player.DamageCount = 100; + } + } + + // Do the damage. + target.Health -= damage; + if (target.Health <= 0) + { + KillMobj(source, target); + return; + } + + if ((world.Random.Next() < target.Info.PainChance) && + (target.Flags & MobjFlags.SkullFly) == 0) + { + // Fight back! + target.Flags |= MobjFlags.JustHit; + + target.SetState(target.Info.PainState); + } + + // We're awake now... + target.ReactionTime = 0; + + if ((target.Threshold == 0 || target.Type == MobjType.Vile) && + source != null && + source != target && + source.Type != MobjType.Vile) + { + // If not intent on another player, chase after this one. + target.Target = source; + target.Threshold = baseThreshold; + if (target.State == DoomInfo.States[(int)target.Info.SpawnState] && + target.Info.SeeState != MobjState.Null) + { + target.SetState(target.Info.SeeState); + } + } + } + + + /// + /// Called when the missile hits something (wall or thing). + /// + public void ExplodeMissile(Mobj thing) + { + thing.MomX = thing.MomY = thing.MomZ = Fixed.Zero; + + thing.SetState(DoomInfo.MobjInfos[(int)thing.Type].DeathState); + + thing.Tics -= world.Random.Next() & 3; + + if (thing.Tics < 1) + { + thing.Tics = 1; + } + + thing.Flags &= ~MobjFlags.Missile; + + if (thing.Info.DeathSound != 0) + { + world.StartSound(thing, thing.Info.DeathSound, SfxType.Misc); + } + } + + + private Mobj bombSource; + private Mobj bombSpot; + private int bombDamage; + + private Func radiusAttackFunc; + + private void InitRadiusAttack() + { + radiusAttackFunc = DoRadiusAttack; + } + + /// + /// "bombSource" is the creature that caused the explosion at "bombSpot". + /// + private bool DoRadiusAttack(Mobj thing) + { + if ((thing.Flags & MobjFlags.Shootable) == 0) + { + return true; + } + + // Boss spider and cyborg take no damage from concussion. + if (thing.Type == MobjType.Cyborg || thing.Type == MobjType.Spider) + { + return true; + } + + var dx = Fixed.Abs(thing.X - bombSpot.X); + var dy = Fixed.Abs(thing.Y - bombSpot.Y); + + var dist = dx > dy ? dx : dy; + dist = new Fixed((dist - thing.Radius).Data >> Fixed.FracBits); + + if (dist < Fixed.Zero) + { + dist = Fixed.Zero; + } + + if (dist.Data >= bombDamage) + { + // Out of range. + return true; + } + + if (world.VisibilityCheck.CheckSight(thing, bombSpot)) + { + // Must be in direct path. + DamageMobj(thing, bombSpot, bombSource, bombDamage - dist.Data); + } + + return true; + } + + /// + /// Source is the creature that caused the explosion at spot. + /// + public void RadiusAttack(Mobj spot, Mobj source, int damage) + { + var bm = world.Map.BlockMap; + + var dist = Fixed.FromInt(damage + GameConst.MaxThingRadius.Data); + + var blockY1 = bm.GetBlockY(spot.Y - dist); + var blockY2 = bm.GetBlockY(spot.Y + dist); + var blockX1 = bm.GetBlockX(spot.X - dist); + var blockX2 = bm.GetBlockX(spot.X + dist); + + bombSpot = spot; + bombSource = source; + bombDamage = damage; + + for (var by = blockY1; by <= blockY2; by++) + { + for (var bx = blockX1; bx <= blockX2; bx++) + { + bm.IterateThings(bx, by, radiusAttackFunc); + } + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThingMovement.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThingMovement.cs new file mode 100644 index 00000000..d8d0d356 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThingMovement.cs @@ -0,0 +1,1178 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class ThingMovement + { + private World world; + + public ThingMovement(World world) + { + this.world = world; + + InitThingMovement(); + InitSlideMovement(); + InitTeleportMovement(); + } + + + + //////////////////////////////////////////////////////////// + // General thing movement + //////////////////////////////////////////////////////////// + + public static readonly Fixed FloatSpeed = Fixed.FromInt(4); + + private static readonly int maxSpecialCrossCount = 20; + private static readonly Fixed maxMove = Fixed.FromInt(30); + private static readonly Fixed gravity = Fixed.One; + + private Mobj currentThing; + private MobjFlags currentFlags; + private Fixed currentX; + private Fixed currentY; + private Fixed[] currentBox; + + private Fixed currentFloorZ; + private Fixed currentCeilingZ; + private Fixed currentDropoffZ; + private bool floatOk; + + private LineDef currentCeilingLine; + + public int crossedSpecialCount; + public LineDef[] crossedSpecials; + + private Func checkLineFunc; + private Func checkThingFunc; + + private void InitThingMovement() + { + currentBox = new Fixed[4]; + crossedSpecials = new LineDef[maxSpecialCrossCount]; + checkLineFunc = CheckLine; + checkThingFunc = CheckThing; + } + + + /// + /// Links a thing into both a block and a subsector based on + /// its x and y. Sets thing.Subsector properly. + /// + public void SetThingPosition(Mobj thing) + { + var map = world.Map; + + var subsector = Geometry.PointInSubsector(thing.X, thing.Y, map); + + thing.Subsector = subsector; + + // Invisible things don't go into the sector links. + if ((thing.Flags & MobjFlags.NoSector) == 0) + { + var sector = subsector.Sector; + + thing.SectorPrev = null; + thing.SectorNext = sector.ThingList; + + if (sector.ThingList != null) + { + sector.ThingList.SectorPrev = thing; + } + + sector.ThingList = thing; + } + + // Inert things don't need to be in blockmap. + if ((thing.Flags & MobjFlags.NoBlockMap) == 0) + { + var index = map.BlockMap.GetIndex(thing.X, thing.Y); + + if (index != -1) + { + var link = map.BlockMap.ThingLists[index]; + + thing.BlockPrev = null; + thing.BlockNext = link; + + if (link != null) + { + link.BlockPrev = thing; + } + + map.BlockMap.ThingLists[index] = thing; + } + else + { + // Thing is off the map. + thing.BlockNext = null; + thing.BlockPrev = null; + } + } + } + + /// + /// Unlinks a thing from block map and sectors. + /// On each position change, BLOCKMAP and other lookups + /// maintaining lists ot things inside these structures + /// need to be updated. + /// + public void UnsetThingPosition(Mobj thing) + { + var map = world.Map; + + // Invisible things don't go into the sector links. + if ((thing.Flags & MobjFlags.NoSector) == 0) + { + // Unlink from subsector. + if (thing.SectorNext != null) + { + thing.SectorNext.SectorPrev = thing.SectorPrev; + } + + if (thing.SectorPrev != null) + { + thing.SectorPrev.SectorNext = thing.SectorNext; + } + else + { + thing.Subsector.Sector.ThingList = thing.SectorNext; + } + } + + // Inert things don't need to be in blockmap. + if ((thing.Flags & MobjFlags.NoBlockMap) == 0) + { + // Unlink from block map. + if (thing.BlockNext != null) + { + thing.BlockNext.BlockPrev = thing.BlockPrev; + } + + if (thing.BlockPrev != null) + { + thing.BlockPrev.BlockNext = thing.BlockNext; + } + else + { + var index = map.BlockMap.GetIndex(thing.X, thing.Y); + + if (index != -1) + { + map.BlockMap.ThingLists[index] = thing.BlockNext; + } + } + } + } + + + /// + /// Adjusts currentFloorZ and currentCeilingZ as lines are contacted. + /// + private bool CheckLine(LineDef line) + { + var mc = world.MapCollision; + + if (currentBox.Right() <= line.BoundingBox.Left() || + currentBox.Left() >= line.BoundingBox.Right() || + currentBox.Top() <= line.BoundingBox.Bottom() || + currentBox.Bottom() >= line.BoundingBox.Top()) + { + return true; + } + + if (Geometry.BoxOnLineSide(currentBox, line) != -1) + { + return true; + } + + // A line has been hit. + // + // The moving thing's destination position will cross the given line. + // If this should not be allowed, return false. + // If the line is special, keep track of it to process later if the move is proven ok. + // + // NOTE: + // specials are NOT sorted by order, so two special lines that are only 8 pixels + // apart could be crossed in either order. + + if (line.BackSector == null) + { + // One sided line. + return false; + } + + if ((currentThing.Flags & MobjFlags.Missile) == 0) + { + if ((line.Flags & LineFlags.Blocking) != 0) + { + // Explicitly blocking everything. + return false; + } + + if (currentThing.Player == null && (line.Flags & LineFlags.BlockMonsters) != 0) + { + // Block monsters only. + return false; + } + } + + // Set openrange, opentop, openbottom. + mc.LineOpening(line); + + // Adjust floor / ceiling heights. + if (mc.OpenTop < currentCeilingZ) + { + currentCeilingZ = mc.OpenTop; + currentCeilingLine = line; + } + + if (mc.OpenBottom > currentFloorZ) + { + currentFloorZ = mc.OpenBottom; + } + + if (mc.LowFloor < currentDropoffZ) + { + currentDropoffZ = mc.LowFloor; + } + + // If contacted a special line, add it to the list. + if (line.Special != 0) + { + crossedSpecials[crossedSpecialCount] = line; + crossedSpecialCount++; + } + + return true; + } + + private bool CheckThing(Mobj thing) + { + if ((thing.Flags & (MobjFlags.Solid | MobjFlags.Special | MobjFlags.Shootable)) == 0) + { + return true; + } + + var blockDist = thing.Radius + currentThing.Radius; + + if (Fixed.Abs(thing.X - currentX) >= blockDist || + Fixed.Abs(thing.Y - currentY) >= blockDist) + { + // Didn't hit it. + return true; + } + + // Don't clip against self. + if (thing == currentThing) + { + return true; + } + + // Check for skulls slamming into things. + if ((currentThing.Flags & MobjFlags.SkullFly) != 0) + { + var damage = ((world.Random.Next() % 8) + 1) * currentThing.Info.Damage; + + world.ThingInteraction.DamageMobj(thing, currentThing, currentThing, damage); + + currentThing.Flags &= ~MobjFlags.SkullFly; + currentThing.MomX = currentThing.MomY = currentThing.MomZ = Fixed.Zero; + + currentThing.SetState(currentThing.Info.SpawnState); + + // Stop moving. + return false; + } + + // Missiles can hit other things. + if ((currentThing.Flags & MobjFlags.Missile) != 0) + { + // See if it went over / under. + if (currentThing.Z > thing.Z + thing.Height) + { + // Overhead. + return true; + } + + if (currentThing.Z + currentThing.Height < thing.Z) + { + // Underneath. + return true; + } + + if (currentThing.Target != null && + (currentThing.Target.Type == thing.Type || + (currentThing.Target.Type == MobjType.Knight && thing.Type == MobjType.Bruiser) || + (currentThing.Target.Type == MobjType.Bruiser && thing.Type == MobjType.Knight))) + { + // Don't hit same species as originator. + if (thing == currentThing.Target) + { + return true; + } + + if (thing.Type != MobjType.Player && !DoomInfo.DeHackEdConst.MonstersInfight) + { + // Explode, but do no damage. + // Let players missile other players. + return false; + } + } + + if ((thing.Flags & MobjFlags.Shootable) == 0) + { + // Didn't do any damage. + return (thing.Flags & MobjFlags.Solid) == 0; + } + + // Damage / explode. + var damage = ((world.Random.Next() % 8) + 1) * currentThing.Info.Damage; + world.ThingInteraction.DamageMobj(thing, currentThing, currentThing.Target, damage); + + // Don't traverse any more. + return false; + } + + // Check for special pickup. + if ((thing.Flags & MobjFlags.Special) != 0) + { + var solid = (thing.Flags & MobjFlags.Solid) != 0; + if ((currentFlags & MobjFlags.PickUp) != 0) + { + // Can remove thing. + world.ItemPickup.TouchSpecialThing(thing, currentThing); + } + return !solid; + } + + return (thing.Flags & MobjFlags.Solid) == 0; + } + + /// + /// This is purely informative, nothing is modified + /// (except things picked up). + /// + /// In: + /// A Mobj (can be valid or invalid) + /// A position to be checked + /// (doesn't need to be related to the mobj.X and Y) + /// + /// During: + /// Special things are touched if MobjFlags.PickUp + /// Early out on solid lines? + /// + /// Out: + /// New subsector + /// CurrentFloorZ + /// CurrentCeilingZ + /// CurrentDropoffZ + /// The lowest point contacted + /// (monsters won't move to a dropoff) + /// crossedSpecials[] + /// crossedSpecialCount + /// + public bool CheckPosition(Mobj thing, Fixed x, Fixed y) + { + var map = world.Map; + var bm = map.BlockMap; + + currentThing = thing; + currentFlags = thing.Flags; + + currentX = x; + currentY = y; + + currentBox[Box.Top] = y + currentThing.Radius; + currentBox[Box.Bottom] = y - currentThing.Radius; + currentBox[Box.Right] = x + currentThing.Radius; + currentBox[Box.Left] = x - currentThing.Radius; + + var newSubsector = Geometry.PointInSubsector(x, y, map); + + currentCeilingLine = null; + + // The base floor / ceiling is from the subsector that contains the point. + // Any contacted lines the step closer together will adjust them. + currentFloorZ = currentDropoffZ = newSubsector.Sector.FloorHeight; + currentCeilingZ = newSubsector.Sector.CeilingHeight; + + var validCount = world.GetNewValidCount(); + + crossedSpecialCount = 0; + + if ((currentFlags & MobjFlags.NoClip) != 0) + { + return true; + } + + // Check things first, possibly picking things up. + // The bounding box is extended by MaxThingRadius because mobj_ts are grouped into + // mapblocks based on their origin point, and can overlap into adjacent blocks by up + // to MaxThingRadius units. + { + var blockX1 = bm.GetBlockX(currentBox[Box.Left] - GameConst.MaxThingRadius); + var blockX2 = bm.GetBlockX(currentBox[Box.Right] + GameConst.MaxThingRadius); + var blockY1 = bm.GetBlockY(currentBox[Box.Bottom] - GameConst.MaxThingRadius); + var blockY2 = bm.GetBlockY(currentBox[Box.Top] + GameConst.MaxThingRadius); + + for (var bx = blockX1; bx <= blockX2; bx++) + { + for (var by = blockY1; by <= blockY2; by++) + { + if (!map.BlockMap.IterateThings(bx, by, checkThingFunc)) + { + return false; + } + } + } + } + + // Check lines. + { + var blockX1 = bm.GetBlockX(currentBox[Box.Left]); + var blockX2 = bm.GetBlockX(currentBox[Box.Right]); + var blockY1 = bm.GetBlockY(currentBox[Box.Bottom]); + var blockY2 = bm.GetBlockY(currentBox[Box.Top]); + + for (var bx = blockX1; bx <= blockX2; bx++) + { + for (var by = blockY1; by <= blockY2; by++) + { + if (!map.BlockMap.IterateLines(bx, by, checkLineFunc, validCount)) + { + return false; + } + } + } + } + + return true; + } + + + /// + /// Attempt to move to a new position, crossing special lines unless + /// MobjFlags.Teleport is set. + /// + public bool TryMove(Mobj thing, Fixed x, Fixed y) + { + floatOk = false; + + if (!CheckPosition(thing, x, y)) + { + // Solid wall or thing. + return false; + } + + if ((thing.Flags & MobjFlags.NoClip) == 0) + { + if (currentCeilingZ - currentFloorZ < thing.Height) + { + // Doesn't fit. + return false; + } + + floatOk = true; + + if ((thing.Flags & MobjFlags.Teleport) == 0 && + currentCeilingZ - thing.Z < thing.Height) + { + // Mobj must lower itself to fit. + return false; + } + + if ((thing.Flags & MobjFlags.Teleport) == 0 && + currentFloorZ - thing.Z > Fixed.FromInt(24)) + { + // Too big a step up. + return false; + } + + if ((thing.Flags & (MobjFlags.DropOff | MobjFlags.Float)) == 0 && + currentFloorZ - currentDropoffZ > Fixed.FromInt(24)) + { + // Don't stand over a dropoff. + return false; + } + } + + // The move is ok, + // so link the thing into its new position. + UnsetThingPosition(thing); + + var oldx = thing.X; + var oldy = thing.Y; + thing.FloorZ = currentFloorZ; + thing.CeilingZ = currentCeilingZ; + thing.X = x; + thing.Y = y; + + SetThingPosition(thing); + + // If any special lines were hit, do the effect. + if ((thing.Flags & (MobjFlags.Teleport | MobjFlags.NoClip)) == 0) + { + while (crossedSpecialCount-- > 0) + { + // See if the line was crossed. + var line = crossedSpecials[crossedSpecialCount]; + var newSide = Geometry.PointOnLineSide(thing.X, thing.Y, line); + var oldSide = Geometry.PointOnLineSide(oldx, oldy, line); + if (newSide != oldSide) + { + if (line.Special != 0) + { + world.MapInteraction.CrossSpecialLine(line, oldSide, thing); + } + } + } + } + + return true; + } + + + private static readonly Fixed stopSpeed = new Fixed(0x1000); + private static readonly Fixed friction = new Fixed(0xe800); + + public void XYMovement(Mobj thing) + { + if (thing.MomX == Fixed.Zero && thing.MomY == Fixed.Zero) + { + if ((thing.Flags & MobjFlags.SkullFly) != 0) + { + // The skull slammed into something. + thing.Flags &= ~MobjFlags.SkullFly; + thing.MomX = thing.MomY = thing.MomZ = Fixed.Zero; + + thing.SetState(thing.Info.SpawnState); + } + + return; + } + + var player = thing.Player; + + if (thing.MomX > maxMove) + { + thing.MomX = maxMove; + } + else if (thing.MomX < -maxMove) + { + thing.MomX = -maxMove; + } + + if (thing.MomY > maxMove) + { + thing.MomY = maxMove; + } + else if (thing.MomY < -maxMove) + { + thing.MomY = -maxMove; + } + + var moveX = thing.MomX; + var moveY = thing.MomY; + + do + { + Fixed pMoveX; + Fixed pMoveY; + + if (moveX > maxMove / 2 || moveY > maxMove / 2) + { + pMoveX = thing.X + moveX / 2; + pMoveY = thing.Y + moveY / 2; + moveX >>= 1; + moveY >>= 1; + } + else + { + pMoveX = thing.X + moveX; + pMoveY = thing.Y + moveY; + moveX = moveY = Fixed.Zero; + } + + if (!TryMove(thing, pMoveX, pMoveY)) + { + // Blocked move. + if (thing.Player != null) + { // Try to slide along it. + SlideMove(thing); + } + else if ((thing.Flags & MobjFlags.Missile) != 0) + { + // Explode a missile. + if (currentCeilingLine != null && + currentCeilingLine.BackSector != null && + currentCeilingLine.BackSector.CeilingFlat == world.Map.SkyFlatNumber) + { + // Hack to prevent missiles exploding against the sky. + // Does not handle sky floors. + world.ThingAllocation.RemoveMobj(thing); + return; + } + world.ThingInteraction.ExplodeMissile(thing); + } + else + { + thing.MomX = thing.MomY = Fixed.Zero; + } + } + } + while (moveX != Fixed.Zero || moveY != Fixed.Zero); + + // Slow down. + if (player != null && (player.Cheats & CheatFlags.NoMomentum) != 0) + { + // Debug option for no sliding at all. + thing.MomX = thing.MomY = Fixed.Zero; + return; + } + + if ((thing.Flags & (MobjFlags.Missile | MobjFlags.SkullFly)) != 0) + { + // No friction for missiles ever. + return; + } + + if (thing.Z > thing.FloorZ) + { + // No friction when airborne. + return; + } + + if ((thing.Flags & MobjFlags.Corpse) != 0) + { + // Do not stop sliding if halfway off a step with some momentum. + if (thing.MomX > Fixed.One / 4 || + thing.MomX < -Fixed.One / 4 || + thing.MomY > Fixed.One / 4 || + thing.MomY < -Fixed.One / 4) + { + if (thing.FloorZ != thing.Subsector.Sector.FloorHeight) + { + return; + } + } + } + + if (thing.MomX > -stopSpeed && + thing.MomX < stopSpeed && + thing.MomY > -stopSpeed && + thing.MomY < stopSpeed && + (player == null || (player.Cmd.ForwardMove == 0 && player.Cmd.SideMove == 0))) + { + // If in a walking frame, stop moving. + if (player != null && (player.Mobj.State.Number - (int)MobjState.PlayRun1) < 4) + { + player.Mobj.SetState(MobjState.Play); + } + + thing.MomX = Fixed.Zero; + thing.MomY = Fixed.Zero; + } + else + { + thing.MomX = thing.MomX * friction; + thing.MomY = thing.MomY * friction; + } + } + + public void ZMovement(Mobj thing) + { + // Check for smooth step up. + if (thing.Player != null && thing.Z < thing.FloorZ) + { + thing.Player.ViewHeight -= thing.FloorZ - thing.Z; + + thing.Player.DeltaViewHeight = + (Player.NormalViewHeight - thing.Player.ViewHeight) >> 3; + } + + // Adjust height. + thing.Z += thing.MomZ; + + if ((thing.Flags & MobjFlags.Float) != 0 && thing.Target != null) + { + // Float down towards target if too close. + if ((thing.Flags & MobjFlags.SkullFly) == 0 && + (thing.Flags & MobjFlags.InFloat) == 0) + { + var dist = Geometry.AproxDistance( + thing.X - thing.Target.X, + thing.Y - thing.Target.Y); + + var delta = (thing.Target.Z + (thing.Height >> 1)) - thing.Z; + + if (delta < Fixed.Zero && dist < -(delta * 3)) + { + thing.Z -= FloatSpeed; + } + else if (delta > Fixed.Zero && dist < (delta * 3)) + { + thing.Z += FloatSpeed; + } + } + } + + // Clip movement. + if (thing.Z <= thing.FloorZ) + { + // Hit the floor. + + // + // The lost soul bounce fix below is based on Chocolate Doom's implementation. + // + + var correctLostSoulBounce = world.Options.GameVersion >= GameVersion.Ultimate; + + if (correctLostSoulBounce && (thing.Flags & MobjFlags.SkullFly) != 0) + { + // The skull slammed into something. + thing.MomZ = -thing.MomZ; + } + + if (thing.MomZ < Fixed.Zero) + { + if (thing.Player != null && thing.MomZ < -gravity * 8) + { + // Squat down. + // Decrease viewheight for a moment after hitting the ground (hard), + // and utter appropriate sound. + thing.Player.DeltaViewHeight = (thing.MomZ >> 3); + world.StartSound(thing, Sfx.OOF, SfxType.Voice); + } + thing.MomZ = Fixed.Zero; + } + thing.Z = thing.FloorZ; + + if (!correctLostSoulBounce && + (thing.Flags & MobjFlags.SkullFly) != 0) + { + thing.MomZ = -thing.MomZ; + } + + if ((thing.Flags & MobjFlags.Missile) != 0 && + (thing.Flags & MobjFlags.NoClip) == 0) + { + world.ThingInteraction.ExplodeMissile(thing); + return; + } + } + else if ((thing.Flags & MobjFlags.NoGravity) == 0) + { + if (thing.MomZ == Fixed.Zero) + { + thing.MomZ = -gravity * 2; + } + else + { + thing.MomZ -= gravity; + } + } + + if (thing.Z + thing.Height > thing.CeilingZ) + { + // Hit the ceiling. + if (thing.MomZ > Fixed.Zero) + { + thing.MomZ = Fixed.Zero; + } + + { + thing.Z = thing.CeilingZ - thing.Height; + } + + if ((thing.Flags & MobjFlags.SkullFly) != 0) + { + // The skull slammed into something. + thing.MomZ = -thing.MomZ; + } + + if ((thing.Flags & MobjFlags.Missile) != 0 && + (thing.Flags & MobjFlags.NoClip) == 0) + { + world.ThingInteraction.ExplodeMissile(thing); + return; + } + } + } + + + public Fixed CurrentFloorZ => currentFloorZ; + public Fixed CurrentCeilingZ => currentCeilingZ; + public Fixed CurrentDropoffZ => currentDropoffZ; + public bool FloatOk => floatOk; + + + + //////////////////////////////////////////////////////////// + // Player's slide movement + //////////////////////////////////////////////////////////// + + private Fixed bestSlideFrac; + private Fixed secondSlideFrac; + + private LineDef bestSlideLine; + private LineDef secondSlideLine; + + private Mobj slideThing; + private Fixed slideMoveX; + private Fixed slideMoveY; + + private Func slideTraverseFunc; + + private void InitSlideMovement() + { + slideTraverseFunc = SlideTraverse; + } + + /// + /// Adjusts the x and y movement so that the next move will + /// slide along the wall. + /// + private void HitSlideLine(LineDef line) + { + if (line.SlopeType == SlopeType.Horizontal) + { + slideMoveY = Fixed.Zero; + return; + } + + if (line.SlopeType == SlopeType.Vertical) + { + slideMoveX = Fixed.Zero; + return; + } + + var side = Geometry.PointOnLineSide(slideThing.X, slideThing.Y, line); + + var lineAngle = Geometry.PointToAngle(Fixed.Zero, Fixed.Zero, line.Dx, line.Dy); + if (side == 1) + { + lineAngle += Angle.Ang180; + } + + var moveAngle = Geometry.PointToAngle(Fixed.Zero, Fixed.Zero, slideMoveX, slideMoveY); + + var deltaAngle = moveAngle - lineAngle; + if (deltaAngle > Angle.Ang180) + { + deltaAngle += Angle.Ang180; + } + + var moveDist = Geometry.AproxDistance(slideMoveX, slideMoveY); + var newDist = moveDist * Trig.Cos(deltaAngle); + + slideMoveX = newDist * Trig.Cos(lineAngle); + slideMoveY = newDist * Trig.Sin(lineAngle); + } + + private bool SlideTraverse(Intercept intercept) + { + var mc = world.MapCollision; + + if (intercept.Line == null) + { + throw new Exception("ThingMovement.SlideTraverse: Not a line?"); + } + + var line = intercept.Line; + + if ((line.Flags & LineFlags.TwoSided) == 0) + { + if (Geometry.PointOnLineSide(slideThing.X, slideThing.Y, line) != 0) + { + // Don't hit the back side. + return true; + } + + goto isBlocking; + } + + // Set openrange, opentop, openbottom. + mc.LineOpening(line); + + if (mc.OpenRange < slideThing.Height) + { + // Doesn't fit. + goto isBlocking; + } + + if (mc.OpenTop - slideThing.Z < slideThing.Height) + { + // Mobj is too high. + goto isBlocking; + } + + if (mc.OpenBottom - slideThing.Z > Fixed.FromInt(24)) + { + // Too big a step up. + goto isBlocking; + } + + // This line doesn't block movement. + return true; + + // The line does block movement, see if it is closer than best so far. + isBlocking: + if (intercept.Frac < bestSlideFrac) + { + secondSlideFrac = bestSlideFrac; + secondSlideLine = bestSlideLine; + bestSlideFrac = intercept.Frac; + bestSlideLine = line; + } + + // Stop. + return false; + } + + /// + /// The MomX / MomY move is bad, so try to slide along a wall. + /// Find the first line hit, move flush to it, and slide along it. + /// This is a kludgy mess. + /// + private void SlideMove(Mobj thing) + { + var pt = world.PathTraversal; + + slideThing = thing; + + var hitCount = 0; + + retry: + // Don't loop forever. + if (++hitCount == 3) + { + // The move most have hit the middle, so stairstep. + StairStep(thing); + return; + } + + Fixed leadX; + Fixed leadY; + Fixed trailX; + Fixed trailY; + + // Trace along the three leading corners. + if (thing.MomX > Fixed.Zero) + { + leadX = thing.X + thing.Radius; + trailX = thing.X - thing.Radius; + } + else + { + leadX = thing.X - thing.Radius; + trailX = thing.X + thing.Radius; + } + + if (thing.MomY > Fixed.Zero) + { + leadY = thing.Y + thing.Radius; + trailY = thing.Y - thing.Radius; + } + else + { + leadY = thing.Y - thing.Radius; + trailY = thing.Y + thing.Radius; + } + + bestSlideFrac = Fixed.OnePlusEpsilon; + + pt.PathTraverse( + leadX, leadY, leadX + thing.MomX, leadY + thing.MomY, + PathTraverseFlags.AddLines, slideTraverseFunc); + + pt.PathTraverse( + trailX, leadY, trailX + thing.MomX, leadY + thing.MomY, + PathTraverseFlags.AddLines, slideTraverseFunc); + + pt.PathTraverse( + leadX, trailY, leadX + thing.MomX, trailY + thing.MomY, + PathTraverseFlags.AddLines, slideTraverseFunc); + + // Move up to the wall. + if (bestSlideFrac == Fixed.OnePlusEpsilon) + { + // The move most have hit the middle, so stairstep. + StairStep(thing); + return; + } + + // Fudge a bit to make sure it doesn't hit. + bestSlideFrac = new Fixed(bestSlideFrac.Data - 0x800); + if (bestSlideFrac > Fixed.Zero) + { + var newX = thing.MomX * bestSlideFrac; + var newY = thing.MomY * bestSlideFrac; + + if (!TryMove(thing, thing.X + newX, thing.Y + newY)) + { + // The move most have hit the middle, so stairstep. + StairStep(thing); + return; + } + } + + // Now continue along the wall. + // First calculate remainder. + bestSlideFrac = new Fixed(Fixed.FracUnit - (bestSlideFrac.Data + 0x800)); + + if (bestSlideFrac > Fixed.One) + { + bestSlideFrac = Fixed.One; + } + + if (bestSlideFrac <= Fixed.Zero) + { + return; + } + + slideMoveX = thing.MomX * bestSlideFrac; + slideMoveY = thing.MomY * bestSlideFrac; + + // Clip the moves. + HitSlideLine(bestSlideLine); + + thing.MomX = slideMoveX; + thing.MomY = slideMoveY; + + if (!TryMove(thing, thing.X + slideMoveX, thing.Y + slideMoveY)) + { + goto retry; + } + } + + private void StairStep(Mobj thing) + { + if (!TryMove(thing, thing.X, thing.Y + thing.MomY)) + { + TryMove(thing, thing.X + thing.MomX, thing.Y); + } + } + + + + //////////////////////////////////////////////////////////// + // Teleport movement + //////////////////////////////////////////////////////////// + + private Func stompThingFunc; + + private void InitTeleportMovement() + { + stompThingFunc = StompThing; + } + + private bool StompThing(Mobj thing) + { + if ((thing.Flags & MobjFlags.Shootable) == 0) + { + return true; + } + + var blockDist = thing.Radius + currentThing.Radius; + var dx = Fixed.Abs(thing.X - currentX); + var dy = Fixed.Abs(thing.Y - currentY); + if (dx >= blockDist || dy >= blockDist) + { + // Didn't hit it. + return true; + } + + // Don't clip against self. + if (thing == currentThing) + { + return true; + } + + // Monsters don't stomp things except on boss level. + if (currentThing.Player == null && world.Options.Map != 30) + { + return false; + } + + world.ThingInteraction.DamageMobj(thing, currentThing, currentThing, 10000); + + return true; + } + + public bool TeleportMove(Mobj thing, Fixed x, Fixed y) + { + // Kill anything occupying the position. + currentThing = thing; + currentFlags = thing.Flags; + + currentX = x; + currentY = y; + + currentBox[Box.Top] = y + currentThing.Radius; + currentBox[Box.Bottom] = y - currentThing.Radius; + currentBox[Box.Right] = x + currentThing.Radius; + currentBox[Box.Left] = x - currentThing.Radius; + + var ss = Geometry.PointInSubsector(x, y, world.Map); + + currentCeilingLine = null; + + // The base floor / ceiling is from the subsector that contains the point. + // Any contacted lines the step closer together will adjust them. + currentFloorZ = currentDropoffZ = ss.Sector.FloorHeight; + currentCeilingZ = ss.Sector.CeilingHeight; + + var validcount = world.GetNewValidCount(); + + crossedSpecialCount = 0; + + // Stomp on any things contacted. + var bm = world.Map.BlockMap; + var blockX1 = bm.GetBlockX(currentBox[Box.Left] - GameConst.MaxThingRadius); + var blockX2 = bm.GetBlockX(currentBox[Box.Right] + GameConst.MaxThingRadius); + var blockY1 = bm.GetBlockY(currentBox[Box.Bottom] - GameConst.MaxThingRadius); + var blockY2 = bm.GetBlockY(currentBox[Box.Top] + GameConst.MaxThingRadius); + + for (var bx = blockX1; bx <= blockX2; bx++) + { + for (var by = blockY1; by <= blockY2; by++) + { + if (!bm.IterateThings(bx, by, stompThingFunc)) + { + return false; + } + } + } + + // the move is ok, so link the thing into its new position + UnsetThingPosition(thing); + + thing.FloorZ = currentFloorZ; + thing.CeilingZ = currentCeilingZ; + thing.X = x; + thing.Y = y; + + SetThingPosition(thing); + + return true; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Thinker.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Thinker.cs new file mode 100644 index 00000000..0e22274f --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Thinker.cs @@ -0,0 +1,54 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public class Thinker + { + private Thinker prev; + private Thinker next; + private ThinkerState thinkerState; + + public Thinker() + { + } + + public virtual void Run() + { + } + + public Thinker Prev + { + get => prev; + set => prev = value; + } + + public Thinker Next + { + get => next; + set => next = value; + } + + public ThinkerState ThinkerState + { + get => thinkerState; + set => thinkerState = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThinkerState.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThinkerState.cs new file mode 100644 index 00000000..08bd71f5 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/ThinkerState.cs @@ -0,0 +1,28 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum ThinkerState + { + Active, + InStasis, + Removed + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Thinkers.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Thinkers.cs new file mode 100644 index 00000000..b407b504 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/Thinkers.cs @@ -0,0 +1,132 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ManagedDoom +{ + public sealed class Thinkers + { + private World world; + + public Thinkers(World world) + { + this.world = world; + + InitThinkers(); + } + + + private Thinker cap; + + private void InitThinkers() + { + cap = new Thinker(); + cap.Prev = cap.Next = cap; + } + + public void Add(Thinker thinker) + { + cap.Prev.Next = thinker; + thinker.Next = cap; + thinker.Prev = cap.Prev; + cap.Prev = thinker; + } + + public void Remove(Thinker thinker) + { + thinker.ThinkerState = ThinkerState.Removed; + } + + public void Run() + { + var current = cap.Next; + while (current != cap) + { + if (current.ThinkerState == ThinkerState.Removed) + { + // Time to remove it. + current.Next.Prev = current.Prev; + current.Prev.Next = current.Next; + } + else + { + if (current.ThinkerState == ThinkerState.Active) + { + current.Run(); + } + } + current = current.Next; + } + } + + public void Reset() + { + cap.Prev = cap.Next = cap; + } + + public ThinkerEnumerator GetEnumerator() + { + return new ThinkerEnumerator(this); + } + + + + public struct ThinkerEnumerator : IEnumerator + { + private Thinkers thinkers; + private Thinker current; + + public ThinkerEnumerator(Thinkers thinkers) + { + this.thinkers = thinkers; + current = thinkers.cap; + } + + public bool MoveNext() + { + while (true) + { + current = current.Next; + if (current == thinkers.cap) + { + return false; + } + else if (current.ThinkerState != ThinkerState.Removed) + { + return true; + } + } + } + + public void Reset() + { + current = thinkers.cap; + } + + public void Dispose() + { + } + + public Thinker Current => current; + + object IEnumerator.Current => throw new NotImplementedException(); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/VerticalDoor.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/VerticalDoor.cs new file mode 100644 index 00000000..6516f527 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/VerticalDoor.cs @@ -0,0 +1,229 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public class VerticalDoor : Thinker + { + private World world; + + private VerticalDoorType type; + private Sector sector; + private Fixed topHeight; + private Fixed speed; + + // 1 = up, 0 = waiting at top, -1 = down. + private int direction; + + // Tics to wait at the top. + private int topWait; + + // When it reaches 0, start going down + // (keep in case a door going down is reset). + private int topCountDown; + + public VerticalDoor(World world) + { + this.world = world; + } + + public override void Run() + { + var sa = world.SectorAction; + + SectorActionResult result; + + switch (direction) + { + case 0: + // Waiting. + if (--topCountDown == 0) + { + switch (type) + { + case VerticalDoorType.BlazeRaise: + // Time to go back down. + direction = -1; + world.StartSound(sector.SoundOrigin, Sfx.BDCLS, SfxType.Misc); + break; + + case VerticalDoorType.Normal: + // Time to go back down. + direction = -1; + world.StartSound(sector.SoundOrigin, Sfx.DORCLS, SfxType.Misc); + break; + + case VerticalDoorType.Close30ThenOpen: + direction = 1; + world.StartSound(sector.SoundOrigin, Sfx.DOROPN, SfxType.Misc); + break; + + default: + break; + } + } + break; + + case 2: + // Initial wait. + if (--topCountDown == 0) + { + switch (type) + { + case VerticalDoorType.RaiseIn5Mins: + direction = 1; + type = VerticalDoorType.Normal; + world.StartSound(sector.SoundOrigin, Sfx.DOROPN, SfxType.Misc); + break; + + default: + break; + } + } + break; + + case -1: + // Down. + result = sa.MovePlane( + sector, + speed, + sector.FloorHeight, + false, 1, direction); + if (result == SectorActionResult.PastDestination) + { + switch (type) + { + case VerticalDoorType.BlazeRaise: + case VerticalDoorType.BlazeClose: + sector.SpecialData = null; + // Unlink and free. + world.Thinkers.Remove(this); + world.StartSound(sector.SoundOrigin, Sfx.BDCLS, SfxType.Misc); + break; + + case VerticalDoorType.Normal: + case VerticalDoorType.Close: + sector.SpecialData = null; + // Unlink and free. + world.Thinkers.Remove(this); + break; + + case VerticalDoorType.Close30ThenOpen: + direction = 0; + topCountDown = 35 * 30; + break; + + default: + break; + } + } + else if (result == SectorActionResult.Crushed) + { + switch (type) + { + case VerticalDoorType.BlazeClose: + case VerticalDoorType.Close: // Do not go back up! + break; + + default: + direction = 1; + world.StartSound(sector.SoundOrigin, Sfx.DOROPN, SfxType.Misc); + break; + } + } + break; + + case 1: + // Up. + result = sa.MovePlane( + sector, + speed, + topHeight, + false, 1, direction); + + if (result == SectorActionResult.PastDestination) + { + switch (type) + { + case VerticalDoorType.BlazeRaise: + case VerticalDoorType.Normal: + // Wait at top. + direction = 0; + topCountDown = topWait; + break; + + case VerticalDoorType.Close30ThenOpen: + case VerticalDoorType.BlazeOpen: + case VerticalDoorType.Open: + sector.SpecialData = null; + // Unlink and free. + world.Thinkers.Remove(this); + break; + + default: + break; + } + } + break; + } + } + + public VerticalDoorType Type + { + get => type; + set => type = value; + } + + public Sector Sector + { + get => sector; + set => sector = value; + } + + public Fixed TopHeight + { + get => topHeight; + set => topHeight = value; + } + + public Fixed Speed + { + get => speed; + set => speed = value; + } + + public int Direction + { + get => direction; + set => direction = value; + } + + public int TopWait + { + get => topWait; + set => topWait = value; + } + + public int TopCountDown + { + get => topCountDown; + set => topCountDown = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/VerticalDoorType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/VerticalDoorType.cs new file mode 100644 index 00000000..1ea31c89 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/VerticalDoorType.cs @@ -0,0 +1,33 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum VerticalDoorType + { + Normal, + Close30ThenOpen, + Close, + Open, + RaiseIn5Mins, + BlazeRaise, + BlazeOpen, + BlazeClose + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/VisibilityCheck.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/VisibilityCheck.cs new file mode 100644 index 00000000..cdc3556e --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/VisibilityCheck.cs @@ -0,0 +1,274 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class VisibilityCheck + { + private World world; + + // Eye z of looker. + private Fixed sightZStart; + private Fixed bottomSlope; + private Fixed topSlope; + + // From looker to target. + private DivLine trace; + private Fixed targetX; + private Fixed targetY; + + private DivLine occluder; + + public VisibilityCheck(World world) + { + this.world = world; + + trace = new DivLine(); + + occluder = new DivLine(); + } + + /// + /// Returns the fractional intercept point along the first divline. + /// This is only called by the addthings and addlines traversers. + /// + private Fixed InterceptVector(DivLine v2, DivLine v1) + { + var den = (v1.Dy >> 8) * v2.Dx - (v1.Dx >> 8) * v2.Dy; + + if (den == Fixed.Zero) + { + return Fixed.Zero; + } + + var num = ((v1.X - v2.X) >> 8) * v1.Dy + ((v2.Y - v1.Y) >> 8) * v1.Dx; + + var frac = num / den; + + return frac; + } + + /// + /// Returns true if strace crosses the given subsector successfully. + /// + private bool CrossSubsector(int subsectorNumber, int validCount) + { + var map = world.Map; + var subsector = map.Subsectors[subsectorNumber]; + var count = subsector.SegCount; + + // Check lines. + for (var i = 0; i < count; i++) + { + var seg = map.Segs[subsector.FirstSeg + i]; + var line = seg.LineDef; + + // Allready checked other side? + if (line.ValidCount == validCount) + { + continue; + } + + line.ValidCount = validCount; + + var v1 = line.Vertex1; + var v2 = line.Vertex2; + var s1 = Geometry.DivLineSide(v1.X, v1.Y, trace); + var s2 = Geometry.DivLineSide(v2.X, v2.Y, trace); + + // Line isn't crossed? + if (s1 == s2) + { + continue; + } + + occluder.MakeFrom(line); + s1 = Geometry.DivLineSide(trace.X, trace.Y, occluder); + s2 = Geometry.DivLineSide(targetX, targetY, occluder); + + // Line isn't crossed? + if (s1 == s2) + { + continue; + } + + // The check below is imported from Chocolate Doom to + // avoid crash due to two-sided lines with no backsector. + if (line.BackSector == null) + { + return false; + } + + // Stop because it is not two sided anyway. + // Might do this after updating validcount? + if ((line.Flags & LineFlags.TwoSided) == 0) + { + return false; + } + + // Crosses a two sided line. + var front = seg.FrontSector; + var back = seg.BackSector; + + // No wall to block sight with? + if (front.FloorHeight == back.FloorHeight && + front.CeilingHeight == back.CeilingHeight) + { + continue; + } + + // Possible occluder because of ceiling height differences. + Fixed openTop; + if (front.CeilingHeight < back.CeilingHeight) + { + openTop = front.CeilingHeight; + } + else + { + openTop = back.CeilingHeight; + } + + // Because of ceiling height differences. + Fixed openBottom; + if (front.FloorHeight > back.FloorHeight) + { + openBottom = front.FloorHeight; + } + else + { + openBottom = back.FloorHeight; + } + + // Quick test for totally closed doors. + if (openBottom >= openTop) + { + // Stop. + return false; + } + + var frac = InterceptVector(trace, occluder); + + if (front.FloorHeight != back.FloorHeight) + { + var slope = (openBottom - sightZStart) / frac; + if (slope > bottomSlope) + { + bottomSlope = slope; + } + } + + if (front.CeilingHeight != back.CeilingHeight) + { + var slope = (openTop - sightZStart) / frac; + if (slope < topSlope) + { + topSlope = slope; + } + } + + if (topSlope <= bottomSlope) + { + // Stop. + return false; + } + } + + // Passed the subsector ok. + return true; + } + + /// + /// Returns true if strace crosses the given node successfully. + /// + private bool CrossBspNode(int nodeNumber, int validCount) + { + if (Node.IsSubsector(nodeNumber)) + { + if (nodeNumber == -1) + { + return CrossSubsector(0, validCount); + } + else + { + return CrossSubsector(Node.GetSubsector(nodeNumber), validCount); + } + } + + var node = world.Map.Nodes[nodeNumber]; + + // Decide which side the start point is on. + var side = Geometry.DivLineSide(trace.X, trace.Y, node); + if (side == 2) + { + // An "on" should cross both sides. + side = 0; + } + + // cross the starting side + if (!CrossBspNode(node.Children[side], validCount)) + { + return false; + } + + // The partition plane is crossed here. + if (side == Geometry.DivLineSide(targetX, targetY, node)) + { + // The line doesn't touch the other side. + return true; + } + + // Cross the ending side. + return CrossBspNode(node.Children[side ^ 1], validCount); + } + + /// + /// Returns true if a straight line between the looker and target is unobstructed. + /// + public bool CheckSight(Mobj looker, Mobj target) + { + var map = world.Map; + + // First check for trivial rejection. + // Check in REJECT table. + if (map.Reject.Check(looker.Subsector.Sector, target.Subsector.Sector)) + { + // Can't possibly be connected. + return false; + } + + // An unobstructed LOS is possible. + // Now look from eyes of t1 to any part of t2. + + sightZStart = looker.Z + looker.Height - (looker.Height >> 2); + topSlope = (target.Z + target.Height) - sightZStart; + bottomSlope = (target.Z) - sightZStart; + + trace.X = looker.X; + trace.Y = looker.Y; + trace.Dx = target.X - looker.X; + trace.Dy = target.Y - looker.Y; + + targetX = target.X; + targetY = target.Y; + + // The head node is the last node output. + return CrossBspNode(map.Nodes.Length - 1, world.GetNewValidCount()); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/WeaponBehavior.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/WeaponBehavior.cs new file mode 100644 index 00000000..eb53d743 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/WeaponBehavior.cs @@ -0,0 +1,697 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class WeaponBehavior + { + public static readonly Fixed MeleeRange = Fixed.FromInt(64); + public static readonly Fixed MissileRange = Fixed.FromInt(32 * 64); + + public static readonly Fixed WeaponTop = Fixed.FromInt(32); + public static readonly Fixed WeaponBottom = Fixed.FromInt(128); + + private static readonly Fixed RaiseSpeed = Fixed.FromInt(6); + private static readonly Fixed LowerSpeed = Fixed.FromInt(6); + + private World world; + + private Fixed currentBulletSlope; + + + public WeaponBehavior(World world) + { + this.world = world; + } + + + public void Light0(Player player) + { + player.ExtraLight = 0; + } + + + public void WeaponReady(Player player, PlayerSpriteDef psp) + { + var pb = world.PlayerBehavior; + + // Get out of attack state. + if (player.Mobj.State == DoomInfo.States[(int)MobjState.PlayAtk1] || + player.Mobj.State == DoomInfo.States[(int)MobjState.PlayAtk2]) + { + player.Mobj.SetState(MobjState.Play); + } + + if (player.ReadyWeapon == WeaponType.Chainsaw && + psp.State == DoomInfo.States[(int)MobjState.Saw]) + { + world.StartSound(player.Mobj, Sfx.SAWIDL, SfxType.Weapon); + } + + // Check for weapon change. + // If player is dead, put the weapon away. + if (player.PendingWeapon != WeaponType.NoChange || player.Health == 0) + { + // Change weapon. + // Pending weapon should allready be validated. + var newState = DoomInfo.WeaponInfos[(int)player.ReadyWeapon].DownState; + pb.SetPlayerSprite(player, PlayerSprite.Weapon, newState); + return; + } + + // Check for fire. + // The missile launcher and bfg do not auto fire. + if ((player.Cmd.Buttons & TicCmdButtons.Attack) != 0) + { + if (!player.AttackDown || + (player.ReadyWeapon != WeaponType.Missile && player.ReadyWeapon != WeaponType.Bfg)) + { + player.AttackDown = true; + FireWeapon(player); + return; + } + } + else + { + player.AttackDown = false; + } + + // Bob the weapon based on movement speed. + var angle = (128 * player.Mobj.World.LevelTime) & Trig.FineMask; + psp.Sx = Fixed.One + player.Bob * Trig.Cos(angle); + + angle &= Trig.FineAngleCount / 2 - 1; + psp.Sy = WeaponTop + player.Bob * Trig.Sin(angle); + } + + + private bool CheckAmmo(Player player) + { + var ammo = DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo; + + // Minimal amount for one shot varies. + int count; + if (player.ReadyWeapon == WeaponType.Bfg) + { + count = DoomInfo.DeHackEdConst.BfgCellsPerShot; + } + else if (player.ReadyWeapon == WeaponType.SuperShotgun) + { + // Double barrel. + count = 2; + } + else + { + // Regular. + count = 1; + } + + // Some do not need ammunition anyway. + // Return if current ammunition sufficient. + if (ammo == AmmoType.NoAmmo || player.Ammo[(int)ammo] >= count) + { + return true; + } + + // Out of ammo, pick a weapon to change to. + // Preferences are set here. + do + { + if (player.WeaponOwned[(int)WeaponType.Plasma] && + player.Ammo[(int)AmmoType.Cell] > 0 && + world.Options.GameMode != GameMode.Shareware) + { + player.PendingWeapon = WeaponType.Plasma; + } + else if (player.WeaponOwned[(int)WeaponType.SuperShotgun] && + player.Ammo[(int)AmmoType.Shell] > 2 && + world.Options.GameMode == GameMode.Commercial) + { + player.PendingWeapon = WeaponType.SuperShotgun; + } + else if (player.WeaponOwned[(int)WeaponType.Chaingun] && + player.Ammo[(int)AmmoType.Clip] > 0) + { + player.PendingWeapon = WeaponType.Chaingun; + } + else if (player.WeaponOwned[(int)WeaponType.Shotgun] && + player.Ammo[(int)AmmoType.Shell] > 0) + { + player.PendingWeapon = WeaponType.Shotgun; + } + else if (player.Ammo[(int)AmmoType.Clip] > 0) + { + player.PendingWeapon = WeaponType.Pistol; + } + else if (player.WeaponOwned[(int)WeaponType.Chainsaw]) + { + player.PendingWeapon = WeaponType.Chainsaw; + } + else if (player.WeaponOwned[(int)WeaponType.Missile] && + player.Ammo[(int)AmmoType.Missile] > 0) + { + player.PendingWeapon = WeaponType.Missile; + } + else if (player.WeaponOwned[(int)WeaponType.Bfg] && + player.Ammo[(int)AmmoType.Cell] > DoomInfo.DeHackEdConst.BfgCellsPerShot && + world.Options.GameMode != GameMode.Shareware) + { + player.PendingWeapon = WeaponType.Bfg; + } + else + { + // If everything fails. + player.PendingWeapon = WeaponType.Fist; + } + + } while (player.PendingWeapon == WeaponType.NoChange); + + // Now set appropriate weapon overlay. + world.PlayerBehavior.SetPlayerSprite( + player, + PlayerSprite.Weapon, + DoomInfo.WeaponInfos[(int)player.ReadyWeapon].DownState); + + return false; + } + + + private void RecursiveSound(Sector sec, int soundblocks, Mobj soundtarget, int validCount) + { + // Wake up all monsters in this sector. + if (sec.ValidCount == validCount && sec.SoundTraversed <= soundblocks + 1) + { + // Already flooded. + return; + } + + sec.ValidCount = validCount; + sec.SoundTraversed = soundblocks + 1; + sec.SoundTarget = soundtarget; + + var mc = world.MapCollision; + + for (var i = 0; i < sec.Lines.Length; i++) + { + var check = sec.Lines[i]; + if ((check.Flags & LineFlags.TwoSided) == 0) + { + continue; + } + + mc.LineOpening(check); + + if (mc.OpenRange <= Fixed.Zero) + { + // Closed door. + continue; + } + + Sector other; + if (check.FrontSide.Sector == sec) + { + other = check.BackSide.Sector; + } + else + { + other = check.FrontSide.Sector; + } + + if ((check.Flags & LineFlags.SoundBlock) != 0) + { + if (soundblocks == 0) + { + RecursiveSound(other, 1, soundtarget, validCount); + } + } + else + { + RecursiveSound(other, soundblocks, soundtarget, validCount); + } + } + } + + + private void NoiseAlert(Mobj target, Mobj emmiter) + { + RecursiveSound( + emmiter.Subsector.Sector, + 0, + target, + world.GetNewValidCount()); + } + + + private void FireWeapon(Player player) + { + if (!CheckAmmo(player)) + { + return; + } + + player.Mobj.SetState(MobjState.PlayAtk1); + + var newState = DoomInfo.WeaponInfos[(int)player.ReadyWeapon].AttackState; + world.PlayerBehavior.SetPlayerSprite(player, PlayerSprite.Weapon, newState); + + NoiseAlert(player.Mobj, player.Mobj); + } + + + public void Lower(Player player, PlayerSpriteDef psp) + { + psp.Sy += LowerSpeed; + + // Is already down. + if (psp.Sy < WeaponBottom) + { + return; + } + + // Player is dead. + if (player.PlayerState == PlayerState.Dead) + { + psp.Sy = WeaponBottom; + + // don't bring weapon back up + return; + } + + var pb = world.PlayerBehavior; + + // The old weapon has been lowered off the screen, + // so change the weapon and start raising it. + if (player.Health == 0) + { + // Player is dead, so keep the weapon off screen. + pb.SetPlayerSprite(player, PlayerSprite.Weapon, MobjState.Null); + return; + } + + player.ReadyWeapon = player.PendingWeapon; + + pb.BringUpWeapon(player); + } + + + public void Raise(Player player, PlayerSpriteDef psp) + { + psp.Sy -= RaiseSpeed; + + if (psp.Sy > WeaponTop) + { + return; + } + + psp.Sy = WeaponTop; + + // The weapon has been raised all the way, so change to the ready state. + var newState = DoomInfo.WeaponInfos[(int)player.ReadyWeapon].ReadyState; + + world.PlayerBehavior.SetPlayerSprite(player, PlayerSprite.Weapon, newState); + } + + + public void Punch(Player player) + { + var random = world.Random; + + var damage = (random.Next() % 10 + 1) << 1; + + if (player.Powers[(int)PowerType.Strength] != 0) + { + damage *= 10; + } + + var hs = world.Hitscan; + + var angle = player.Mobj.Angle; + angle += new Angle((random.Next() - random.Next()) << 18); + + var slope = hs.AimLineAttack(player.Mobj, angle, MeleeRange); + hs.LineAttack(player.Mobj, angle, MeleeRange, slope, damage); + + // Turn to face target. + if (hs.LineTarget != null) + { + world.StartSound(player.Mobj, Sfx.PUNCH, SfxType.Weapon); + + player.Mobj.Angle = Geometry.PointToAngle( + player.Mobj.X, player.Mobj.Y, + hs.LineTarget.X, hs.LineTarget.Y); + } + } + + + public void Saw(Player player) + { + var damage = 2 * (world.Random.Next() % 10 + 1); + + var random = world.Random; + + var attackAngle = player.Mobj.Angle; + attackAngle += new Angle((random.Next() - random.Next()) << 18); + + var hs = world.Hitscan; + + // Use MeleeRange + Fixed.Epsilon so that the puff doesn't skip the flash. + var slope = hs.AimLineAttack(player.Mobj, attackAngle, MeleeRange + Fixed.Epsilon); + hs.LineAttack(player.Mobj, attackAngle, MeleeRange + Fixed.Epsilon, slope, damage); + + if (hs.LineTarget == null) + { + world.StartSound(player.Mobj, Sfx.SAWFUL, SfxType.Weapon); + return; + } + + world.StartSound(player.Mobj, Sfx.SAWHIT, SfxType.Weapon); + + // Turn to face target. + var targetAngle = Geometry.PointToAngle( + player.Mobj.X, player.Mobj.Y, + hs.LineTarget.X, hs.LineTarget.Y); + + if (targetAngle - player.Mobj.Angle > Angle.Ang180) + { + // The code below is based on Mocha Doom's implementation. + // It is still unclear for me why this code works like the original verion... + if ((int)(targetAngle - player.Mobj.Angle).Data < -Angle.Ang90.Data / 20) + { + player.Mobj.Angle = targetAngle + Angle.Ang90 / 21; + } + else + { + player.Mobj.Angle -= Angle.Ang90 / 20; + } + } + else + { + if (targetAngle - player.Mobj.Angle > Angle.Ang90 / 20) + { + player.Mobj.Angle = targetAngle - Angle.Ang90 / 21; + } + else + { + player.Mobj.Angle += Angle.Ang90 / 20; + } + } + + player.Mobj.Flags |= MobjFlags.JustAttacked; + } + + + public void ReFire(Player player) + { + // Check for fire. + // If a weaponchange is pending, let it go through instead. + if ((player.Cmd.Buttons & TicCmdButtons.Attack) != 0 && + player.PendingWeapon == WeaponType.NoChange && + player.Health != 0) + { + player.Refire++; + FireWeapon(player); + } + else + { + player.Refire = 0; + CheckAmmo(player); + } + } + + + private void BulletSlope(Mobj mo) + { + var hs = world.Hitscan; + + // See which target is to be aimed at. + var angle = mo.Angle; + + currentBulletSlope = hs.AimLineAttack(mo, angle, Fixed.FromInt(1024)); + + if (hs.LineTarget == null) + { + angle += new Angle(1 << 26); + currentBulletSlope = hs.AimLineAttack(mo, angle, Fixed.FromInt(1024)); + if (hs.LineTarget == null) + { + angle -= new Angle(2 << 26); + currentBulletSlope = hs.AimLineAttack(mo, angle, Fixed.FromInt(1024)); + } + } + } + + + private void GunShot(Mobj mo, bool accurate) + { + var random = world.Random; + + var damage = 5 * (random.Next() % 3 + 1); + + var angle = mo.Angle; + + if (!accurate) + { + angle += new Angle((random.Next() - random.Next()) << 18); + } + + world.Hitscan.LineAttack(mo, angle, MissileRange, currentBulletSlope, damage); + } + + + public void FirePistol(Player player) + { + world.StartSound(player.Mobj, Sfx.PISTOL, SfxType.Weapon); + + player.Mobj.SetState(MobjState.PlayAtk2); + + player.Ammo[(int)DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo]--; + + world.PlayerBehavior.SetPlayerSprite( + player, + PlayerSprite.Flash, + DoomInfo.WeaponInfos[(int)player.ReadyWeapon].FlashState); + + BulletSlope(player.Mobj); + + GunShot(player.Mobj, player.Refire == 0); + } + + + public void Light1(Player player) + { + player.ExtraLight = 1; + } + + + public void FireShotgun(Player player) + { + world.StartSound(player.Mobj, Sfx.SHOTGN, SfxType.Weapon); + + player.Mobj.SetState(MobjState.PlayAtk2); + + player.Ammo[(int)DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo]--; + + world.PlayerBehavior.SetPlayerSprite( + player, + PlayerSprite.Flash, + DoomInfo.WeaponInfos[(int)player.ReadyWeapon].FlashState); + + BulletSlope(player.Mobj); + + for (var i = 0; i < 7; i++) + { + GunShot(player.Mobj, false); + } + } + + + public void Light2(Player player) + { + player.ExtraLight = 2; + } + + + public void FireCGun(Player player, PlayerSpriteDef psp) + { + world.StartSound(player.Mobj, Sfx.PISTOL, SfxType.Weapon); + + if (player.Ammo[(int)DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo] == 0) + { + return; + } + + player.Mobj.SetState(MobjState.PlayAtk2); + + player.Ammo[(int)DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo]--; + + world.PlayerBehavior.SetPlayerSprite( + player, + PlayerSprite.Flash, + DoomInfo.WeaponInfos[(int)player.ReadyWeapon].FlashState + + psp.State.Number - DoomInfo.States[(int)MobjState.Chain1].Number); + + BulletSlope(player.Mobj); + + GunShot(player.Mobj, player.Refire == 0); + } + + + public void FireShotgun2(Player player) + { + world.StartSound(player.Mobj, Sfx.DSHTGN, SfxType.Weapon); + + player.Mobj.SetState(MobjState.PlayAtk2); + + player.Ammo[(int)DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo] -= 2; + + world.PlayerBehavior.SetPlayerSprite( + player, + PlayerSprite.Flash, + DoomInfo.WeaponInfos[(int)player.ReadyWeapon].FlashState); + + BulletSlope(player.Mobj); + + var random = world.Random; + var hs = world.Hitscan; + + for (var i = 0; i < 20; i++) + { + var damage = 5 * (random.Next() % 3 + 1); + var angle = player.Mobj.Angle; + angle += new Angle((random.Next() - random.Next()) << 19); + hs.LineAttack( + player.Mobj, + angle, + MissileRange, + currentBulletSlope + new Fixed((random.Next() - random.Next()) << 5), + damage); + } + } + + + public void CheckReload(Player player) + { + CheckAmmo(player); + } + + + public void OpenShotgun2(Player player) + { + world.StartSound(player.Mobj, Sfx.DBOPN, SfxType.Weapon); + } + + + public void LoadShotgun2(Player player) + { + world.StartSound(player.Mobj, Sfx.DBLOAD, SfxType.Weapon); + } + + + public void CloseShotgun2(Player player) + { + world.StartSound(player.Mobj, Sfx.DBCLS, SfxType.Weapon); + ReFire(player); + } + + + public void GunFlash(Player player) + { + player.Mobj.SetState(MobjState.PlayAtk2); + + world.PlayerBehavior.SetPlayerSprite( + player, + PlayerSprite.Flash, + DoomInfo.WeaponInfos[(int)player.ReadyWeapon].FlashState); + } + + + public void FireMissile(Player player) + { + player.Ammo[(int)DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo]--; + + world.ThingAllocation.SpawnPlayerMissile(player.Mobj, MobjType.Rocket); + } + + + public void FirePlasma(Player player) + { + player.Ammo[(int)DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo]--; + + world.PlayerBehavior.SetPlayerSprite( + player, + PlayerSprite.Flash, + DoomInfo.WeaponInfos[(int)player.ReadyWeapon].FlashState + (world.Random.Next() & 1)); + + world.ThingAllocation.SpawnPlayerMissile(player.Mobj, MobjType.Plasma); + } + + + public void A_BFGsound(Player player) + { + world.StartSound(player.Mobj, Sfx.BFG, SfxType.Weapon); + } + + + public void FireBFG(Player player) + { + player.Ammo[(int)DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo] -= DoomInfo.DeHackEdConst.BfgCellsPerShot; + + world.ThingAllocation.SpawnPlayerMissile(player.Mobj, MobjType.Bfg); + } + + + public void BFGSpray(Mobj bfgBall) + { + var hs = world.Hitscan; + var random = world.Random; + + // Offset angles from its attack angle. + for (var i = 0; i < 40; i++) + { + var an = bfgBall.Angle - Angle.Ang90 / 2 + Angle.Ang90 / 40 * (uint)i; + + // bfgBall.Target is the originator (player) of the missile. + hs.AimLineAttack(bfgBall.Target, an, Fixed.FromInt(16 * 64)); + + if (hs.LineTarget == null) + { + continue; + } + + world.ThingAllocation.SpawnMobj( + hs.LineTarget.X, + hs.LineTarget.Y, + hs.LineTarget.Z + (hs.LineTarget.Height >> 2), + MobjType.Extrabfg); + + var damage = 0; + for (var j = 0; j < 15; j++) + { + damage += (random.Next() & 7) + 1; + } + + world.ThingInteraction.DamageMobj( + hs.LineTarget, + bfgBall.Target, + bfgBall.Target, + damage); + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/WeaponInfo.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/WeaponInfo.cs new file mode 100644 index 00000000..ccef656b --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/WeaponInfo.cs @@ -0,0 +1,83 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed class WeaponInfo + { + private AmmoType ammo; + private MobjState upState; + private MobjState downState; + private MobjState readyState; + private MobjState attackState; + private MobjState flashState; + + public WeaponInfo( + AmmoType ammo, + MobjState upState, + MobjState downState, + MobjState readyState, + MobjState attackState, + MobjState flashState) + { + this.ammo = ammo; + this.upState = upState; + this.downState = downState; + this.readyState = readyState; + this.attackState = attackState; + this.flashState = flashState; + } + + public AmmoType Ammo + { + get => ammo; + set => ammo = value; + } + + public MobjState UpState + { + get => upState; + set => upState = value; + } + + public MobjState DownState + { + get => downState; + set => downState = value; + } + + public MobjState ReadyState + { + get => readyState; + set => readyState = value; + } + + public MobjState AttackState + { + get => attackState; + set => attackState = value; + } + + public MobjState FlashState + { + get => flashState; + set => flashState = value; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/WeaponType.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/WeaponType.cs new file mode 100644 index 00000000..5b72fbbe --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/WeaponType.cs @@ -0,0 +1,39 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum WeaponType + { + Fist, + Pistol, + Shotgun, + Chaingun, + Missile, + Plasma, + Bfg, + Chainsaw, + SuperShotgun, + + Count, + + // No pending weapon change. + NoChange + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/World.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/World.cs new file mode 100644 index 00000000..bc13c0b8 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/Doom/World/World.cs @@ -0,0 +1,378 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public sealed partial class World + { + private GameOptions options; + private DoomGame game; + private DoomRandom random; + + private Map map; + + private Thinkers thinkers; + private Specials specials; + private ThingAllocation thingAllocation; + private ThingMovement thingMovement; + private ThingInteraction thingInteraction; + private MapCollision mapCollision; + private MapInteraction mapInteraction; + private PathTraversal pathTraversal; + private Hitscan hitscan; + private VisibilityCheck visibilityCheck; + private SectorAction sectorAction; + private PlayerBehavior playerBehavior; + private ItemPickup itemPickup; + private WeaponBehavior weaponBehavior; + private MonsterBehavior monsterBehavior; + private LightingChange lightingChange; + private StatusBar statusBar; + private AutoMap autoMap; + private Cheat cheat; + + private int totalKills; + private int totalItems; + private int totalSecrets; + + private int levelTime; + private bool doneFirstTic; + private bool secretExit; + private bool completed; + + private int validCount; + + private int displayPlayer; + + public World(CommonResource resorces, GameOptions options) : this(resorces, options, null) + { + } + + public World(CommonResource resorces, GameOptions options, DoomGame game) + { + this.options = options; + this.game = game; + + if (game != null) + { + random = game.Random; + } + else + { + random = new DoomRandom(); + } + + map = new Map(resorces, this); + + thinkers = new Thinkers(this); + specials = new Specials(this); + thingAllocation = new ThingAllocation(this); + thingMovement = new ThingMovement(this); + thingInteraction = new ThingInteraction(this); + mapCollision = new MapCollision(this); + mapInteraction = new MapInteraction(this); + pathTraversal = new PathTraversal(this); + hitscan = new Hitscan(this); + visibilityCheck = new VisibilityCheck(this); + sectorAction = new SectorAction(this); + playerBehavior = new PlayerBehavior(this); + itemPickup = new ItemPickup(this); + weaponBehavior = new WeaponBehavior(this); + monsterBehavior = new MonsterBehavior(this); + lightingChange = new LightingChange(this); + statusBar = new StatusBar(this); + autoMap = new AutoMap(this); + cheat = new Cheat(this); + + options.IntermissionInfo.TotalFrags = 0; + options.IntermissionInfo.ParTime = 180; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + options.Players[i].KillCount = 0; + options.Players[i].SecretCount = 0; + options.Players[i].ItemCount = 0; + } + + // Initial height of view will be set by player think. + options.Players[options.ConsolePlayer].ViewZ = Fixed.Epsilon; + + totalKills = 0; + totalItems = 0; + totalSecrets = 0; + + LoadThings(); + + // If deathmatch, randomly spawn the active players. + if (options.Deathmatch != 0) + { + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (options.Players[i].InGame) + { + options.Players[i].Mobj = null; + thingAllocation.DeathMatchSpawnPlayer(i); + } + } + } + + specials.SpawnSpecials(); + + levelTime = 0; + doneFirstTic = false; + secretExit = false; + completed = false; + + validCount = 0; + + displayPlayer = options.ConsolePlayer; + + options.Music.StartMusic(Map.GetMapBgm(options), true); + } + + public UpdateResult Update() + { + var players = options.Players; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (players[i].InGame) + { + playerBehavior.PlayerThink(players[i]); + } + } + + thinkers.Run(); + specials.Update(); + thingAllocation.RespawnSpecials(); + + statusBar.Update(); + autoMap.Update(); + + levelTime++; + + if (completed) + { + return UpdateResult.Completed; + } + else + { + if (doneFirstTic) + { + return UpdateResult.None; + } + else + { + doneFirstTic = true; + return UpdateResult.NeedWipe; + } + } + } + + private void LoadThings() + { + for (var i = 0; i < map.Things.Length; i++) + { + var mt = map.Things[i]; + + var spawn = true; + + // Do not spawn cool, new monsters if not commercial. + if (options.GameMode != GameMode.Commercial) + { + switch (mt.Type) + { + case 68: // Arachnotron + case 64: // Archvile + case 88: // Boss Brain + case 89: // Boss Shooter + case 69: // Hell Knight + case 67: // Mancubus + case 71: // Pain Elemental + case 65: // Former Human Commando + case 66: // Revenant + case 84: // Wolf SS + spawn = false; + break; + } + } + + if (!spawn) + { + break; + } + + thingAllocation.SpawnMapThing(mt); + } + } + + public void ExitLevel() + { + secretExit = false; + completed = true; + } + + public void SecretExitLevel() + { + secretExit = true; + completed = true; + } + + public void StartSound(Mobj mobj, Sfx sfx, SfxType type) + { + options.Sound.StartSound(mobj, sfx, type); + } + + public void StartSound(Mobj mobj, Sfx sfx, SfxType type, int volume) + { + options.Sound.StartSound(mobj, sfx, type, volume); + } + + public void StopSound(Mobj mobj) + { + options.Sound.StopSound(mobj); + } + + public int GetNewValidCount() + { + validCount++; + return validCount; + } + + public bool DoEvent(DoomEvent e) + { + if (!options.NetGame && options.Skill != GameSkill.Nightmare) + { + cheat.DoEvent(e); + } + + if (autoMap.Visible) + { + if (autoMap.DoEvent(e)) + { + return true; + } + } + + if (e.Key == DoomKey.Tab && e.Type == EventType.KeyDown) + { + if (autoMap.Visible) + { + autoMap.Close(); + } + else + { + autoMap.Open(); + } + return true; + } + + if (e.Key == DoomKey.F12 && e.Type == EventType.KeyDown) + { + if (options.DemoPlayback || options.Deathmatch == 0) + { + ChangeDisplayPlayer(); + } + return true; + } + + return false; + } + + public void ChangeDisplayPlayer() + { + displayPlayer++; + if (displayPlayer == Player.MaxPlayerCount || + !options.Players[displayPlayer].InGame) + { + displayPlayer = 0; + } + } + + public GameOptions Options => options; + public DoomGame Game => game; + public DoomRandom Random => random; + + public Map Map => map; + + public Thinkers Thinkers => thinkers; + public Specials Specials => specials; + public ThingAllocation ThingAllocation => thingAllocation; + public ThingMovement ThingMovement => thingMovement; + public ThingInteraction ThingInteraction => thingInteraction; + public MapCollision MapCollision => mapCollision; + public MapInteraction MapInteraction => mapInteraction; + public PathTraversal PathTraversal => pathTraversal; + public Hitscan Hitscan => hitscan; + public VisibilityCheck VisibilityCheck => visibilityCheck; + public SectorAction SectorAction => sectorAction; + public PlayerBehavior PlayerBehavior => playerBehavior; + public ItemPickup ItemPickup => itemPickup; + public WeaponBehavior WeaponBehavior => weaponBehavior; + public MonsterBehavior MonsterBehavior => monsterBehavior; + public LightingChange LightingChange => lightingChange; + public StatusBar StatusBar => statusBar; + public AutoMap AutoMap => autoMap; + public Cheat Cheat => cheat; + + public int TotalKills + { + get => totalKills; + set => totalKills = value; + } + + public int TotalItems + { + get => totalItems; + set => totalItems = value; + } + + public int TotalSecrets + { + get => totalSecrets; + set => totalSecrets = value; + } + + public int LevelTime + { + get => levelTime; + set => levelTime = value; + } + + public int GameTic + { + get + { + if (game != null) + { + return game.GameTic; + } + else + { + return levelTime; + } + } + } + + public bool SecretExit => secretExit; + + public Player ConsolePlayer => options.Players[options.ConsolePlayer]; + public Player DisplayPlayer => options.Players[displayPlayer]; + public bool FirstTicIsNotYetDone => ConsolePlayer.ViewZ == Fixed.Epsilon; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/DoomApplication.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/DoomApplication.cs new file mode 100644 index 00000000..c2804921 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/DoomApplication.cs @@ -0,0 +1,643 @@ +using System.Linq; +using System.Net.Http; +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.Runtime.ExceptionServices; +using SFML.Graphics; +using SFML.Window; +using ManagedDoom.SoftwareRendering; +using ManagedDoom.Audio; +using ManagedDoom.UserInput; +using System.IO; +using System.Threading.Tasks; +using Aura_OS; + +namespace ManagedDoom +{ + public sealed class DoomApplication : IDisposable + { + private Config config; + + public RenderWindow window { get; internal set; } + + private CommonResource resource; + public SfmlRenderer renderer; + private SfmlUserInput userInput; + + private static List Events { get; set; } + + + static DoomApplication() + { + Events = new List(); + } + + private GameOptions options; + + private DoomMenu menu; + + private OpeningSequence opening; + + private DemoPlayback demoPlayback; + + private TicCmd[] cmds; + private DoomGame game; + + private WipeEffect wipe; + private bool wiping; + + private ApplicationState currentState; + private ApplicationState nextState; + private bool needWipe; + + private bool sendPause; + + private bool quit; + private string quitMessage; + public static Stream WadStream { get; internal set; } + + public DoomApplication(CommandLineArgs args, String[] configLines) + { + configLines = new string[] { + "video_screenwidth=320", + "video_screenHeight=200", + }; + config = new Config(configLines); + + try + { + WadStream = new MemoryStream(Files.DoomWad); + config.video_screenwidth = Math.Clamp(config.video_screenwidth, 320, 3200); + config.video_screenheight = Math.Clamp(config.video_screenheight, 200, 2000); + var videoMode = VideoMode.CanvasMode; + var style = Styles.Close | Styles.Titlebar; + if (config.video_fullscreen) + { + style = Styles.Fullscreen; + } + window = new RenderWindow(videoMode, ApplicationInfo.Title, style); + window.Clear(new Color(64, 64, 64)); + window.Display(); + + if (args.deh.Present) + { + DeHackEd.ReadFiles(args.deh.Value); + } + + // resource = new CommonResource(GetWadPaths(args)); + resource = new CommonResource(new string[] { "0:\\doom1.wad" }); + + renderer = new SfmlRenderer(config, window, resource); + + if (!args.nosound.Present && !args.nosfx.Present) + { + + } + + if (!args.nosound.Present && !args.nomusic.Present) + { + + } + + userInput = new SfmlUserInput(config, window, !args.nomouse.Present); + + + + options = new GameOptions(); + options.GameVersion = resource.Wad.GameVersion; + options.GameMode = resource.Wad.GameMode; + options.MissionPack = resource.Wad.MissionPack; + options.Renderer = renderer; + + options.UserInput = userInput; + + menu = new DoomMenu(this); + + opening = new OpeningSequence(resource, options); + + cmds = new TicCmd[Player.MaxPlayerCount]; + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + cmds[i] = new TicCmd(); + } + game = new DoomGame(resource, options); + + wipe = new WipeEffect(renderer.WipeBandCount, renderer.WipeHeight); + wiping = false; + + currentState = ApplicationState.None; + nextState = ApplicationState.Opening; + needWipe = false; + + sendPause = false; + + quit = false; + quitMessage = null; + + CheckGameArgs(args); + } + catch (Exception e) + { + Dispose(); + ExceptionDispatchInfo.Throw(e); + } + + } + + private string[] GetWadPaths(CommandLineArgs args) + { + var wadPaths = new List(); + + if (args.iwad.Present) + { + wadPaths.Add(args.iwad.Value); + } + /*else + { + wadPaths.Add(ConfigUtilities.GetDefaultIwadPath()); + }*/ + // TODO: add wads + + if (args.file.Present) + { + foreach (var path in args.file.Value) + { + wadPaths.Add(path); + } + } + + return wadPaths.ToArray(); + } + + private void CheckGameArgs(CommandLineArgs args) + { + if (args.warp.Present) + { + nextState = ApplicationState.Game; + options.Episode = args.warp.Value.Item1; + options.Map = args.warp.Value.Item2; + 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 = ApplicationState.Game; + game.LoadGame(args.loadgame.Value); + } + + if (args.playdemo.Present) + { + nextState = ApplicationState.DemoPlayback; + demoPlayback = new DemoPlayback(resource, options, args.playdemo.Value); + } + + if (args.timedemo.Present) + { + nextState = ApplicationState.DemoPlayback; + demoPlayback = new DemoPlayback(resource, options, args.timedemo.Value); + } + } + + public void Run(uint[] downKeys, uint[] upKeys) + { + // var watch = System.Diagnostics.Stopwatch.StartNew(); + + Events.Clear(); + foreach (var upKey in upKeys) + { + Events.Add(new DoomEvent(EventType.KeyUp, (DoomKey)upKey)); + } + foreach (var downKey in downKeys) + { + Events.Add(new DoomEvent(EventType.KeyDown, (DoomKey)downKey)); + } + + DoEvents(); + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("events: {0} ms", (float)(watch.ElapsedMilliseconds) / 1000); + // watch.Restart(); + if (Update() == UpdateResult.Completed) + { + // config.Save(ConfigUtilities.GetConfigPath()); + // TODO: handle config save + return; + } + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("update: {0} ms", watch.ElapsedMilliseconds); + } + + public static bool IsKeyPressed(DoomKey key) + { + return Events.Any(e => + { + return e.Type == EventType.KeyDown && e.Key == key; + } + ); + } + + public void NewGame(GameSkill skill, int episode, int map) + { + game.DeferedInitNew(skill, episode, map); + nextState = ApplicationState.Game; + } + + public void EndGame() + { + nextState = ApplicationState.Opening; + } + + private void DoEvents() + { + if (wiping) + { + return; + } + + foreach (var e in Events) + { + if (menu.DoEvent(e)) + { + continue; + } + + if (e.Type == EventType.KeyDown) + { + if (CheckFunctionKey(e.Key)) + { + continue; + } + } + + if (currentState == ApplicationState.Game) + { + if (e.Key == DoomKey.Pause && e.Type == EventType.KeyDown) + { + sendPause = true; + continue; + } + + if (game.DoEvent(e)) + { + continue; + } + } + else if (currentState == ApplicationState.DemoPlayback) + { + demoPlayback.DoEvent(e); + } + } + + + } + + 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 == ApplicationState.Game) + { + menu.EndGame(); + } + else + { + options.Sound.StartSound(Sfx.OOF); + } + return true; + + case DoomKey.F8: + renderer.DisplayMessage = !renderer.DisplayMessage; + if (currentState == ApplicationState.Game && game.State == GameState.Level) + { + string msg; + if (renderer.DisplayMessage) + { + msg = DoomInfo.Strings.MSGON; + } + else + { + msg = DoomInfo.Strings.MSGOFF; + } + game.World.ConsolePlayer.SendMessage(msg); + } + menu.StartSound(Sfx.SWTCHN); + return true; + + case DoomKey.F9: + menu.QuickLoad(); + return true; + + case DoomKey.F10: + menu.Quit(); + return true; + + case DoomKey.F11: + var gcl = renderer.GammaCorrectionLevel; + gcl++; + if (gcl > renderer.MaxGammaCorrectionLevel) + { + gcl = 0; + } + renderer.GammaCorrectionLevel = gcl; + if (currentState == ApplicationState.Game && game.State == GameState.Level) + { + string msg; + if (gcl == 0) + { + msg = DoomInfo.Strings.GAMMALVL0; + } + else + { + msg = "Gamma correction level " + gcl; + } + game.World.ConsolePlayer.SendMessage(msg); + } + return true; + + case DoomKey.Add: + case DoomKey.Quote: + if (currentState == ApplicationState.Game && + game.State == GameState.Level && + game.World.AutoMap.Visible) + { + return false; + } + else + { + renderer.WindowSize = Math.Min(renderer.WindowSize + 1, renderer.MaxWindowSize); + options.Sound.StartSound(Sfx.STNMOV); + return true; + } + + case DoomKey.Subtract: + case DoomKey.Hyphen: + if (currentState == ApplicationState.Game && + game.State == GameState.Level && + game.World.AutoMap.Visible) + { + return false; + } + else + { + renderer.WindowSize = Math.Max(renderer.WindowSize - 1, 0); + options.Sound.StartSound(Sfx.STNMOV); + return true; + } + + default: + return false; + } + } + private UpdateResult Update() + { + if (!wiping) + { + menu.Update(); + + if (nextState != currentState) + { + if (nextState != ApplicationState.Opening) + { + opening.Reset(); + } + + if (nextState != ApplicationState.DemoPlayback) + { + demoPlayback = null; + } + + currentState = nextState; + } + + if (quit) + { + return UpdateResult.Completed; + } + + if (needWipe) + { + needWipe = false; + StartWipe(); + } + } + + if (!wiping) + { + switch (currentState) + { + case ApplicationState.Opening: + if (opening.Update() == UpdateResult.NeedWipe) + { + StartWipe(); + } + break; + + case ApplicationState.DemoPlayback: + var result = demoPlayback.Update(); + if (result == UpdateResult.NeedWipe) + { + StartWipe(); + } + else if (result == UpdateResult.Completed) + { + Quit("FPS: " + demoPlayback.Fps.ToString("0.0")); + } + break; + + case ApplicationState.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) + { + var result = wipe.Update(); + renderer.RenderWipe(this, wipe); + if (result == UpdateResult.Completed) + { + wiping = false; + } + } + else + { + renderer.Render(this); + } + + options.Sound.Update(); + + return UpdateResult.None; + } + + private void StartWipe() + { + wipe.Start(); + renderer.InitializeWipe(); + wiping = true; + } + + public void PauseGame() + { + if (currentState == ApplicationState.Game && + game.State == GameState.Level && + !game.Paused && !sendPause) + { + sendPause = true; + } + } + + public void ResumeGame() + { + if (currentState == ApplicationState.Game && + game.State == GameState.Level && + game.Paused && !sendPause) + { + sendPause = true; + } + } + + public bool SaveGame(int slotNumber, string description) + { + if (currentState == ApplicationState.Game && game.State == GameState.Level) + { + game.SaveGame(slotNumber, description); + return true; + } + else + { + return false; + } + } + + public void LoadGame(int slotNumber) + { + game.LoadGame(slotNumber); + nextState = ApplicationState.Game; + } + + public void Quit() + { + quit = true; + } + + public void Quit(string message) + { + quit = true; + quitMessage = message; + } + + public void Dispose() + { + if (userInput != null) + { + userInput.Dispose(); + userInput = null; + } + + + + if (renderer != null) + { + renderer.Dispose(); + renderer = null; + } + + if (resource != null) + { + resource.Dispose(); + resource = null; + } + + if (window != null) + { + window.Dispose(); + window = null; + } + } + + public ApplicationState State => currentState; + public OpeningSequence Opening => opening; + public DemoPlayback DemoPlayback => demoPlayback; + public GameOptions Options => options; + public DoomGame Game => game; + public DoomMenu Menu => menu; + public string QuitMessage => quitMessage; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Audio/Sound.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Audio/Sound.cs new file mode 100644 index 00000000..059b79d6 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Audio/Sound.cs @@ -0,0 +1,42 @@ + +using System.Collections.Generic; +using System; +using ManagedDoom; +using SFML.System; +using Time = System.TimeSpan; +namespace SFML.Audio +{ + public class Sound + { + public SoundStatus Status { get; internal set; } + public SoundBuffer SoundBuffer { get; internal set; } + public float Pitch { get; internal set; } + public float Volume { get; internal set; } + public Time PlayingOffset { get; internal set; } + public Vector3f Position { get; internal set; } + + internal void Stop() + { + // TODO: implement + } + + internal void Play() + { + } + + public override string ToString() + { + return $"{SoundBuffer.samples.Length} samples. Pitch: {Pitch}, Volume: {Volume}, Position: {Position}"; + } + + internal void Pause() + { + // TODO: implement + } + + internal void Dispose() + { + // TODO: implement + } + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Audio/SoundBuffer.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Audio/SoundBuffer.cs new file mode 100644 index 00000000..d451600f --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Audio/SoundBuffer.cs @@ -0,0 +1,29 @@ + +using System; +using SFML.System; + +using Time = System.TimeSpan; + +namespace SFML.Audio +{ + public class SoundBuffer + { + public short[] samples; + private int v; + public uint sampleRate; + + public SoundBuffer(short[] samples, int v, uint sampleRate) + { + this.samples = samples; + this.v = v; + this.sampleRate = sampleRate; + } + + public Time Duration { get; internal set; } + + internal void Dispose() + { + // TODO: implement + } + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Audio/SoundStatus.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Audio/SoundStatus.cs new file mode 100644 index 00000000..b6421d39 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Audio/SoundStatus.cs @@ -0,0 +1,19 @@ +namespace SFML.Audio +{ + //////////////////////////////////////////////////////////// + /// + /// Enumeration of all possible sound states + /// + //////////////////////////////////////////////////////////// + public enum SoundStatus + { + /// Sound is not playing + Stopped, + + /// Sound is paused + Paused, + + /// Sound is playing + Playing + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/BlendMode.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/BlendMode.cs new file mode 100644 index 00000000..9940df5c --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/BlendMode.cs @@ -0,0 +1,206 @@ +using System; +using System.Runtime.InteropServices; + +namespace SFML.Graphics +{ + //////////////////////////////////////////////////////////// + /// + /// Blending modes for drawing + /// + //////////////////////////////////////////////////////////// + [StructLayout(LayoutKind.Sequential)] + public struct BlendMode : IEquatable + { + //////////////////////////////////////////////////////////// + /// + /// Enumeration of the blending factors + /// + //////////////////////////////////////////////////////////// + public enum Factor + { + /// (0, 0, 0, 0) + Zero, + + /// (1, 1, 1, 1) + One, + + /// (src.r, src.g, src.b, src.a) + SrcColor, + + /// (1, 1, 1, 1) - (src.r, src.g, src.b, src.a) + OneMinusSrcColor, + + /// (dst.r, dst.g, dst.b, dst.a) + DstColor, + + /// (1, 1, 1, 1) - (dst.r, dst.g, dst.b, dst.a) + OneMinusDstColor, + + /// (src.a, src.a, src.a, src.a) + SrcAlpha, + + /// (1, 1, 1, 1) - (src.a, src.a, src.a, src.a) + OneMinusSrcAlpha, + + /// (dst.a, dst.a, dst.a, dst.a) + DstAlpha, + + /// (1, 1, 1, 1) - (dst.a, dst.a, dst.a, dst.a) + OneMinusDstAlpha + } + + //////////////////////////////////////////////////////////// + /// + /// Enumeration of the blending equations + /// + //////////////////////////////////////////////////////////// + public enum Equation + { + /// Pixel = Src * SrcFactor + Dst * DstFactor + Add, + + /// Pixel = Src * SrcFactor - Dst * DstFactor + Subtract, + + /// Pixel = Dst * DstFactor - Src * SrcFactor + ReverseSubtract + } + + /// Blend source and dest according to dest alpha + public static readonly BlendMode Alpha = new BlendMode(Factor.SrcAlpha, Factor.OneMinusSrcAlpha, Equation.Add, + Factor.One, Factor.OneMinusSrcAlpha, Equation.Add); + + /// Add source to dest + public static readonly BlendMode Add = new BlendMode(Factor.SrcAlpha, Factor.One, Equation.Add, + Factor.One, Factor.One, Equation.Add); + + /// Multiply source and dest + public static readonly BlendMode Multiply = new BlendMode(Factor.DstColor, Factor.Zero); + + /// Overwrite dest with source + public static readonly BlendMode None = new BlendMode(Factor.One, Factor.Zero); + + + //////////////////////////////////////////////////////////// + /// + /// Construct the blend mode given the factors and equation + /// + /// Specifies how to compute the source factor for the color and alpha channels. + /// Specifies how to compute the destination factor for the color and alpha channels. + //////////////////////////////////////////////////////////// + public BlendMode(Factor SourceFactor, Factor DestinationFactor) + : this(SourceFactor, DestinationFactor, Equation.Add) + { + } + + //////////////////////////////////////////////////////////// + /// + /// Construct the blend mode given the factors and equation + /// + /// Specifies how to compute the source factor for the color and alpha channels. + /// Specifies how to compute the destination factor for the color and alpha channels. + /// Specifies how to combine the source and destination colors and alpha. + //////////////////////////////////////////////////////////// + public BlendMode(Factor SourceFactor, Factor DestinationFactor, Equation BlendEquation) + : this(SourceFactor, DestinationFactor, BlendEquation, SourceFactor, DestinationFactor, BlendEquation) + { + } + + //////////////////////////////////////////////////////////// + /// + /// Construct the blend mode given the factors and equation + /// + /// Specifies how to compute the source factor for the color channels. + /// Specifies how to compute the destination factor for the color channels. + /// Specifies how to combine the source and destination colors. + /// Specifies how to compute the source factor. + /// Specifies how to compute the destination factor. + /// Specifies how to combine the source and destination alphas. + //////////////////////////////////////////////////////////// + public BlendMode(Factor ColorSourceFactor, Factor ColorDestinationFactor, Equation ColorBlendEquation, Factor AlphaSourceFactor, Factor AlphaDestinationFactor, Equation AlphaBlendEquation) + { + ColorSrcFactor = ColorSourceFactor; + ColorDstFactor = ColorDestinationFactor; + ColorEquation = ColorBlendEquation; + AlphaSrcFactor = AlphaSourceFactor; + AlphaDstFactor = AlphaDestinationFactor; + AlphaEquation = AlphaBlendEquation; + } + + //////////////////////////////////////////////////////////// + /// + /// Compare two blend modes and checks if they are equal + /// + /// Blend Modes are equal + //////////////////////////////////////////////////////////// + public static bool operator ==(BlendMode left, BlendMode right) => left.Equals(right); + + //////////////////////////////////////////////////////////// + /// + /// Compare two blend modes and checks if they are not equal + /// + /// Blend Modes are not equal + //////////////////////////////////////////////////////////// + public static bool operator !=(BlendMode left, BlendMode right) => !left.Equals(right); + + //////////////////////////////////////////////////////////// + /// + /// Compare blend mode and object and checks if they are equal + /// + /// Object to check + /// Object and blend mode are equal + //////////////////////////////////////////////////////////// + public override bool Equals(object obj) => (obj is BlendMode) && Equals((BlendMode)obj); + + /////////////////////////////////////////////////////////// + /// + /// Compare two blend modes and checks if they are equal + /// + /// Blend Mode to check + /// blend modes are equal + //////////////////////////////////////////////////////////// + public bool Equals(BlendMode other) + { + return (ColorSrcFactor == other.ColorSrcFactor) && + (ColorDstFactor == other.ColorDstFactor) && + (ColorEquation == other.ColorEquation) && + (AlphaSrcFactor == other.AlphaSrcFactor) && + (AlphaDstFactor == other.AlphaDstFactor) && + (AlphaEquation == other.AlphaEquation); + } + + //////////////////////////////////////////////////////////// + /// + /// Provide a integer describing the object + /// + /// Integer description of the object + //////////////////////////////////////////////////////////// + public override int GetHashCode() + { + return ColorSrcFactor.GetHashCode() ^ + ColorDstFactor.GetHashCode() ^ + ColorEquation.GetHashCode() ^ + AlphaSrcFactor.GetHashCode() ^ + AlphaDstFactor.GetHashCode() ^ + AlphaEquation.GetHashCode(); + } + + /// Source blending factor for the color channels + public Factor ColorSrcFactor; + + /// Destination blending factor for the color channels + public Factor ColorDstFactor; + + /// Blending equation for the color channels + public Equation ColorEquation; + + /// Source blending factor for the alpha channel + public Factor AlphaSrcFactor; + + /// Destination blending factor for the alpha channel + public Factor AlphaDstFactor; + + /// Blending equation for the alpha channel + public Equation AlphaEquation; + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/Color.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/Color.cs new file mode 100644 index 00000000..96833850 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/Color.cs @@ -0,0 +1,10 @@ +namespace SFML.Graphics +{ + public class Color + { + public Color(uint red, uint green, uint blue) + { + + } + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/KeyEventArgs.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/KeyEventArgs.cs new file mode 100644 index 00000000..8e6b4d52 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/KeyEventArgs.cs @@ -0,0 +1,9 @@ +using ManagedDoom; + +namespace SFML.Graphics +{ + public class KeyEventArgs + { + public DoomKey Code { get; set; } + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/RenderStates.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/RenderStates.cs new file mode 100644 index 00000000..c8c41444 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/RenderStates.cs @@ -0,0 +1,13 @@ +namespace SFML.Graphics +{ + public struct RenderStates + { + private BlendMode blendMode; + + public RenderStates(BlendMode blendMode) + { + this.blendMode = blendMode; + } + } + +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/RenderWindow.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/RenderWindow.cs new file mode 100644 index 00000000..a6090921 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/RenderWindow.cs @@ -0,0 +1,77 @@ +using System; +using System.Threading.Tasks; +using SFML.System; +using SFML.Window; + +namespace SFML.Graphics +{ + + public class RenderWindow : SFML.Window.Window + { + public bool IsOpen => true; + + public event EventHandler KeyPressed = null; + public event EventHandler KeyReleased = null; + + public RenderWindow(VideoMode videoMode, string title, Styles style) + { + VideoMode = videoMode; + } + + public virtual Vector2u Size + { + get { return new Vector2u(VideoMode.Width, VideoMode.Height); } + } + + public VideoMode VideoMode { get; } + + public void Clear(Color color) + { + + } + + public void Display() + { + + } + + internal void SetFramerateLimit(int v) + { + // TODO: implement + } + + internal void DispatchEvents(uint[] keys) + { + + } + + internal void Dispose() + { + // TODO: implement + } + + internal void Draw(Sprite sfmlSprite, RenderStates sfmlStates) + { + //await VideoMode.Draw(sfmlSprite); + /*for (uint i = 0; i < sfmlSprite.Texture.Width; i++) + { + for (uint j = 0; j < sfmlSprite.Texture.Height; j++) + { + float x = sfmlSprite.Position.X + i; + float y = sfmlSprite.Position.Y + j; + await VideoMode.Draw(sfmlSprite.Texture[i, j], new Vector2f(x, y)); + } + }*/ + } + + internal void SetMouseCursorGrabbed(bool v) + { + // TODO: implement + } + + internal void SetMouseCursorVisible(bool v) + { + // TODO: implement + } + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/Sprite.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/Sprite.cs new file mode 100644 index 00000000..694f48ed --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/Sprite.cs @@ -0,0 +1,30 @@ + +using System; +using SFML.System; + +namespace SFML.Graphics +{ + public class Sprite + { + public Texture Texture { get; internal set; } + public Vector2f Position { get; internal set; } + public int Rotation { get; internal set; } + public Vector2f Scale { get; internal set; } + + public Sprite(Texture sfmlTexture) + { + this.Texture = sfmlTexture; + } + + internal void Dispose() + { + // TODO:: implement + } + + public override string ToString() => $"{Position}, {Rotation}, {Scale}"; + + + + } + +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/Texture.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/Texture.cs new file mode 100644 index 00000000..2d82d868 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Graphics/Texture.cs @@ -0,0 +1,41 @@ +using System; + +namespace SFML.Graphics +{ + // sfml byte[] format is rgba https://en.sfml-dev.org/forums/index.php?topic=2476.0 + public class Texture + { + public Texture(uint width, uint height) + { + Width = width; + Height = height; + byteRgbaData = new ushort[this.Width * this.Height * 4]; + } + + public uint Width { get; } + public uint Height { get; } + public ushort[] byteRgbaData { get; private set; } + + + internal void Update(byte[] sfmlTextureData, uint height, uint width, uint x, uint y) + { + Array.Copy(sfmlTextureData, byteRgbaData, sfmlTextureData.Length); + /*for (uint i = 0; i < height; i++) + { + uint destinationY = y + i; + // convert array of byte to array of uint for simpler manipulation + uint[] data = new uint[width * height]; + Buffer.BlockCopy(sfmlTextureData, 0, data, 0, (int)(width * height)); + // copy data to correct location in texture + Array.Copy(data, i * width, Pixels, destinationY * this.Width + x, width); + }*/ + //sfmlTextureData.CopyTo(pixels, ); + } + + internal void Dispose() + { + //Array.Clear(this.byteRgbaData, 0, this.byteRgbaData.Length); + } + } + +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/System/Vector2.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/System/Vector2.cs new file mode 100644 index 00000000..3d5c6602 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/System/Vector2.cs @@ -0,0 +1,476 @@ +using System; +using System.Runtime.InteropServices; + +namespace SFML.System +{ + //////////////////////////////////////////////////////////// + /// + /// Vector2f is an utility class for manipulating 2 dimensional + /// vectors with float components + /// + //////////////////////////////////////////////////////////// + [StructLayout(LayoutKind.Sequential)] + public struct Vector2f : IEquatable + { + //////////////////////////////////////////////////////////// + /// + /// Construct the vector from its coordinates + /// + /// X coordinate + /// Y coordinate + //////////////////////////////////////////////////////////// + public Vector2f(float x, float y) + { + X = x; + Y = y; + } + + //////////////////////////////////////////////////////////// + /// + /// Operator - overload ; returns the opposite of a vector + /// + /// Vector to negate + /// -v + //////////////////////////////////////////////////////////// + public static Vector2f operator -(Vector2f v) => new Vector2f(-v.X, -v.Y); + + //////////////////////////////////////////////////////////// + /// + /// Operator - overload ; subtracts two vectors + /// + /// First vector + /// Second vector + /// v1 - v2 + //////////////////////////////////////////////////////////// + public static Vector2f operator -(Vector2f v1, Vector2f v2) => new Vector2f(v1.X - v2.X, v1.Y - v2.Y); + + //////////////////////////////////////////////////////////// + /// + /// Operator + overload ; add two vectors + /// + /// First vector + /// Second vector + /// v1 + v2 + //////////////////////////////////////////////////////////// + public static Vector2f operator +(Vector2f v1, Vector2f v2) => new Vector2f(v1.X + v2.X, v1.Y + v2.Y); + + //////////////////////////////////////////////////////////// + /// + /// Operator * overload ; multiply a vector by a scalar value + /// + /// Vector + /// Scalar value + /// v * x + //////////////////////////////////////////////////////////// + public static Vector2f operator *(Vector2f v, float x) => new Vector2f(v.X * x, v.Y * x); + + //////////////////////////////////////////////////////////// + /// + /// Operator * overload ; multiply a scalar value by a vector + /// + /// Scalar value + /// Vector + /// x * v + //////////////////////////////////////////////////////////// + public static Vector2f operator *(float x, Vector2f v) => new Vector2f(v.X * x, v.Y * x); + + //////////////////////////////////////////////////////////// + /// + /// Operator / overload ; divide a vector by a scalar value + /// + /// Vector + /// Scalar value + /// v / x + //////////////////////////////////////////////////////////// + public static Vector2f operator /(Vector2f v, float x) => new Vector2f(v.X / x, v.Y / x); + + //////////////////////////////////////////////////////////// + /// + /// Operator == overload ; check vector equality + /// + /// First vector + /// Second vector + /// v1 == v2 + //////////////////////////////////////////////////////////// + public static bool operator ==(Vector2f v1, Vector2f v2) => v1.Equals(v2); + + //////////////////////////////////////////////////////////// + /// + /// Operator != overload ; check vector inequality + /// + /// First vector + /// Second vector + /// v1 != v2 + //////////////////////////////////////////////////////////// + public static bool operator !=(Vector2f v1, Vector2f v2) => !v1.Equals(v2); + + //////////////////////////////////////////////////////////// + /// + /// Provide a string describing the object + /// + /// String description of the object + //////////////////////////////////////////////////////////// + public override string ToString() => $"[Vector2f] X({X}) Y({Y})"; + + //////////////////////////////////////////////////////////// + /// + /// Compare vector and object and checks if they are equal + /// + /// Object to check + /// Object and vector are equal + //////////////////////////////////////////////////////////// + public override bool Equals(object obj) => (obj is Vector2f) && Equals((Vector2f)obj); + + /////////////////////////////////////////////////////////// + /// + /// Compare two vectors and checks if they are equal + /// + /// Vector to check + /// Vectors are equal + //////////////////////////////////////////////////////////// + public bool Equals(Vector2f other) => (X == other.X) && (Y == other.Y); + + //////////////////////////////////////////////////////////// + /// + /// Provide a integer describing the object + /// + /// Integer description of the object + //////////////////////////////////////////////////////////// + public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode(); + + //////////////////////////////////////////////////////////// + /// + /// Explicit casting to another vector type + /// + /// Vector being casted + /// Casting result + //////////////////////////////////////////////////////////// + public static explicit operator Vector2i(Vector2f v) => new Vector2i((int)v.X, (int)v.Y); + + //////////////////////////////////////////////////////////// + /// + /// Explicit casting to another vector type + /// + /// Vector being casted + /// Casting result + //////////////////////////////////////////////////////////// + public static explicit operator Vector2u(Vector2f v) => new Vector2u((uint)v.X, (uint)v.Y); + + /// X (horizontal) component of the vector + public float X; + + /// Y (vertical) component of the vector + public float Y; + } + + //////////////////////////////////////////////////////////// + /// + /// Vector2i is an utility class for manipulating 2 dimensional + /// vectors with integer components + /// + //////////////////////////////////////////////////////////// + [StructLayout(LayoutKind.Sequential)] + public struct Vector2i : IEquatable + { + //////////////////////////////////////////////////////////// + /// + /// Construct the vector from its coordinates + /// + /// X coordinate + /// Y coordinate + //////////////////////////////////////////////////////////// + public Vector2i(int x, int y) + { + X = x; + Y = y; + } + + //////////////////////////////////////////////////////////// + /// + /// Operator - overload ; returns the opposite of a vector + /// + /// Vector to negate + /// -v + //////////////////////////////////////////////////////////// + public static Vector2i operator -(Vector2i v) => new Vector2i(-v.X, -v.Y); + + //////////////////////////////////////////////////////////// + /// + /// Operator - overload ; subtracts two vectors + /// + /// First vector + /// Second vector + /// v1 - v2 + //////////////////////////////////////////////////////////// + public static Vector2i operator -(Vector2i v1, Vector2i v2) => new Vector2i(v1.X - v2.X, v1.Y - v2.Y); + + //////////////////////////////////////////////////////////// + /// + /// Operator + overload ; add two vectors + /// + /// First vector + /// Second vector + /// v1 + v2 + //////////////////////////////////////////////////////////// + public static Vector2i operator +(Vector2i v1, Vector2i v2) => new Vector2i(v1.X + v2.X, v1.Y + v2.Y); + + //////////////////////////////////////////////////////////// + /// + /// Operator * overload ; multiply a vector by a scalar value + /// + /// Vector + /// Scalar value + /// v * x + //////////////////////////////////////////////////////////// + public static Vector2i operator *(Vector2i v, int x) => new Vector2i(v.X * x, v.Y * x); + + //////////////////////////////////////////////////////////// + /// + /// Operator * overload ; multiply a scalar value by a vector + /// + /// Scalar value + /// Vector + /// x * v + //////////////////////////////////////////////////////////// + public static Vector2i operator *(int x, Vector2i v) => new Vector2i(v.X * x, v.Y * x); + + //////////////////////////////////////////////////////////// + /// + /// Operator / overload ; divide a vector by a scalar value + /// + /// Vector + /// Scalar value + /// v / x + //////////////////////////////////////////////////////////// + public static Vector2i operator /(Vector2i v, int x) => new Vector2i(v.X / x, v.Y / x); + + //////////////////////////////////////////////////////////// + /// + /// Operator == overload ; check vector equality + /// + /// First vector + /// Second vector + /// v1 == v2 + //////////////////////////////////////////////////////////// + public static bool operator ==(Vector2i v1, Vector2i v2) => v1.Equals(v2); + + //////////////////////////////////////////////////////////// + /// + /// Operator != overload ; check vector inequality + /// + /// First vector + /// Second vector + /// v1 != v2 + //////////////////////////////////////////////////////////// + public static bool operator !=(Vector2i v1, Vector2i v2) => !v1.Equals(v2); + + //////////////////////////////////////////////////////////// + /// + /// Provide a string describing the object + /// + /// String description of the object + //////////////////////////////////////////////////////////// + public override string ToString() => $"[Vector2i] X({X}) Y({Y})"; + + //////////////////////////////////////////////////////////// + /// + /// Compare vector and object and checks if they are equal + /// + /// Object to check + /// Object and vector are equal + //////////////////////////////////////////////////////////// + public override bool Equals(object obj) => (obj is Vector2i) && Equals((Vector2i)obj); + + /////////////////////////////////////////////////////////// + /// + /// Compare two vectors and checks if they are equal + /// + /// Vector to check + /// Vectors are equal + //////////////////////////////////////////////////////////// + public bool Equals(Vector2i other) => (X == other.X) && (Y == other.Y); + + //////////////////////////////////////////////////////////// + /// + /// Provide a integer describing the object + /// + /// Integer description of the object + //////////////////////////////////////////////////////////// + public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode(); + + //////////////////////////////////////////////////////////// + /// + /// Explicit casting to another vector type + /// + /// Vector being casted + /// Casting result + //////////////////////////////////////////////////////////// + public static explicit operator Vector2f(Vector2i v) => new Vector2f(v.X, v.Y); + + //////////////////////////////////////////////////////////// + /// + /// Explicit casting to another vector type + /// + /// Vector being casted + /// Casting result + //////////////////////////////////////////////////////////// + public static explicit operator Vector2u(Vector2i v) => new Vector2u((uint)v.X, (uint)v.Y); + + /// X (horizontal) component of the vector + public int X; + + /// Y (vertical) component of the vector + public int Y; + } + + //////////////////////////////////////////////////////////// + /// + /// Vector2u is an utility class for manipulating 2 dimensional + /// vectors with unsigned integer components + /// + //////////////////////////////////////////////////////////// + [StructLayout(LayoutKind.Sequential)] + public struct Vector2u : IEquatable + { + //////////////////////////////////////////////////////////// + /// + /// Construct the vector from its coordinates + /// + /// X coordinate + /// Y coordinate + //////////////////////////////////////////////////////////// + public Vector2u(uint x, uint y) + { + X = x; + Y = y; + } + + //////////////////////////////////////////////////////////// + /// + /// Operator - overload ; subtracts two vectors + /// + /// First vector + /// Second vector + /// v1 - v2 + //////////////////////////////////////////////////////////// + public static Vector2u operator -(Vector2u v1, Vector2u v2) => new Vector2u(v1.X - v2.X, v1.Y - v2.Y); + + //////////////////////////////////////////////////////////// + /// + /// Operator + overload ; add two vectors + /// + /// First vector + /// Second vector + /// v1 + v2 + //////////////////////////////////////////////////////////// + public static Vector2u operator +(Vector2u v1, Vector2u v2) => new Vector2u(v1.X + v2.X, v1.Y + v2.Y); + + //////////////////////////////////////////////////////////// + /// + /// Operator * overload ; multiply a vector by a scalar value + /// + /// Vector + /// Scalar value + /// v * x + //////////////////////////////////////////////////////////// + public static Vector2u operator *(Vector2u v, uint x) => new Vector2u(v.X * x, v.Y * x); + + //////////////////////////////////////////////////////////// + /// + /// Operator * overload ; multiply a scalar value by a vector + /// + /// Scalar value + /// Vector + /// x * v + //////////////////////////////////////////////////////////// + public static Vector2u operator *(uint x, Vector2u v) => new Vector2u(v.X * x, v.Y * x); + + //////////////////////////////////////////////////////////// + /// + /// Operator / overload ; divide a vector by a scalar value + /// + /// Vector + /// Scalar value + /// v / x + //////////////////////////////////////////////////////////// + public static Vector2u operator /(Vector2u v, uint x) => new Vector2u(v.X / x, v.Y / x); + + //////////////////////////////////////////////////////////// + /// + /// Operator == overload ; check vector equality + /// + /// First vector + /// Second vector + /// v1 == v2 + //////////////////////////////////////////////////////////// + public static bool operator ==(Vector2u v1, Vector2u v2) => v1.Equals(v2); + + //////////////////////////////////////////////////////////// + /// + /// Operator != overload ; check vector inequality + /// + /// First vector + /// Second vector + /// v1 != v2 + //////////////////////////////////////////////////////////// + public static bool operator !=(Vector2u v1, Vector2u v2) => !v1.Equals(v2); + + //////////////////////////////////////////////////////////// + /// + /// Provide a string describing the object + /// + /// String description of the object + //////////////////////////////////////////////////////////// + public override string ToString() => $"[Vector2u] X({X}) Y({Y})"; + + //////////////////////////////////////////////////////////// + /// + /// Compare vector and object and checks if they are equal + /// + /// Object to check + /// Object and vector are equal + //////////////////////////////////////////////////////////// + public override bool Equals(object obj) => (obj is Vector2u) && Equals((Vector2u)obj); + + /////////////////////////////////////////////////////////// + /// + /// Compare two vectors and checks if they are equal + /// + /// Vector to check + /// Vectors are equal + //////////////////////////////////////////////////////////// + public bool Equals(Vector2u other) => (X == other.X) && (Y == other.Y); + + //////////////////////////////////////////////////////////// + /// + /// Provide a integer describing the object + /// + /// Integer description of the object + //////////////////////////////////////////////////////////// + public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode(); + + //////////////////////////////////////////////////////////// + /// + /// Explicit casting to another vector type + /// + /// Vector being casted + /// Casting result + //////////////////////////////////////////////////////////// + public static explicit operator Vector2i(Vector2u v) => new Vector2i((int)v.X, (int)v.Y); + + //////////////////////////////////////////////////////////// + /// + /// Explicit casting to another vector type + /// + /// Vector being casted + /// Casting result + //////////////////////////////////////////////////////////// + public static explicit operator Vector2f(Vector2u v) => new Vector2f(v.X, v.Y); + + /// X (horizontal) component of the vector + public uint X; + + /// Y (vertical) component of the vector + public uint Y; + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/System/Vector3.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/System/Vector3.cs new file mode 100644 index 00000000..6e64408d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/System/Vector3.cs @@ -0,0 +1,152 @@ +using System; +using System.Runtime.InteropServices; + +namespace SFML.System +{ + //////////////////////////////////////////////////////////// + /// + /// Vector3f is an utility class for manipulating 3 dimensional + /// vectors with float components + /// + //////////////////////////////////////////////////////////// + [StructLayout(LayoutKind.Sequential)] + public struct Vector3f : IEquatable + { + //////////////////////////////////////////////////////////// + /// + /// Construct the vector from its coordinates + /// + /// X coordinate + /// Y coordinate + /// Z coordinate + //////////////////////////////////////////////////////////// + public Vector3f(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + + //////////////////////////////////////////////////////////// + /// + /// Operator - overload ; returns the opposite of a vector + /// + /// Vector to negate + /// -v + //////////////////////////////////////////////////////////// + public static Vector3f operator -(Vector3f v) => new Vector3f(-v.X, -v.Y, -v.Z); + + //////////////////////////////////////////////////////////// + /// + /// Operator - overload ; subtracts two vectors + /// + /// First vector + /// Second vector + /// v1 - v2 + //////////////////////////////////////////////////////////// + public static Vector3f operator -(Vector3f v1, Vector3f v2) => new Vector3f(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z); + + //////////////////////////////////////////////////////////// + /// + /// Operator + overload ; add two vectors + /// + /// First vector + /// Second vector + /// v1 + v2 + //////////////////////////////////////////////////////////// + public static Vector3f operator +(Vector3f v1, Vector3f v2) => new Vector3f(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z); + + //////////////////////////////////////////////////////////// + /// + /// Operator * overload ; multiply a vector by a scalar value + /// + /// Vector + /// Scalar value + /// v * x + //////////////////////////////////////////////////////////// + public static Vector3f operator *(Vector3f v, float x) => new Vector3f(v.X * x, v.Y * x, v.Z * x); + + //////////////////////////////////////////////////////////// + /// + /// Operator * overload ; multiply a scalar value by a vector + /// + /// Scalar value + /// Vector + /// x * v + //////////////////////////////////////////////////////////// + public static Vector3f operator *(float x, Vector3f v) => new Vector3f(v.X * x, v.Y * x, v.Z * x); + + //////////////////////////////////////////////////////////// + /// + /// Operator / overload ; divide a vector by a scalar value + /// + /// Vector + /// Scalar value + /// v / x + //////////////////////////////////////////////////////////// + public static Vector3f operator /(Vector3f v, float x) => new Vector3f(v.X / x, v.Y / x, v.Z / x); + + //////////////////////////////////////////////////////////// + /// + /// Operator == overload ; check vector equality + /// + /// First vector + /// Second vector + /// v1 == v2 + //////////////////////////////////////////////////////////// + public static bool operator ==(Vector3f v1, Vector3f v2) => v1.Equals(v2); + + //////////////////////////////////////////////////////////// + /// + /// Operator != overload ; check vector inequality + /// + /// First vector + /// Second vector + /// v1 != v2 + //////////////////////////////////////////////////////////// + public static bool operator !=(Vector3f v1, Vector3f v2) => !v1.Equals(v2); + + //////////////////////////////////////////////////////////// + /// + /// Provide a string describing the object + /// + /// String description of the object + //////////////////////////////////////////////////////////// + public override string ToString() => $"[Vector3f] X({X}) Y({Y}) Z({Z})"; + + //////////////////////////////////////////////////////////// + /// + /// Compare vector and object and checks if they are equal + /// + /// Object to check + /// Object and vector are equal + //////////////////////////////////////////////////////////// + public override bool Equals(object obj) => (obj is Vector3f) && Equals((Vector3f)obj); + + /////////////////////////////////////////////////////////// + /// + /// Compare two vectors and checks if they are equal + /// + /// Vector to check + /// Vectors are equal + //////////////////////////////////////////////////////////// + public bool Equals(Vector3f other) => (X == other.X) && (Y == other.Y) && (Z == other.Z); + + //////////////////////////////////////////////////////////// + /// + /// Provide a integer describing the object + /// + /// Integer description of the object + //////////////////////////////////////////////////////////// + public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); + + /// X (horizontal) component of the vector + public float X; + + /// Y (vertical) component of the vector + public float Y; + + /// Z (depth) component of the vector + public float Z; + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/Mouse.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/Mouse.cs new file mode 100644 index 00000000..9d66ea46 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/Mouse.cs @@ -0,0 +1,141 @@ +using System; +using System.Runtime.InteropServices; +using System.Security; +using SFML.System; + +namespace SFML.Window +{ + //////////////////////////////////////////////////////////// + /// + /// Give access to the real-time state of the mouse + /// + //////////////////////////////////////////////////////////// + public static class Mouse + { + //////////////////////////////////////////////////////////// + /// + /// Mouse buttons + /// + //////////////////////////////////////////////////////////// + public enum Button + { + /// The left mouse button + Left, + + /// The right mouse button + Right, + + /// The middle (wheel) mouse button + Middle, + + /// The first extra mouse button + XButton1, + + /// The second extra mouse button + XButton2, + + /// Keep last -- the total number of mouse buttons + ButtonCount + }; + + //////////////////////////////////////////////////////////// + /// + /// Mouse wheels + /// + //////////////////////////////////////////////////////////// + public enum Wheel + { + /// The vertical mouse wheel + VerticalWheel, + + /// The horizontal mouse wheel + HorizontalWheel + }; + + //////////////////////////////////////////////////////////// + /// + /// Check if a mouse button is pressed + /// + /// Button to check + /// True if the button is pressed, false otherwise + //////////////////////////////////////////////////////////// + public static bool IsButtonPressed(Button button) + { + //TODO: imlmement + return false; + //return sfMouse_isButtonPressed(button); + } + + //////////////////////////////////////////////////////////// + /// + /// Get the current position of the mouse + /// + /// This function returns the current position of the mouse + /// cursor in desktop coordinates. + /// Current position of the mouse + //////////////////////////////////////////////////////////// + public static Vector2i GetPosition() + { + return GetPosition(null); + } + + //////////////////////////////////////////////////////////// + /// + /// Get the current position of the mouse + /// + /// This function returns the current position of the mouse + /// cursor relative to a window. + /// Reference window + /// Current position of the mouse + //////////////////////////////////////////////////////////// + public static Vector2i GetPosition(Window relativeTo) + { + // TODO: implement + return new Vector2i(0, 0); + /* + if (relativeTo != null) + { + return relativeTo.InternalGetMousePosition(); + } + else + { + return sfMouse_getPosition(IntPtr.Zero); + }*/ + } + + //////////////////////////////////////////////////////////// + /// + /// Set the current position of the mouse + /// + /// This function sets the current position of the mouse + /// cursor in desktop coordinates. + /// New position of the mouse + //////////////////////////////////////////////////////////// + public static void SetPosition(Vector2i position) + { + SetPosition(position, null); + } + + //////////////////////////////////////////////////////////// + /// + /// Set the current position of the mouse + /// + /// This function sets the current position of the mouse + /// cursor relative to a window. + /// New position of the mouse + /// Reference window + //////////////////////////////////////////////////////////// + public static void SetPosition(Vector2i position, Window relativeTo) + { + // TODO: implement + /*if (relativeTo != null) + { + relativeTo.InternalSetMousePosition(position); + } + else + { + sfMouse_setPosition(position, IntPtr.Zero); + }*/ + } + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/Styles.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/Styles.cs new file mode 100644 index 00000000..af405888 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/Styles.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SFML.Window +{ + + public enum Styles + { + Close, + Titlebar, + Fullscreen + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/VideoMode.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/VideoMode.cs new file mode 100644 index 00000000..f070bd50 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/VideoMode.cs @@ -0,0 +1,33 @@ +using System; +using System.Numerics; +using System.Threading.Tasks; +using ManagedDoom; +using SFML.System; + +namespace SFML.Window +{ + + public class VideoMode + { + static public VideoMode CanvasMode { get; internal set; } + + static VideoMode() + { + // TODO: no const size + CanvasMode = new VideoMode(320, 200); + } + + public VideoMode(uint width, uint height) + { + this.Width = width; + this.Height = height; + } + + public uint Width { get; } + public uint Height { get; } + + public void Draw(uint pixel, Vector2f position) { } + + public void Draw(Graphics.Sprite sfmlSprite) { } + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/Window.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/Window.cs new file mode 100644 index 00000000..85cbcf1b --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SFML/Window/Window.cs @@ -0,0 +1,14 @@ +using System; +using SFML.System; + +namespace SFML.Window +{ + public class Window + { + internal void InternalSetMousePosition(Vector2i position) + { + throw new NotImplementedException(); + } + } + +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/AutoMapRenderer.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/AutoMapRenderer.cs new file mode 100644 index 00000000..6a6b2937 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/AutoMapRenderer.cs @@ -0,0 +1,334 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.SoftwareRendering +{ + public sealed class AutoMapRenderer + { + private static readonly float pr = 8 * DoomInfo.MobjInfos[(int)MobjType.Player].Radius.ToFloat() / 7; + + // The vector graphics for the automap. + // A line drawing of the player pointing right, starting from the middle. + private static readonly float[] playerArrow = new float[] + { + -pr + pr / 8, 0, pr, 0, // ----- + pr, 0, pr - pr / 2, pr / 4, // -----> + pr, 0, pr - pr / 2, -pr / 4, + -pr + pr / 8, 0, -pr - pr / 8, pr / 4, // >----> + -pr + pr / 8, 0, -pr - pr / 8, -pr / 4, + -pr + 3 * pr / 8, 0, -pr + pr / 8, pr / 4, // >>---> + -pr + 3 * pr / 8, 0, -pr + pr / 8, -pr / 4 + }; + + private static readonly float tr = 16; + + private static readonly float[] thingTriangle = new float[] + { + -0.5F * tr, -0.7F * tr, tr, 0F, + tr, 0F, -0.5F * tr, 0.7F * tr, + -0.5F * tr, 0.7F * tr, -0.5F * tr, -0.7F * tr + }; + + // For use if I do walls with outsides / insides. + private static readonly int reds = (256 - 5 * 16); + private static readonly int redRange = 16; + private static readonly int greens = (7 * 16); + private static readonly int greenRange = 16; + private static readonly int grays = (6 * 16); + private static readonly int grayRange = 16; + private static readonly int browns = (4 * 16); + private static readonly int brownRange = 16; + private static readonly int yellows = (256 - 32 + 7); + private static readonly int yellowRange = 1; + private static readonly int black = 0; + private static readonly int white = (256 - 47); + + // Automap colors. + private static readonly int background = black; + private static readonly int wallColors = reds; + private static readonly int wallRange = redRange; + private static readonly int tsWallColors = grays; + private static readonly int tsWallRange = grayRange; + private static readonly int fdWallColors = browns; + private static readonly int fdWallRange = brownRange; + private static readonly int cdWallColors = yellows; + private static readonly int cdWallRange = yellowRange; + private static readonly int thingColors = greens; + private static readonly int thingRange = greenRange; + private static readonly int secretWallColors = wallColors; + private static readonly int secretWallRange = wallRange; + + private static readonly int[] playerColors = new int[] + { + greens, + grays, + browns, + reds + }; + + private DrawScreen screen; + + private int scale; + private int amWidth; + private int amHeight; + private float ppu; + + private float minX; + private float maxX; + private float width; + private float minY; + private float maxY; + private float height; + + private float viewX; + private float viewY; + private float zoom; + + private Patch[] markNumbers; + + public AutoMapRenderer(Wad wad, DrawScreen screen) + { + this.screen = screen; + + scale = screen.Width / 320; + amWidth = screen.Width; + amHeight = screen.Height - scale * StatusBarRenderer.Height; + ppu = (float)scale / 16; + + markNumbers = new Patch[10]; + for (var i = 0; i < markNumbers.Length; i++) + { + markNumbers[i] = Patch.FromWad(wad, "AMMNUM" + i); + } + } + + public void Render(Player player) + { + screen.FillRect(0, 0, amWidth, amHeight, background); + + var world = player.Mobj.World; + var am = world.AutoMap; + + minX = am.MinX.ToFloat(); + maxX = am.MaxX.ToFloat(); + width = maxX - minX; + minY = am.MinY.ToFloat(); + maxY = am.MaxY.ToFloat(); + height = maxY - minY; + + viewX = am.ViewX.ToFloat(); + viewY = am.ViewY.ToFloat(); + zoom = am.Zoom.ToFloat(); + + foreach (var line in world.Map.Lines) + { + var v1 = ToScreenPos(line.Vertex1); + var v2 = ToScreenPos(line.Vertex2); + + var cheating = am.State != AutoMapState.None; + + if (cheating || (line.Flags & LineFlags.Mapped) != 0) + { + if ((line.Flags & LineFlags.DontDraw) != 0 && !cheating) + { + continue; + } + + if (line.BackSector == null) + { + screen.DrawLine(v1.X, v1.Y, v2.X, v2.Y, wallColors); + } + else + { + if (line.Special == (LineSpecial)39) + { + // Teleporters. + screen.DrawLine(v1.X, v1.Y, v2.X, v2.Y, wallColors + wallRange / 2); + } + else if ((line.Flags & LineFlags.Secret) != 0) + { + // Secret door. + if (cheating) + { + screen.DrawLine(v1.X, v1.Y, v2.X, v2.Y, secretWallColors); + } + else + { + screen.DrawLine(v1.X, v1.Y, v2.X, v2.Y, wallColors); + } + } + else if (line.BackSector.FloorHeight != line.FrontSector.FloorHeight) + { + // Floor level change. + screen.DrawLine(v1.X, v1.Y, v2.X, v2.Y, fdWallColors); + } + else if (line.BackSector.CeilingHeight != line.FrontSector.CeilingHeight) + { + // Ceiling level change. + screen.DrawLine(v1.X, v1.Y, v2.X, v2.Y, cdWallColors); + } + else if (cheating) + { + screen.DrawLine(v1.X, v1.Y, v2.X, v2.Y, tsWallColors); + } + } + } + else if (player.Powers[(int)PowerType.AllMap] > 0) + { + if ((line.Flags & LineFlags.DontDraw) == 0) + { + screen.DrawLine(v1.X, v1.Y, v2.X, v2.Y, grays + 3); + } + } + } + + for (var i = 0; i < am.Marks.Count; i++) + { + var pos = ToScreenPos(am.Marks[i]); + screen.DrawPatch( + markNumbers[i], + (int)MathF.Round(pos.X), + (int)MathF.Round(pos.Y), + scale); + } + + if (am.State == AutoMapState.AllThings) + { + DrawThings(world); + } + + DrawPlayers(world); + + if (!am.Follow) + { + screen.DrawLine( + amWidth / 2 - 2 * scale, amHeight / 2, + amWidth / 2 + 2 * scale, amHeight / 2, + grays); + + screen.DrawLine( + amWidth / 2, amHeight / 2 - 2 * scale, + amWidth / 2, amHeight / 2 + 2 * scale, + grays); + } + + screen.DrawText( + world.Map.Title, + 0, + amHeight - scale, + scale); + } + + private void DrawPlayers(World world) + { + var options = world.Options; + var players = options.Players; + var consolePlayer = world.ConsolePlayer; + var am = world.AutoMap; + + if (!options.NetGame) + { + DrawCharacter(consolePlayer.Mobj, playerArrow, white); + return; + } + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + var player = players[i]; + if (options.Deathmatch != 0 && !options.DemoPlayback && player != consolePlayer) + { + continue; + } + + if (!player.InGame) + { + continue; + } + + int color; + if (player.Powers[(int)PowerType.Invisibility] > 0) + { + // Close to black. + color = 246; + } + else + { + color = playerColors[i]; + } + + DrawCharacter(player.Mobj, playerArrow, color); + } + } + + private void DrawThings(World world) + { + foreach (var thinker in world.Thinkers) + { + var mobj = thinker as Mobj; + if (mobj != null) + { + DrawCharacter(mobj, thingTriangle, greens); + } + } + } + + private void DrawCharacter(Mobj mobj, float[] data, int color) + { + var pos = ToScreenPos(mobj.X, mobj.Y); + var sin = (float)Math.Sin(mobj.Angle.ToRadian()); + var cos = (float)Math.Cos(mobj.Angle.ToRadian()); + for (var i = 0; i < data.Length; i += 4) + { + var x1 = pos.X + zoom * ppu * (cos * data[i + 0] - sin * data[i + 1]); + var y1 = pos.Y - zoom * ppu * (sin * data[i + 0] + cos * data[i + 1]); + var x2 = pos.X + zoom * ppu * (cos * data[i + 2] - sin * data[i + 3]); + var y2 = pos.Y - zoom * ppu * (sin * data[i + 2] + cos * data[i + 3]); + screen.DrawLine(x1, y1, x2, y2, color); + } + } + + private DrawPos ToScreenPos(Fixed x, Fixed y) + { + var posX = zoom * ppu * (x.ToFloat() - viewX) + amWidth / 2; + var posY = -zoom * ppu * (y.ToFloat() - viewY) + amHeight / 2; + return new DrawPos(posX, posY); + } + + private DrawPos ToScreenPos(Vertex v) + { + var posX = zoom * ppu * (v.X.ToFloat() - viewX) + amWidth / 2; + var posY = -zoom * ppu * (v.Y.ToFloat() - viewY) + amHeight / 2; + return new DrawPos(posX, posY); + } + + + + private struct DrawPos + { + public float X; + public float Y; + + public DrawPos(float x, float y) + { + X = x; + Y = y; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/DrawScreen.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/DrawScreen.cs new file mode 100644 index 00000000..e6a7fd8c --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/DrawScreen.cs @@ -0,0 +1,544 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using Aura_OS.System.Graphics.UI.GUI.Components; +using Cosmos.System.Graphics; +using System; +using System.Collections.Generic; + +namespace ManagedDoom.SoftwareRendering +{ + public sealed class DrawScreen + { + private int width; + private int height; + private byte[] data; + + public DirectBitmap bitmap; + + private Patch[] chars; + + public DrawScreen(Wad wad, int width, int height) + { + this.width = width; + this.height = height; + data = new byte[width * height]; + bitmap = new DirectBitmap(width, height); + + chars = new Patch[128]; + for (var i = 0; i < chars.Length; i++) + { + var name = "STCFN" + i.ToString("000"); + var lump = wad.GetLumpNumber(name); + if (lump != -1) + { + chars[i] = Patch.FromData(name, wad.ReadLump(lump)); + } + } + } + + public void DrawPatch(Patch patch, int x, int y, int scale) + { + //var watch = System.Diagnostics.Stopwatch.StartNew(); + var drawX = x - scale * patch.LeftOffset; + var drawY = y - scale * patch.TopOffset; + var drawWidth = scale * patch.Width; + + var i = 0; + var frac = Fixed.One / scale - Fixed.Epsilon; + var step = Fixed.One / scale; + + if (drawX < 0) + { + var exceed = -drawX; + frac += exceed * step; + i += exceed; + } + + if (drawX + drawWidth > width) + { + var exceed = drawX + drawWidth - width; + drawWidth -= exceed; + } + + for (; i < drawWidth; i++) + { + DrawColumn(patch.Columns[frac.ToIntFloor()], drawX + i, drawY, scale); + frac += step; + } + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine($"DrawScreen.DrawPatch ({patch}, {x}, {y}, {scale}): {watch.ElapsedMilliseconds} ms"); + } + + public void DrawPatchFlip(Patch patch, int x, int y, int scale) + { + var drawX = x - scale * patch.LeftOffset; + var drawY = y - scale * patch.TopOffset; + var drawWidth = scale * patch.Width; + + var i = 0; + var frac = Fixed.One / scale - Fixed.Epsilon; + var step = Fixed.One / scale; + + if (drawX < 0) + { + var exceed = -drawX; + frac += exceed * step; + i += exceed; + } + + if (drawX + drawWidth > width) + { + var exceed = drawX + drawWidth - width; + drawWidth -= exceed; + } + + for (; i < drawWidth; i++) + { + var col = patch.Width - frac.ToIntFloor() - 1; + DrawColumn(patch.Columns[col], drawX + i, drawY, scale); + frac += step; + } + } + + private void DrawColumn(Column[] source, int x, int y, int scale) + { + // var watch = System.Diagnostics.Stopwatch.StartNew(); + var step = Fixed.One / scale; + + foreach (var column in source) + { + + var exTopDelta = scale * column.TopDelta; + var exLength = scale * column.Length; + + var sourceIndex = column.Offset; + var drawY = y + exTopDelta; + var drawLength = exLength; + + var i = 0; + var p = height * x + drawY; + var frac = Fixed.One / scale - Fixed.Epsilon; + + if (drawY < 0) + { + var exceed = -drawY; + p += exceed; + frac += exceed * step; + i += exceed; + } + + if (drawY + drawLength > height) + { + var exceed = drawY + drawLength - height; + drawLength -= exceed; + } + + for (; i < drawLength; i++) + { + data[p] = column.Data[sourceIndex + frac.ToIntFloor()]; + p++; + frac += step; + } + } + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine($"DrawColumns {source.Length}, x: {x}, y: {y} {watch.ElapsedMilliseconds}"); + } + + public void DrawText(IReadOnlyList text, int x, int y, int scale) + { + var drawX = x; + var drawY = y - 7 * scale; + foreach (var ch in text) + { + if (ch >= chars.Length) + { + continue; + } + + if (ch == 32) + { + drawX += 4 * scale; + continue; + } + + var index = (int)ch; + if ('a' <= index && index <= 'z') + { + index = index - 'a' + 'A'; + } + + var patch = chars[index]; + if (patch == null) + { + continue; + } + + DrawPatch(patch, drawX, drawY, scale); + + drawX += scale * patch.Width; + } + } + + public void DrawChar(char ch, int x, int y, int scale) + { + var drawX = x; + var drawY = y - 7 * scale; + + if (ch >= chars.Length) + { + return; + } + + if (ch == 32) + { + return; + } + + var index = (int)ch; + if ('a' <= index && index <= 'z') + { + index = index - 'a' + 'A'; + } + + var patch = chars[index]; + if (patch == null) + { + return; + } + + DrawPatch(patch, drawX, drawY, scale); + } + + public void DrawText(string text, int x, int y, int scale) + { + var drawX = x; + var drawY = y - 7 * scale; + foreach (var ch in text) + { + if (ch >= chars.Length) + { + continue; + } + + if (ch == 32) + { + drawX += 4 * scale; + continue; + } + + var index = (int)ch; + if ('a' <= index && index <= 'z') + { + index = index - 'a' + 'A'; + } + + var patch = chars[index]; + if (patch == null) + { + continue; + } + + DrawPatch(patch, drawX, drawY, scale); + + drawX += scale * patch.Width; + } + } + + public int MeasureChar(char ch, int scale) + { + if (ch >= chars.Length) + { + return 0; + } + + if (ch == 32) + { + return 4 * scale; + } + + var index = (int)ch; + if ('a' <= index && index <= 'z') + { + index = index - 'a' + 'A'; + } + + var patch = chars[index]; + if (patch == null) + { + return 0; + } + + return scale * patch.Width; + } + + public int MeasureText(IReadOnlyList text, int scale) + { + var width = 0; + + foreach (var ch in text) + { + if (ch >= chars.Length) + { + continue; + } + + if (ch == 32) + { + width += 4 * scale; + continue; + } + + var index = (int)ch; + if ('a' <= index && index <= 'z') + { + index = index - 'a' + 'A'; + } + + var patch = chars[index]; + if (patch == null) + { + continue; + } + + width += scale * patch.Width; + } + + return width; + } + + public int MeasureText(string text, int scale) + { + var width = 0; + + foreach (var ch in text) + { + if (ch >= chars.Length) + { + continue; + } + + if (ch == 32) + { + width += 4 * scale; + continue; + } + + var index = (int)ch; + if ('a' <= index && index <= 'z') + { + index = index - 'a' + 'A'; + } + + var patch = chars[index]; + if (patch == null) + { + continue; + } + + width += scale * patch.Width; + } + + return width; + } + + public void FillRect(int x, int y, int w, int h, int color) + { + var x1 = x; + var x2 = x + w; + for (var drawX = x1; drawX < x2; drawX++) + { + var pos = height * drawX + y; + for (var i = 0; i < h; i++) + { + data[pos] = (byte)color; + pos++; + } + } + } + + + + [Flags] + private enum OutCode + { + Inside = 0, + Left = 1, + Right = 2, + Bottom = 4, + Top = 8 + } + + private OutCode ComputeOutCode(float x, float y) + { + var code = OutCode.Inside; + + if (x < 0) + { + code |= OutCode.Left; + } + else if (x > width) + { + code |= OutCode.Right; + } + + if (y < 0) + { + code |= OutCode.Bottom; + } + else if (y > height) + { + code |= OutCode.Top; + } + + return code; + } + + public void DrawLine(float x1, float y1, float x2, float y2, int color) + { + var outCode1 = ComputeOutCode(x1, y1); + var outCode2 = ComputeOutCode(x2, y2); + + var accept = false; + + while (true) + { + if ((outCode1 | outCode2) == 0) + { + accept = true; + break; + } + else if ((outCode1 & outCode2) != 0) + { + break; + } + else + { + var x = 0.0F; + var y = 0.0F; + + var outcodeOut = outCode2 > outCode1 ? outCode2 : outCode1; + + if ((outcodeOut & OutCode.Top) != 0) + { + x = x1 + (x2 - x1) * (height - y1) / (y2 - y1); + y = height; + } + else if ((outcodeOut & OutCode.Bottom) != 0) + { + x = x1 + (x2 - x1) * (0 - y1) / (y2 - y1); + y = 0; + } + else if ((outcodeOut & OutCode.Right) != 0) + { + y = y1 + (y2 - y1) * (width - x1) / (x2 - x1); + x = width; + } + else if ((outcodeOut & OutCode.Left) != 0) + { + y = y1 + (y2 - y1) * (0 - x1) / (x2 - x1); + x = 0; + } + + if (outcodeOut == outCode1) + { + x1 = x; + y1 = y; + outCode1 = ComputeOutCode(x1, y1); + } + else + { + x2 = x; + y2 = y; + outCode2 = ComputeOutCode(x2, y2); + } + } + } + + if (accept) + { + var bx1 = Math.Clamp((int)x1, 0, width - 1); + var by1 = Math.Clamp((int)y1, 0, height - 1); + var bx2 = Math.Clamp((int)x2, 0, width - 1); + var by2 = Math.Clamp((int)y2, 0, height - 1); + Bresenham(bx1, by1, bx2, by2, color); + } + } + + private void Bresenham(int x1, int y1, int x2, int y2, int color) + { + var dx = x2 - x1; + var ax = 2 * (dx < 0 ? -dx : dx); + var sx = dx < 0 ? -1 : 1; + + var dy = y2 - y1; + var ay = 2 * (dy < 0 ? -dy : dy); + var sy = dy < 0 ? -1 : 1; + + var x = x1; + var y = y1; + + if (ax > ay) + { + var d = ay - ax / 2; + + while (true) + { + data[height * x + y] = (byte)color; + + if (x == x2) + { + return; + } + + if (d >= 0) + { + y += sy; + d -= ax; + } + + x += sx; + d += ay; + } + } + else + { + var d = ax - ay / 2; + while (true) + { + data[height * x + y] = (byte)color; + + if (y == y2) + { + return; + } + + if (d >= 0) + { + x += sx; + d -= ay; + } + + y += sy; + d += ax; + } + } + } + + public int Width => width; + public int Height => height; + public byte[] Data => data; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/FinaleRenderer.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/FinaleRenderer.cs new file mode 100644 index 00000000..ca23e63e --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/FinaleRenderer.cs @@ -0,0 +1,215 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.SoftwareRendering +{ + public sealed class FinaleRenderer + { + private Wad wad; + private FlatLookup flats; + private SpriteLookup sprites; + + private DrawScreen screen; + private int scale; + + private PatchCache cache; + + public FinaleRenderer(CommonResource resource, DrawScreen screen) + { + wad = resource.Wad; + flats = resource.Flats; + sprites = resource.Sprites; + + this.screen = screen; + scale = screen.Width / 320; + + cache = new PatchCache(wad); + } + + public void Render(Finale finale) + { + if (finale.Stage == 2) + { + RenderCast(finale); + return; + } + + if (finale.Stage == 0) + { + RenderTextScreen(finale); + } + else + { + switch (finale.Options.Episode) + { + case 1: + DrawPatch("CREDIT", 0, 0); + break; + + case 2: + DrawPatch("VICTORY2", 0, 0); + break; + + case 3: + BunnyScroll(finale); + break; + + case 4: + DrawPatch("ENDPIC", 0, 0); + break; + } + } + } + + private void RenderTextScreen(Finale finale) + { + FillFlat(flats[finale.Flat]); + + // Draw some of the text onto the screen. + var cx = 10 * scale; + var cy = 17 * scale; + var ch = 0; + + var count = (finale.Count - 10) / Finale.TextSpeed; + if (count < 0) + { + count = 0; + } + + for (; count > 0; count--) + { + if (ch == finale.Text.Length) + { + break; + } + + var c = finale.Text[ch++]; + + if (c == '\n') + { + cx = 10 * scale; + cy += 11 * scale; + continue; + } + + screen.DrawChar(c, cx, cy, scale); + + cx += screen.MeasureChar(c, scale); + } + } + + private void BunnyScroll(Finale finale) + { + var scroll = 320 - finale.Scrolled; + DrawPatch("PFUB2", scroll - 320, 0); + DrawPatch("PFUB1", scroll, 0); + + if (finale.ShowTheEnd) + { + string patch = "END0"; + switch (finale.TheEndIndex) + { + case 1: + patch = "END1"; + break; + case 2: + patch = "END2"; + break; + case 3: + patch = "END3"; + break; + case 4: + patch = "END4"; + break; + case 5: + patch = "END5"; + break; + case 6: + patch = "END6"; + break; + } + + DrawPatch( + patch, + (320 - 13 * 8) / 2, + (240 - 8 * 8) / 2); + } + } + + private void FillFlat(Flat flat) + { + var src = flat.Data; + var dst = screen.Data; + var scale = screen.Width / 320; + var xFrac = Fixed.One / scale - Fixed.Epsilon; + var step = Fixed.One / scale; + for (var x = 0; x < screen.Width; x++) + { + var yFrac = Fixed.One / scale - Fixed.Epsilon; + var p = screen.Height * x; + for (var y = 0; y < screen.Height; y++) + { + var spotX = xFrac.ToIntFloor() & 0x3F; + var spotY = yFrac.ToIntFloor() & 0x3F; + dst[p] = src[(spotY << 6) + spotX]; + yFrac += step; + p++; + } + xFrac += step; + } + } + + private void DrawPatch(string name, int x, int y) + { + var scale = screen.Width / 320; + screen.DrawPatch(cache[name], scale * x, scale * y, scale); + } + + private void RenderCast(Finale finale) + { + DrawPatch("BOSSBACK", 0, 0); + + var frame = finale.CastState.Frame & 0x7fff; + var patch = sprites[finale.CastState.Sprite].Frames[frame].Patches[0]; + if (sprites[finale.CastState.Sprite].Frames[frame].Flip[0]) + { + screen.DrawPatchFlip( + patch, + screen.Width / 2, + screen.Height - scale * 30, + scale); + } + else + { + screen.DrawPatch( + patch, + screen.Width / 2, + screen.Height - scale * 30, + scale); + } + + var width = screen.MeasureText(finale.CastName, scale); + screen.DrawText( + finale.CastName, + (screen.Width - width) / 2, + screen.Height - scale * 13, + scale); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/IRenderer.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/IRenderer.cs new file mode 100644 index 00000000..9e525309 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/IRenderer.cs @@ -0,0 +1,32 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.SoftwareRendering +{ + public interface IRenderer + { + public int MaxWindowSize { get; } + public int WindowSize { get; set; } + + public bool DisplayMessage { get; set; } + + public int MaxGammaCorrectionLevel { get; } + public int GammaCorrectionLevel { get; set; } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/IntermissionRenderer.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/IntermissionRenderer.cs new file mode 100644 index 00000000..6d338b4b --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/IntermissionRenderer.cs @@ -0,0 +1,719 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom.SoftwareRendering +{ + public sealed class IntermissionRenderer + { + // GLOBAL LOCATIONS + private static readonly int titleY = 2; + private static readonly int spacingY = 33; + + // SINGPLE-PLAYER STUFF + private static readonly int spStatsX = 50; + private static readonly int spStatsY = 50; + private static readonly int spTimeX = 16; + private static readonly int spTimeY = 200 - 32; + + // NET GAME STUFF + private static readonly int ngStatsY = 50; + private static readonly int ngSpacingX = 64; + + // DEATHMATCH STUFF + private static readonly int dmMatrixX = 42; + private static readonly int dmMatrixY = 68; + private static readonly int dmSpacingX = 40; + private static readonly int dmTotalsX = 269; + private static readonly int dmKillersX = 10; + private static readonly int dmKillersY = 100; + private static readonly int dmVictimsX = 5; + private static readonly int dmVictimsY = 50; + + private static readonly string[] mapPictures = new string[] + { + "WIMAP0", + "WIMAP1", + "WIMAP2" + }; + + private static readonly string[] playerBoxes = new string[] + { + "STPB0", + "STPB1", + "STPB2", + "STPB3" + }; + + private static readonly string[] youAreHere = new string[] + { + "WIURH0", + "WIURH1" + }; + + private static readonly string[][] doomLevels; + private static readonly string[] doom2Levels; + + static IntermissionRenderer() + { + doomLevels = new string[4][]; + for (var e = 0; e < 4; e++) + { + doomLevels[e] = new string[9]; + for (var m = 0; m < 9; m++) + { + doomLevels[e][m] = "WILV" + e + m; + } + } + + doom2Levels = new string[32]; + for (var m = 0; m < 32; m++) + { + doom2Levels[m] = "CWILV" + m.ToString("00"); + } + } + + + private Wad wad; + private DrawScreen screen; + + private PatchCache cache; + + private Patch minus; + private Patch[] numbers; + private Patch percent; + private Patch colon; + + private int scale; + + public IntermissionRenderer(Wad wad, DrawScreen screen) + { + this.wad = wad; + this.screen = screen; + + cache = new PatchCache(wad); + + minus = Patch.FromWad(wad, "WIMINUS"); + numbers = new Patch[10]; + for (var i = 0; i < 10; i++) + { + numbers[i] = Patch.FromWad(wad, "WINUM" + i); + } + percent = Patch.FromWad(wad, "WIPCNT"); + colon = Patch.FromWad(wad, "WICOLON"); + + scale = screen.Width / 320; + } + + + private void DrawPatch(Patch patch, int x, int y) + { + screen.DrawPatch(patch, scale * x, scale * y, scale); + } + + private void DrawPatch(string name, int x, int y) + { + var scale = screen.Width / 320; + screen.DrawPatch(cache[name], scale * x, scale * y, scale); + } + + private int GetWidth(string name) + { + return cache.GetWidth(name); + } + + private int GetHeight(string name) + { + return cache.GetHeight(name); + } + + + public void Render(Intermission im) + { + switch (im.State) + { + case IntermissionState.StatCount: + if (im.Options.Deathmatch != 0) + { + DrawDeathmatchStats(im); + } + else if (im.Options.NetGame) + { + DrawNetGameStats(im); + } + else + { + DrawSinglePlayerStats(im); + } + break; + + case IntermissionState.ShowNextLoc: + DrawShowNextLoc(im); + break; + + case IntermissionState.NoState: + DrawNoState(im); + break; + } + } + + + private void DrawBackground(Intermission im) + { + if (im.Options.GameMode == GameMode.Commercial) + { + DrawPatch("INTERPIC", 0, 0); + } + else + { + var e = im.Options.Episode - 1; + if (e < mapPictures.Length) + { + DrawPatch(mapPictures[e], 0, 0); + } + else + { + DrawPatch("INTERPIC", 0, 0); + } + } + } + + private void DrawSinglePlayerStats(Intermission im) + { + DrawBackground(im); + + // Draw animated background. + DrawBackgroundAnimation(im); + + // Draw level name. + DrawFinishedLevelName(im); + + // Line height. + var lineHeight = (3 * numbers[0].Height) / 2; + + DrawPatch( + "WIOSTK", // KILLS + spStatsX, + spStatsY); + + DrawPercent( + 320 - spStatsX, + spStatsY, + im.KillCount[0]); + + DrawPatch( + "WIOSTI", // ITEMS + spStatsX, + spStatsY + lineHeight); + + DrawPercent( + 320 - spStatsX, + spStatsY + lineHeight, + im.ItemCount[0]); + + DrawPatch( + "WISCRT2", // SECRET + spStatsX, + spStatsY + 2 * lineHeight); + + DrawPercent( + 320 - spStatsX, + spStatsY + 2 * lineHeight, + im.SecretCount[0]); + + DrawPatch( + "WITIME", // TIME + spTimeX, + spTimeY); + + DrawTime( + 320 / 2 - spTimeX, + spTimeY, + im.TimeCount); + + if (im.Info.Episode < 3) + { + + DrawPatch( + "WIPAR", // PAR + 320 / 2 + spTimeX, + spTimeY); + + DrawTime( + 320 - spTimeX, + spTimeY, + im.ParCount); + } + } + + private void DrawNetGameStats(Intermission im) + { + DrawBackground(im); + + // Draw animated background. + DrawBackgroundAnimation(im); + + // Draw level name. + DrawFinishedLevelName(im); + + var ngStatsX = 32 + GetWidth("STFST01") / 2; + if (!im.DoFrags) + { + ngStatsX += 32; + } + + // Draw stat titles (top line). + DrawPatch( + "WIOSTK", // KILLS + ngStatsX + ngSpacingX - GetWidth("WIOSTK"), + ngStatsY); + + DrawPatch( + "WIOSTI", // ITEMS + ngStatsX + 2 * ngSpacingX - GetWidth("WIOSTI"), + ngStatsY); + + DrawPatch( + "WIOSTS", // SCRT + ngStatsX + 3 * ngSpacingX - GetWidth("WIOSTS"), + ngStatsY); + + if (im.DoFrags) + { + DrawPatch( + "WIFRGS", // FRAGS + ngStatsX + 4 * ngSpacingX - GetWidth("WIFRGS"), + ngStatsY); + } + + // Draw stats. + var y = ngStatsY + GetHeight("WIOSTK"); + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (!im.Options.Players[i].InGame) + { + continue; + } + + var x = ngStatsX; + + DrawPatch( + playerBoxes[i], + x - GetWidth(playerBoxes[i]), + y); + + if (i == im.Options.ConsolePlayer) + { + DrawPatch( + "STFST01", // Player face + x - GetWidth(playerBoxes[i]), + y); + } + + x += ngSpacingX; + + DrawPercent(x - percent.Width, y + 10, im.KillCount[i]); + x += ngSpacingX; + + DrawPercent(x - percent.Width, y + 10, im.ItemCount[i]); + x += ngSpacingX; + + DrawPercent(x - percent.Width, y + 10, im.SecretCount[i]); + x += ngSpacingX; + + if (im.DoFrags) + { + DrawNumber(x, y + 10, im.FragCount[i], -1); + } + + y += spacingY; + } + } + + private void DrawDeathmatchStats(Intermission im) + { + DrawBackground(im); + + // Draw animated background. + DrawBackgroundAnimation(im); + + // Draw level name. + DrawFinishedLevelName(im); + + // Draw stat titles (top line). + DrawPatch( + "WIMSTT", // TOTAL + dmTotalsX - GetWidth("WIMSTT") / 2, + dmMatrixY - spacingY + 10); + + DrawPatch( + "WIKILRS", // KILLERS + dmKillersX, + dmKillersY); + + DrawPatch( + "WIVCTMS", // VICTIMS + dmVictimsX, + dmVictimsY); + + // Draw player boxes. + var x = dmMatrixX + dmSpacingX; + var y = dmMatrixY; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + if (im.Options.Players[i].InGame) + { + DrawPatch( + playerBoxes[i], + x - GetWidth(playerBoxes[i]) / 2, + dmMatrixY - spacingY); + + DrawPatch( + playerBoxes[i], + dmMatrixX - GetWidth(playerBoxes[i]) / 2, + y); + + if (i == im.Options.ConsolePlayer) + { + DrawPatch( + "STFDEAD0", // Player face (dead) + x - GetWidth(playerBoxes[i]) / 2, + dmMatrixY - spacingY); + + DrawPatch( + "STFST01", // Player face + dmMatrixX - GetWidth(playerBoxes[i]) / 2, + y); + } + } + else + { + // V_DrawPatch(x-SHORT(bp[i]->width)/2, + // DM_MATRIXY - WI_SPACINGY, FB, bp[i]); + // V_DrawPatch(DM_MATRIXX-SHORT(bp[i]->width)/2, + // y, FB, bp[i]); + } + + x += dmSpacingX; + y += spacingY; + } + + // Draw stats. + y = dmMatrixY + 10; + var w = numbers[0].Width; + + for (var i = 0; i < Player.MaxPlayerCount; i++) + { + x = dmMatrixX + dmSpacingX; + + if (im.Options.Players[i].InGame) + { + for (var j = 0; j < Player.MaxPlayerCount; j++) + { + if (im.Options.Players[j].InGame) + { + DrawNumber(x + w, y, im.DeathmatchFrags[i][j], 2); + } + + x += dmSpacingX; + } + + DrawNumber(dmTotalsX + w, y, im.DeathmatchTotals[i], 2); + } + + y += spacingY; + } + } + + + private void DrawNoState(Intermission im) + { + DrawShowNextLoc(im); + } + + private void DrawShowNextLoc(Intermission im) + { + DrawBackground(im); + + // Draw animated background. + DrawBackgroundAnimation(im); + + if (im.Options.GameMode != GameMode.Commercial) + { + if (im.Info.Episode > 2) + { + DrawEnteringLevelName(im); + return; + } + + var last = (im.Info.LastLevel == 8) ? im.Info.NextLevel - 1 : im.Info.LastLevel; + + // Draw a splat on taken cities. + for (var i = 0; i <= last; i++) + { + var x = WorldMap.Locations[im.Info.Episode][i].X; + var y = WorldMap.Locations[im.Info.Episode][i].Y; + DrawPatch("WISPLAT", x, y); + } + + // Splat the secret level? + if (im.Info.DidSecret) + { + var x = WorldMap.Locations[im.Info.Episode][8].X; + var y = WorldMap.Locations[im.Info.Episode][8].Y; + DrawPatch("WISPLAT", x, y); + } + + // Draw "you are here". + if (im.ShowYouAreHere) + { + var x = WorldMap.Locations[im.Info.Episode][im.Info.NextLevel].X; + var y = WorldMap.Locations[im.Info.Episode][im.Info.NextLevel].Y; + DrawSuitablePatch(youAreHere, x, y); + } + } + + // Draw next level name. + if ((im.Options.GameMode != GameMode.Commercial) || im.Info.NextLevel != 30) + { + DrawEnteringLevelName(im); + } + } + + private void DrawFinishedLevelName(Intermission intermission) + { + var wbs = intermission.Info; + var y = titleY; + + string levelName; + if (intermission.Options.GameMode != GameMode.Commercial) + { + var e = intermission.Options.Episode - 1; + levelName = doomLevels[e][wbs.LastLevel]; + } + else + { + levelName = doom2Levels[wbs.LastLevel]; + } + + // Draw level name. + DrawPatch( + levelName, + (320 - GetWidth(levelName)) / 2, + y); + + // Draw "Finished!". + y += (5 * GetHeight(levelName)) / 4; + + DrawPatch( + "WIF", + (320 - GetWidth("WIF")) / 2, + y); + } + + private void DrawEnteringLevelName(Intermission im) + { + var wbs = im.Info; + int y = titleY; + + string levelName; + if (im.Options.GameMode != GameMode.Commercial) + { + var e = im.Options.Episode - 1; + levelName = doomLevels[e][wbs.NextLevel]; + } + else + { + levelName = doom2Levels[wbs.NextLevel]; + } + + // Draw "Entering". + DrawPatch( + "WIENTER", + (320 - GetWidth("WIENTER")) / 2, + y); + + // Draw level name. + y += (5 * GetHeight(levelName)) / 4; + + DrawPatch( + levelName, + (320 - GetWidth(levelName)) / 2, + y); + } + + + private int DrawNumber(int x, int y, int n, int digits) + { + if (digits < 0) + { + if (n == 0) + { + // Make variable-length zeros 1 digit long. + digits = 1; + } + else + { + // Figure out number of digits. + digits = 0; + var temp = n; + while (temp != 0) + { + temp /= 10; + digits++; + } + } + } + + var neg = n < 0; + if (neg) + { + n = -n; + } + + // If non-number, do not draw it. + if (n == 1994) + { + return 0; + } + + var fontWidth = numbers[0].Width; + + // Draw the new number. + while (digits-- != 0) + { + x -= fontWidth; + DrawPatch(numbers[n % 10], x, y); + n /= 10; + } + + // Draw a minus sign if necessary. + if (neg) + { + DrawPatch(minus, x -= 8, y); + } + + return x; + } + + private void DrawPercent(int x, int y, int p) + { + if (p < 0) + { + return; + } + + DrawPatch(percent, x, y); + DrawNumber(x, y, p, -1); + } + + private void DrawTime(int x, int y, int t) + { + if (t < 0) + { + return; + } + + if (t <= 61 * 59) + { + var div = 1; + + do + { + var n = (t / div) % 60; + x = DrawNumber(x, y, n, 2) - colon.Width; + div *= 60; + + // Draw. + if (div == 60 || t / div != 0) + { + DrawPatch(colon, x, y); + } + } + while (t / div != 0); + } + else + { + DrawPatch( + "WISUCKS", // SUCKS + x - GetWidth("WISUCKS"), + y); + } + } + + private void DrawBackgroundAnimation(Intermission im) + { + if (im.Options.GameMode == GameMode.Commercial) + { + return; + } + + if (im.Info.Episode > 2) + { + return; + } + + for (var i = 0; i < im.Animations.Length; i++) + { + var a = im.Animations[i]; + if (a.PatchNumber >= 0) + { + DrawPatch(a.Patches[a.PatchNumber], a.LocationX, a.LocationY); + } + } + } + + private void DrawSuitablePatch(IReadOnlyList candidates, int x, int y) + { + var fits = false; + var i = 0; + + do + { + var patch = cache[candidates[i]]; + + var left = x - patch.LeftOffset; + var top = y - patch.TopOffset; + var right = left + patch.Width; + var bottom = top + patch.Height; + + if (left >= 0 && right < 320 && top >= 0 && bottom < 320) + { + fits = true; + } + else + { + i++; + } + } + while (!fits && i != 2); + + if (fits && i < 2) + { + DrawPatch(candidates[i], x, y); + } + else + { + throw new Exception("Could not place patch!"); + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/MenuRenderer.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/MenuRenderer.cs new file mode 100644 index 00000000..04a4c97d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/MenuRenderer.cs @@ -0,0 +1,279 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; + +namespace ManagedDoom.SoftwareRendering +{ + public sealed class MenuRenderer + { + private static readonly char[] cursor = { '_' }; + + private Wad wad; + private DrawScreen screen; + + private PatchCache cache; + + public MenuRenderer(Wad wad, DrawScreen screen) + { + this.wad = wad; + this.screen = screen; + + cache = new PatchCache(wad); + } + + public void Render(DoomMenu menu) + { + var selectable = menu.Current as SelectableMenu; + if (selectable != null) + { + DrawSelectableMenu(selectable); + } + + var save = menu.Current as SaveMenu; + if (save != null) + { + DrawSaveMenu(save); + } + + var load = menu.Current as LoadMenu; + if (load != null) + { + DrawLoadMenu(load); + } + + var yesNo = menu.Current as YesNoConfirm; + if (yesNo != null) + { + DrawText(yesNo.Text); + } + + var pressAnyKey = menu.Current as PressAnyKey; + if (pressAnyKey != null) + { + DrawText(pressAnyKey.Text); + } + + var quit = menu.Current as QuitConfirm; + if (quit != null) + { + DrawText(quit.Text); + } + + var help = menu.Current as HelpScreen; + if (help != null) + { + DrawHelp(help); + } + } + + private void DrawSelectableMenu(SelectableMenu selectable) + { + for (var i = 0; i < selectable.Name.Count; i++) + { + DrawMenuPatch( + selectable.Name[i], + selectable.TitleX[i], + selectable.TitleY[i]); + } + + foreach (var item in selectable.Items) + { + DrawMenuItem(selectable.Menu, item); + } + + var choice = selectable.Choice; + var skull = selectable.Menu.Tics / 8 % 2 == 0 ? "M_SKULL1" : "M_SKULL2"; + DrawMenuPatch(skull, choice.SkullX, choice.SkullY); + } + + private void DrawSaveMenu(SaveMenu save) + { + for (var i = 0; i < save.Name.Count; i++) + { + DrawMenuPatch( + save.Name[i], + save.TitleX[i], + save.TitleY[i]); + } + + foreach (var item in save.Items) + { + DrawMenuItem(save.Menu, item); + } + + var choice = save.Choice; + var skull = save.Menu.Tics / 8 % 2 == 0 ? "M_SKULL1" : "M_SKULL2"; + DrawMenuPatch(skull, choice.SkullX, choice.SkullY); + } + + private void DrawLoadMenu(LoadMenu load) + { + for (var i = 0; i < load.Name.Count; i++) + { + DrawMenuPatch( + load.Name[i], + load.TitleX[i], + load.TitleY[i]); + } + + foreach (var item in load.Items) + { + DrawMenuItem(load.Menu, item); + } + + var choice = load.Choice; + var skull = load.Menu.Tics / 8 % 2 == 0 ? "M_SKULL1" : "M_SKULL2"; + DrawMenuPatch(skull, choice.SkullX, choice.SkullY); + } + + private void DrawMenuItem(DoomMenu menu, MenuItem item) + { + var simple = item as SimpleMenuItem; + if (simple != null) + { + DrawSimpleMenuItem(simple); + } + + var toggle = item as ToggleMenuItem; + if (toggle != null) + { + DrawToggleMenuItem(toggle); + } + + var slider = item as SliderMenuItem; + if (slider != null) + { + DrawSliderMenuItem(slider); + } + + var textBox = item as TextBoxMenuItem; + if (textBox != null) + { + DrawTextBoxMenuItem(textBox, menu.Tics); + } + } + + private void DrawMenuPatch(string name, int x, int y) + { + var scale = screen.Width / 320; + screen.DrawPatch(cache[name], scale * x, scale * y, scale); + } + + private void DrawMenuText(IReadOnlyList text, int x, int y) + { + var scale = screen.Width / 320; + screen.DrawText(text, scale * x, scale * y, scale); + } + + private void DrawSimpleMenuItem(SimpleMenuItem item) + { + DrawMenuPatch(item.Name, item.ItemX, item.ItemY); + } + + private void DrawToggleMenuItem(ToggleMenuItem item) + { + DrawMenuPatch(item.Name, item.ItemX, item.ItemY); + DrawMenuPatch(item.State, item.StateX, item.ItemY); + } + + private void DrawSliderMenuItem(SliderMenuItem item) + { + DrawMenuPatch(item.Name, item.ItemX, item.ItemY); + + DrawMenuPatch("M_THERML", item.SliderX, item.SliderY); + for (var i = 0; i < item.SliderLength; i++) + { + var x = item.SliderX + 8 * (1 + i); + DrawMenuPatch("M_THERMM", x, item.SliderY); + } + + var end = item.SliderX + 8 * (1 + item.SliderLength); + DrawMenuPatch("M_THERMR", end, item.SliderY); + + var pos = item.SliderX + 8 * (1 + item.SliderPosition); + DrawMenuPatch("M_THERMO", pos, item.SliderY); + } + + private char[] emptyText = "EMPTY SLOT".ToCharArray(); + + private void DrawTextBoxMenuItem(TextBoxMenuItem item, int tics) + { + var length = 24; + DrawMenuPatch("M_LSLEFT", item.ItemX, item.ItemY); + for (var i = 0; i < length; i++) + { + var x = item.ItemX + 8 * (1 + i); + DrawMenuPatch("M_LSCNTR", x, item.ItemY); + } + DrawMenuPatch("M_LSRGHT", item.ItemX + 8 * (1 + length), item.ItemY); + + if (!item.Editing) + { + var text = item.Text != null ? item.Text : emptyText; + DrawMenuText(text, item.ItemX + 8, item.ItemY); + } + else + { + DrawMenuText(item.Text, item.ItemX + 8, item.ItemY); + if (tics / 3 % 2 == 0) + { + var textWidth = screen.MeasureText(item.Text, 1); + DrawMenuText(cursor, item.ItemX + 8 + textWidth, item.ItemY); + } + } + } + + private void DrawText(IReadOnlyList text) + { + var scale = screen.Width / 320; + var height = 7 * scale * text.Count; + + for (var i = 0; i < text.Count; i++) + { + var x = (screen.Width - screen.MeasureText(text[i], scale)) / 2; + var y = (screen.Height - height) / 2 + 7 * scale * (i + 1); + screen.DrawText(text[i], x, y, scale); + } + } + + private void DrawHelp(HelpScreen help) + { + var skull = help.Menu.Tics / 8 % 2 == 0 ? "M_SKULL1" : "M_SKULL2"; + + if (help.Menu.Options.GameMode == GameMode.Commercial) + { + DrawMenuPatch("HELP", 0, 0); + DrawMenuPatch(skull, 298, 160); + } + else + { + if (help.Page == 0) + { + DrawMenuPatch("HELP1", 0, 0); + DrawMenuPatch(skull, 298, 170); + } + else + { + DrawMenuPatch("HELP2", 0, 0); + DrawMenuPatch(skull, 248, 180); + } + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/OpeningSequenceRenderer.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/OpeningSequenceRenderer.cs new file mode 100644 index 00000000..604e8d29 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/OpeningSequenceRenderer.cs @@ -0,0 +1,57 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.SoftwareRendering +{ + public class OpeningSequenceRenderer + { + private DrawScreen screen; + private SfmlRenderer parent; + + private PatchCache cache; + + public OpeningSequenceRenderer(Wad wad, DrawScreen screen, SfmlRenderer parent) + { + this.screen = screen; + this.parent = parent; + + cache = new PatchCache(wad); + } + + public void Render(OpeningSequence sequence) + { + //var watch = System.Diagnostics.Stopwatch.StartNew(); + var scale = screen.Width / 320; + + switch (sequence.State) + { + case OpeningSequenceState.Title: + screen.DrawPatch(cache["TITLEPIC"], 0, 0, scale); + break; + case OpeningSequenceState.Demo: + parent.RenderGame(sequence.DemoGame); + break; + case OpeningSequenceState.Credit: + screen.DrawPatch(cache["CREDIT"], 0, 0, scale); + break; + } + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine($"OpeningSequence.Render {sequence.State} ms {watch.ElapsedMilliseconds}"); + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/SfmlRenderer.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/SfmlRenderer.cs new file mode 100644 index 00000000..eeb22781 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/SfmlRenderer.cs @@ -0,0 +1,507 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Aura_OS; +using Aura_OS.System.Graphics.UI.GUI.Components; +using Cosmos.System.Graphics; +using SFML.Graphics; +using SFML.System; + +namespace ManagedDoom.SoftwareRendering +{ + public sealed class SfmlRenderer : IRenderer, IDisposable + { + private static double[] gammaCorrectionParameters = new double[] + { + 1.00, + 0.95, + 0.90, + 0.85, + 0.80, + 0.75, + 0.70, + 0.65, + 0.60, + 0.55, + 0.50 + }; + + private Config config; + + private RenderWindow sfmlWindow; + private Palette palette; + + private int sfmlWindowWidth; + private int sfmlWindowHeight; + + private DrawScreen screen; + + private int sfmlTextureWidth; + private int sfmlTextureHeight; + + private byte[] sfmlTextureData; + private SFML.Graphics.Texture sfmlTexture; + private SFML.Graphics.Sprite sfmlSprite; + private SFML.Graphics.RenderStates sfmlStates; + + private MenuRenderer menu; + private ThreeDRenderer threeD; + private StatusBarRenderer statusBar; + private IntermissionRenderer intermission; + private OpeningSequenceRenderer openingSequence; + private AutoMapRenderer autoMap; + private FinaleRenderer finale; + + private Patch pause; + + private int wipeBandWidth; + private int wipeBandCount; + private int wipeHeight; + private byte[] wipeBuffer; + + public int X; + public int Y; + + public SfmlRenderer(Config config, RenderWindow window, CommonResource resource) + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Initialize renderer: "); + + this.config = config; + + config.video_gamescreensize = Math.Clamp(config.video_gamescreensize, 0, MaxWindowSize); + config.video_gammacorrection = Math.Clamp(config.video_gammacorrection, 0, MaxGammaCorrectionLevel); + + sfmlWindow = window; + palette = resource.Palette; + + sfmlWindowWidth = (int)window.Size.X; + sfmlWindowHeight = (int)window.Size.Y; + + if (config.video_highresolution) + { + screen = new DrawScreen(resource.Wad, 640, 400); + sfmlTextureWidth = 512; + sfmlTextureHeight = 1024; + } + else + { + screen = new DrawScreen(resource.Wad, 320, 200); + sfmlTextureWidth = 320; + sfmlTextureHeight = 200; + } + + sfmlTextureData = new byte[4 * screen.Width * screen.Height]; + + sfmlTexture = new SFML.Graphics.Texture((uint)sfmlTextureWidth, (uint)sfmlTextureHeight); + sfmlSprite = new SFML.Graphics.Sprite(sfmlTexture); + + sfmlSprite.Position = new Vector2f(0, 0); + sfmlSprite.Rotation = 90; + var scaleX = (float)sfmlWindowWidth / screen.Width; + var scaleY = (float)sfmlWindowHeight / screen.Height; + sfmlSprite.Scale = new Vector2f(scaleY, -scaleX); + + sfmlStates = new RenderStates(BlendMode.None); + + menu = new MenuRenderer(resource.Wad, screen); + threeD = new ThreeDRenderer(resource, screen, config.video_gamescreensize); + statusBar = new StatusBarRenderer(resource.Wad, screen); + intermission = new IntermissionRenderer(resource.Wad, screen); + openingSequence = new OpeningSequenceRenderer(resource.Wad, screen, this); + autoMap = new AutoMapRenderer(resource.Wad, screen); + finale = new FinaleRenderer(resource, screen); + + pause = Patch.FromWad(resource.Wad, "M_PAUSE"); + + var scale = screen.Width / 320; + wipeBandWidth = 2 * scale; + wipeBandCount = screen.Width / wipeBandWidth + 1; + wipeHeight = screen.Height / scale; + wipeBuffer = new byte[screen.Data.Length]; + + palette.ResetColors(gammaCorrectionParameters[config.video_gammacorrection]); + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + Dispose(); + ExceptionDispatchInfo.Throw(e); + } + } + + public void RenderApplication(DoomApplication app) + { + //var watch = System.Diagnostics.Stopwatch.StartNew(); + if (app.State == ApplicationState.Opening) + { + openingSequence.Render(app.Opening); + } + else if (app.State == ApplicationState.DemoPlayback) + { + RenderGame(app.DemoPlayback.Game); + } + else if (app.State == ApplicationState.Game) + { + RenderGame(app.Game); + } + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("RenderApplication 1st part: {0} s", (float)(watch.ElapsedMilliseconds) / 1000); + //watch.Restart(); + + if (!app.Menu.Active) + { + if (app.State == ApplicationState.Game && + app.Game.State == GameState.Level && + app.Game.Paused) + { + var scale = screen.Width / 320; + screen.DrawPatch( + pause, + (screen.Width - scale * pause.Width) / 2, + 4 * scale, + scale); + } + } + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("RenderApplication 2nd part: {0} s", (float)(watch.ElapsedMilliseconds) / 1000); + //watch.Restart(); + } + + public void RenderMenu(DoomApplication app) + { + if (app.Menu.Active) + { + menu.Render(app.Menu); + } + } + + public void RenderGame(DoomGame game) + { + // var watch = System.Diagnostics.Stopwatch.StartNew(); + if (game.State == GameState.Level) + { + var consolePlayer = game.World.ConsolePlayer; + var displayPlayer = game.World.DisplayPlayer; + + if (game.World.AutoMap.Visible) + { + autoMap.Render(consolePlayer); + statusBar.Render(consolePlayer, true); + } + else + { + // var watch2 = System.Diagnostics.Stopwatch.StartNew(); + threeD.Render(displayPlayer); + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("threeD.Render(displayPlayer) {0} ms", watch2.ElapsedMilliseconds); + // watch2.Restart(); + if (threeD.WindowSize < 8) + { + statusBar.Render(consolePlayer, true); + } + else if (threeD.WindowSize == ThreeDRenderer.MaxScreenSize) + { + statusBar.Render(consolePlayer, false); + } + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("statusBar.Render {0} ms", watch2.ElapsedMilliseconds); + // watch2.Restart(); + } + + if (config.video_displaymessage || ReferenceEquals(consolePlayer.Message, (string)DoomInfo.Strings.MSGOFF)) + { + if (consolePlayer.MessageTime > 0) + { + var scale = screen.Width / 320; + screen.DrawText(consolePlayer.Message, 0, 7 * scale, scale); + } + } + } + else if (game.State == GameState.Intermission) + { + intermission.Render(game.Intermission); + } + else if (game.State == GameState.Finale) + { + finale.Render(game.Finale); + } + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Render state {0}, {1} ms", game.State, watch.ElapsedMilliseconds); + } + + public void Render(DoomApplication app) + { + //var watch = System.Diagnostics.Stopwatch.StartNew(); + RenderApplication(app); + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("RenderApplication: {0} s", (float)(watch.ElapsedMilliseconds) / 1000); + //watch.Restart(); + RenderMenu(app); + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("RenderMenu: {0} s", (float)(watch.ElapsedMilliseconds) / 1000); + //watch.Restart(); + + var colors = palette[0]; + if (app.State == ApplicationState.Game && + app.Game.State == GameState.Level) + { + colors = palette[GetPaletteNumber(app.Game.World.ConsolePlayer)]; + } + else if (app.State == ApplicationState.Opening && + app.Opening.State == OpeningSequenceState.Demo && + app.Opening.DemoGame.State == GameState.Level) + { + colors = palette[GetPaletteNumber(app.Opening.DemoGame.World.ConsolePlayer)]; + } + else if (app.State == ApplicationState.DemoPlayback && + app.DemoPlayback.Game.State == GameState.Level) + { + colors = palette[GetPaletteNumber(app.DemoPlayback.Game.World.ConsolePlayer)]; + } + + Display(colors); + } + + public void RenderWipe(DoomApplication app, WipeEffect wipe) + { + RenderApplication(app); + + var scale = screen.Width / 320; + for (var i = 0; i < wipeBandCount - 1; i++) + { + var x1 = wipeBandWidth * i; + var x2 = x1 + wipeBandWidth; + var y1 = Math.Max(scale * wipe.Y[i], 0); + var y2 = Math.Max(scale * wipe.Y[i + 1], 0); + var dy = (float)(y2 - y1) / wipeBandWidth; + for (var x = x1; x < x2; x++) + { + var y = (int)MathF.Round(y1 + dy * ((x - x1) / 2 * 2)); + var copyLength = screen.Height - y; + if (copyLength > 0) + { + var srcPos = screen.Height * x; + var dstPos = screen.Height * x + y; + Array.Copy(wipeBuffer, srcPos, screen.Data, dstPos, copyLength); + } + } + } + + RenderMenu(app); + + Display(palette[0]); + } + + public void InitializeWipe() + { + Array.Copy(screen.Data, wipeBuffer, screen.Data.Length); + } + + void UpdteTextureDataWithColors(uint[] colors) + { + var screenData = screen.Data; + var p = MemoryMarshal.Cast(sfmlTextureData); + for (var i = 0; i < p.Length; i++) + { + p[i] = colors[screenData[i]]; + } + } + + private void Display(uint[] colors) + { + RenderWithColorsAndScreenDataUnmarshalled(screen.Data, colors); + + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("JS renderWithColorsAndScreenDataUnmarshalled: {0} s", watch.ElapsedMilliseconds); + // watch.Restart(); + + // DoomApplication.JSInProcessRuntime.InvokeVoid("renderWithColorsAndScreenData", args); + } + + int width = 320; + int height = 200; + public DirectBitmap bitmap = new DirectBitmap(320, 200); + + public void RenderWithColorsAndScreenDataUnmarshalled(byte[] screenData, uint[] colors) + { + int x = 0, y = height - 1; + + for (int i = 0; i < screenData.Length; i++) + { + if (screenData[i] < colors.Length) + { + SetSinglePixel(bitmap, x, y, colors, screenData[i]); + } + + y++; + if (y >= height) + { + y = 0; + x++; + } + } + + RenderBitmap(bitmap); + } + + private void SetSinglePixel(DirectBitmap bitmap, int x, int y, uint[] colors, uint colorIndex) + { + uint color = colors[colorIndex]; + + byte a = (byte)((color >> 24) & 0xff); // Alpha + byte b = (byte)((color >> 16) & 0xff); // Red + byte g = (byte)((color >> 8) & 0xff); // Green + byte r = (byte)(color & 0xff); // Blue + + bitmap.SetPixel(x, y, System.Drawing.Color.FromArgb(a, r, g, b).ToArgb()); + } + + + private void RenderBitmap(DirectBitmap bitmap) + { + Kernel.canvas.DrawImage(bitmap.Bitmap, X, Y); + } + + private static int GetPaletteNumber(Player player) + { + var count = player.DamageCount; + + if (player.Powers[(int)PowerType.Strength] != 0) + { + // Slowly fade the berzerk out. + var bzc = 12 - (player.Powers[(int)PowerType.Strength] >> 6); + if (bzc > count) + { + count = bzc; + } + } + + int palette; + + if (count != 0) + { + palette = (count + 7) >> 3; + + if (palette >= Palette.DamageCount) + { + palette = Palette.DamageCount - 1; + } + + palette += Palette.DamageStart; + } + else if (player.BonusCount != 0) + { + palette = (player.BonusCount + 7) >> 3; + + if (palette >= Palette.BonusCount) + { + palette = Palette.BonusCount - 1; + } + + palette += Palette.BonusStart; + } + else if (player.Powers[(int)PowerType.IronFeet] > 4 * 32 || + (player.Powers[(int)PowerType.IronFeet] & 8) != 0) + { + palette = Palette.IronFeet; + } + else + { + palette = 0; + } + + return palette; + } + + public void Dispose() + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Shutdown renderer."); + + if (sfmlSprite != null) + { + sfmlSprite.Dispose(); + sfmlSprite = null; + } + + if (sfmlTexture != null) + { + sfmlTexture.Dispose(); + sfmlTexture = null; + } + } + + public int WipeBandCount => wipeBandCount; + public int WipeHeight => wipeHeight; + + public int MaxWindowSize + { + get + { + return ThreeDRenderer.MaxScreenSize; + } + } + + public int WindowSize + { + get + { + return threeD.WindowSize; + } + + set + { + config.video_gamescreensize = value; + threeD.WindowSize = value; + } + } + + public bool DisplayMessage + { + get + { + return config.video_displaymessage; + } + + set + { + config.video_displaymessage = value; + } + } + + public int MaxGammaCorrectionLevel + { + get + { + return gammaCorrectionParameters.Length - 1; + } + } + + public int GammaCorrectionLevel + { + get + { + return config.video_gammacorrection; + } + + set + { + config.video_gammacorrection = value; + palette.ResetColors(gammaCorrectionParameters[config.video_gammacorrection]); + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/StatusBarRenderer.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/StatusBarRenderer.cs new file mode 100644 index 00000000..f3f16515 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/StatusBarRenderer.cs @@ -0,0 +1,479 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.SoftwareRendering +{ + public sealed class StatusBarRenderer + { + public static readonly int Height = 32; + + // Ammo number pos. + private static readonly int ammoWidth = 3; + private static readonly int ammoX = 44; + private static readonly int ammoY = 171; + + // Health number pos. + private static readonly int healthX = 90; + private static readonly int healthY = 171; + + // Weapon pos. + private static readonly int armsX = 111; + private static readonly int armsY = 172; + private static readonly int armsBackgroundX = 104; + private static readonly int armsBackgroundY = 168; + private static readonly int armsSpaceX = 12; + private static readonly int armsSpaceY = 10; + + // Frags pos. + private static readonly int fragsWidth = 2; + private static readonly int fragsX = 138; + private static readonly int fragsY = 171; + + // Armor number pos. + private static readonly int armorX = 221; + private static readonly int armorY = 171; + + // Key icon positions. + private static readonly int key0Width = 8; + private static readonly int key0X = 239; + private static readonly int key0Y = 171; + private static readonly int key1Width = key0Width; + private static readonly int key1X = 239; + private static readonly int key1Y = 181; + private static readonly int key2Width = key0Width; + private static readonly int key2X = 239; + private static readonly int key2Y = 191; + + // Ammunition counter. + private static readonly int ammo0Width = 3; + private static readonly int ammo0X = 288; + private static readonly int ammo0Y = 173; + private static readonly int ammo1Width = ammo0Width; + private static readonly int ammo1X = 288; + private static readonly int ammo1Y = 179; + private static readonly int ammo2Width = ammo0Width; + private static readonly int ammo2X = 288; + private static readonly int ammo2Y = 191; + private static readonly int ammo3Wdth = ammo0Width; + private static readonly int ammo3X = 288; + private static readonly int ammo3Y = 185; + + // Indicate maximum ammunition. + // Only needed because backpack exists. + private static readonly int maxAmmo0Width = 3; + private static readonly int maxAmmo0X = 314; + private static readonly int maxAmmo0Y = 173; + private static readonly int maxAmmo1Width = maxAmmo0Width; + private static readonly int maxAmmo1X = 314; + private static readonly int maxAmmo1Y = 179; + private static readonly int maxAmmo2Width = maxAmmo0Width; + private static readonly int maxAmmo2X = 314; + private static readonly int maxAmmo2Y = 191; + private static readonly int maxAmmo3Width = maxAmmo0Width; + private static readonly int maxAmmo3X = 314; + private static readonly int maxAmmo3Y = 185; + + private static readonly int faceX = 143; + private static readonly int faceY = 168; + private static readonly int faceBackgroundX = 143; + private static readonly int faceBackgroundY = 169; + + private DrawScreen screen; + + private Patches patches; + + private int scale; + + private NumberWidget ready; + private PercentWidget health; + private PercentWidget armor; + + private NumberWidget[] ammo; + private NumberWidget[] maxAmmo; + + private MultIconWidget[] weapons; + + private NumberWidget frags; + + private MultIconWidget[] keys; + + public StatusBarRenderer(Wad wad, DrawScreen screen) + { + this.screen = screen; + + patches = new Patches(wad); + + scale = screen.Width / 320; + + ready = new NumberWidget(); + ready.Patches = patches.TallNumbers; + ready.Width = ammoWidth; + ready.X = ammoX; + ready.Y = ammoY; + + health = new PercentWidget(); + health.NumberWidget.Patches = patches.TallNumbers; + health.NumberWidget.Width = 3; + health.NumberWidget.X = healthX; + health.NumberWidget.Y = healthY; + health.Patch = patches.TallPercent; + + armor = new PercentWidget(); + armor.NumberWidget.Patches = patches.TallNumbers; + armor.NumberWidget.Width = 3; + armor.NumberWidget.X = armorX; + armor.NumberWidget.Y = armorY; + armor.Patch = patches.TallPercent; + + ammo = new NumberWidget[(int)AmmoType.Count]; + ammo[0] = new NumberWidget(); + ammo[0].Patches = patches.ShortNumbers; + ammo[0].Width = ammo0Width; + ammo[0].X = ammo0X; + ammo[0].Y = ammo0Y; + ammo[1] = new NumberWidget(); + ammo[1].Patches = patches.ShortNumbers; + ammo[1].Width = ammo1Width; + ammo[1].X = ammo1X; + ammo[1].Y = ammo1Y; + ammo[2] = new NumberWidget(); + ammo[2].Patches = patches.ShortNumbers; + ammo[2].Width = ammo2Width; + ammo[2].X = ammo2X; + ammo[2].Y = ammo2Y; + ammo[3] = new NumberWidget(); + ammo[3].Patches = patches.ShortNumbers; + ammo[3].Width = ammo3Wdth; + ammo[3].X = ammo3X; + ammo[3].Y = ammo3Y; + + maxAmmo = new NumberWidget[(int)AmmoType.Count]; + maxAmmo[0] = new NumberWidget(); + maxAmmo[0].Patches = patches.ShortNumbers; + maxAmmo[0].Width = maxAmmo0Width; + maxAmmo[0].X = maxAmmo0X; + maxAmmo[0].Y = maxAmmo0Y; + maxAmmo[1] = new NumberWidget(); + maxAmmo[1].Patches = patches.ShortNumbers; + maxAmmo[1].Width = maxAmmo1Width; + maxAmmo[1].X = maxAmmo1X; + maxAmmo[1].Y = maxAmmo1Y; + maxAmmo[2] = new NumberWidget(); + maxAmmo[2].Patches = patches.ShortNumbers; + maxAmmo[2].Width = maxAmmo2Width; + maxAmmo[2].X = maxAmmo2X; + maxAmmo[2].Y = maxAmmo2Y; + maxAmmo[3] = new NumberWidget(); + maxAmmo[3].Patches = patches.ShortNumbers; + maxAmmo[3].Width = maxAmmo3Width; + maxAmmo[3].X = maxAmmo3X; + maxAmmo[3].Y = maxAmmo3Y; + + weapons = new MultIconWidget[6]; + for (var i = 0; i < weapons.Length; i++) + { + weapons[i] = new MultIconWidget(); + weapons[i].X = armsX + (i % 3) * armsSpaceX; + weapons[i].Y = armsY + (i / 3) * armsSpaceY; + weapons[i].Patches = patches.Arms[i]; + } + + frags = new NumberWidget(); + frags.Patches = patches.TallNumbers; + frags.Width = fragsWidth; + frags.X = fragsX; + frags.Y = fragsY; + + keys = new MultIconWidget[3]; + keys[0] = new MultIconWidget(); + keys[0].X = key0X; + keys[0].Y = key0Y; + keys[0].Patches = patches.Keys; + keys[1] = new MultIconWidget(); + keys[1].X = key1X; + keys[1].Y = key1Y; + keys[1].Patches = patches.Keys; + keys[2] = new MultIconWidget(); + keys[2].X = key2X; + keys[2].Y = key2Y; + keys[2].Patches = patches.Keys; + } + + public void Render(Player player, bool drawBackground) + { + if (drawBackground) + { + screen.DrawPatch( + patches.Background, + 0, + scale * (200 - Height), + scale); + } + + if (DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo != AmmoType.NoAmmo) + { + var num = player.Ammo[(int)DoomInfo.WeaponInfos[(int)player.ReadyWeapon].Ammo]; + DrawNumber(ready, num); + } + + DrawPercent(health, player.Health); + DrawPercent(armor, player.ArmorPoints); + + for (var i = 0; i < (int)AmmoType.Count; i++) + { + DrawNumber(ammo[i], player.Ammo[i]); + DrawNumber(maxAmmo[i], player.MaxAmmo[i]); + } + + if (player.Mobj.World.Options.Deathmatch == 0) + { + if (drawBackground) + { + screen.DrawPatch( + patches.ArmsBackground, + scale * armsBackgroundX, + scale * armsBackgroundY, + scale); + } + + for (var i = 0; i < weapons.Length; i++) + { + DrawMultIcon(weapons[i], player.WeaponOwned[i + 1] ? 1 : 0); + } + } + else + { + var sum = 0; + for (var i = 0; i < player.Frags.Length; i++) + { + sum += player.Frags[i]; + } + DrawNumber(frags, sum); + } + + if (drawBackground) + { + if (player.Mobj.World.Options.NetGame) + { + screen.DrawPatch( + patches.FaceBackground[player.Number], + scale * faceBackgroundX, + scale * faceBackgroundY, + scale); + } + + screen.DrawPatch( + patches.Faces[player.Mobj.World.StatusBar.FaceIndex], + scale * faceX, + scale * faceY, + scale); + } + + for (var i = 0; i < 3; i++) + { + if (player.Cards[i + 3]) + { + DrawMultIcon(keys[i], i + 3); + } + else if (player.Cards[i]) + { + DrawMultIcon(keys[i], i); + } + } + } + + private void DrawNumber(NumberWidget widget, int num) + { + var digits = widget.Width; + + var w = widget.Patches[0].Width; + var h = widget.Patches[0].Height; + var x = widget.X; + + var neg = num < 0; + + if (neg) + { + if (digits == 2 && num < -9) + { + num = -9; + } + else if (digits == 3 && num < -99) + { + num = -99; + } + + num = -num; + } + + x = widget.X - digits * w; + + if (num == 1994) + { + return; + } + + x = widget.X; + + // In the special case of 0, you draw 0. + if (num == 0) + { + screen.DrawPatch( + widget.Patches[0], + scale * (x - w), + scale * widget.Y, + scale); + } + + // Draw the new number. + while (num != 0 && digits-- != 0) + { + x -= w; + + screen.DrawPatch( + widget.Patches[num % 10], + scale * x, + scale * widget.Y, + scale); + + num /= 10; + } + + // Draw a minus sign if necessary. + if (neg) + { + screen.DrawPatch( + patches.TallMinus, + scale * (x - 8), + scale * widget.Y, + scale); + } + } + + private void DrawPercent(PercentWidget per, int value) + { + screen.DrawPatch( + per.Patch, + scale * per.NumberWidget.X, + scale * per.NumberWidget.Y, + scale); + + DrawNumber(per.NumberWidget, value); + } + + private void DrawMultIcon(MultIconWidget mi, int value) + { + screen.DrawPatch( + mi.Patches[value], + scale * mi.X, + scale * mi.Y, + scale); + } + + + + private class NumberWidget + { + public int X; + public int Y; + public int Width; + public Patch[] Patches; + } + + private class PercentWidget + { + public NumberWidget NumberWidget = new NumberWidget(); + public Patch Patch; + } + + private class MultIconWidget + { + public int X; + public int Y; + public Patch[] Patches; + } + + private class Patches + { + public Patch Background; + public Patch[] TallNumbers; + public Patch[] ShortNumbers; + public Patch TallMinus; + public Patch TallPercent; + public Patch[] Keys; + public Patch ArmsBackground; + public Patch[][] Arms; + public Patch[] FaceBackground; + public Patch[] Faces; + + public Patches(Wad wad) + { + Background = Patch.FromWad(wad, "STBAR"); + + TallNumbers = new Patch[10]; + ShortNumbers = new Patch[10]; + for (var i = 0; i < 10; i++) + { + TallNumbers[i] = Patch.FromWad(wad, "STTNUM" + i); + ShortNumbers[i] = Patch.FromWad(wad, "STYSNUM" + i); + } + TallMinus = Patch.FromWad(wad, "STTMINUS"); + TallPercent = Patch.FromWad(wad, "STTPRCNT"); + + Keys = new Patch[(int)CardType.Count]; + for (var i = 0; i < Keys.Length; i++) + { + Keys[i] = Patch.FromWad(wad, "STKEYS" + i); + } + + ArmsBackground = Patch.FromWad(wad, "STARMS"); + Arms = new Patch[6][]; + for (var i = 0; i < 6; i++) + { + var num = i + 2; + Arms[i] = new Patch[2]; + Arms[i][0] = Patch.FromWad(wad, "STGNUM" + num); + Arms[i][1] = ShortNumbers[num]; + } + + FaceBackground = new Patch[Player.MaxPlayerCount]; + for (var i = 0; i < FaceBackground.Length; i++) + { + FaceBackground[i] = Patch.FromWad(wad, "STFB" + i); + } + Faces = new Patch[StatusBar.Face.FaceCount]; + var faceCount = 0; + for (var i = 0; i < StatusBar.Face.PainFaceCount; i++) + { + for (var j = 0; j < StatusBar.Face.StraightFaceCount; j++) + { + Faces[faceCount++] = Patch.FromWad(wad, "STFST" + i + j); + } + Faces[faceCount++] = Patch.FromWad(wad, "STFTR" + i + "0"); + Faces[faceCount++] = Patch.FromWad(wad, "STFTL" + i + "0"); + Faces[faceCount++] = Patch.FromWad(wad, "STFOUCH" + i); + Faces[faceCount++] = Patch.FromWad(wad, "STFEVL" + i); + Faces[faceCount++] = Patch.FromWad(wad, "STFKILL" + i); + } + Faces[faceCount++] = Patch.FromWad(wad, "STFGOD0"); + Faces[faceCount++] = Patch.FromWad(wad, "STFDEAD0"); + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/ThreeDRenderer.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/ThreeDRenderer.cs new file mode 100644 index 00000000..01a673d2 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/ThreeDRenderer.cs @@ -0,0 +1,3079 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.SoftwareRendering +{ + public sealed class ThreeDRenderer + { + public static readonly int MaxScreenSize = 9; + + private ColorMap colorMap; + private TextureLookup textures; + private FlatLookup flats; + private SpriteLookup sprites; + + private DrawScreen screen; + private int screenWidth; + private int screenHeight; + private byte[] screenData; + private int drawScale; + + private int windowSize; + + public ThreeDRenderer(CommonResource resource, DrawScreen screen, int windowSize) + { + colorMap = resource.ColorMap; + textures = resource.Textures; + flats = resource.Flats; + sprites = resource.Sprites; + + this.screen = screen; + screenWidth = screen.Width; + screenHeight = screen.Height; + screenData = screen.Data; + drawScale = screenWidth / 320; + + this.windowSize = windowSize; + + InitWallRendering(); + InitPlaneRendering(); + InitSkyRendering(); + InitLighting(); + InitRenderingHistory(); + InitSpriteRendering(); + InitWeaponRendering(); + InitFuzzEffect(); + InitColorTranslation(); + InitWindowBorder(resource.Wad); + + SetWindowSize(windowSize); + } + + private void SetWindowSize(int size) + { + var scale = screenWidth / 320; + if (size < 7) + { + var width = scale * (96 + 32 * size); + var height = scale * (48 + 16 * size); + var x = (screenWidth - width) / 2; + var y = (screenHeight - StatusBarRenderer.Height * scale - height) / 2; + ResetWindow(x, y, width, height); + } + else if (size == 7) + { + var width = screenWidth; + var height = screenHeight - StatusBarRenderer.Height * scale; + ResetWindow(0, 0, width, height); + } + else + { + var width = screenWidth; + var height = screenHeight; + ResetWindow(0, 0, width, height); + } + + ResetWallRendering(); + ResetPlaneRendering(); + ResetSkyRendering(); + ResetLighting(); + ResetRenderingHistory(); + ResetWeaponRendering(); + } + + + + //////////////////////////////////////////////////////////// + // Window settings + //////////////////////////////////////////////////////////// + + private int windowX; + private int windowY; + private int windowWidth; + private int windowHeight; + private int centerX; + private int centerY; + private Fixed centerXFrac; + private Fixed centerYFrac; + private Fixed projection; + + private void ResetWindow(int x, int y, int width, int height) + { + windowX = x; + windowY = y; + windowWidth = width; + windowHeight = height; + centerX = windowWidth / 2; + centerY = windowHeight / 2; + centerXFrac = Fixed.FromInt(centerX); + centerYFrac = Fixed.FromInt(centerY); + projection = centerXFrac; + } + + + + //////////////////////////////////////////////////////////// + // Wall rendering + //////////////////////////////////////////////////////////// + + private const int FineFov = 2048; + + private int[] angleToX; + private Angle[] xToAngle; + private Angle clipAngle; + private Angle clipAngle2; + + private void InitWallRendering() + { + angleToX = new int[Trig.FineAngleCount / 2]; + xToAngle = new Angle[screenWidth]; + } + + private void ResetWallRendering() + { + var focalLength = centerXFrac / Trig.Tan(Trig.FineAngleCount / 4 + FineFov / 2); + + for (var i = 0; i < Trig.FineAngleCount / 2; i++) + { + int t; + + if (Trig.Tan(i) > Fixed.FromInt(2)) + { + t = -1; + } + else if (Trig.Tan(i) < Fixed.FromInt(-2)) + { + t = windowWidth + 1; + } + else + { + t = (centerXFrac - Trig.Tan(i) * focalLength).ToIntCeiling(); + + if (t < -1) + { + t = -1; + } + else if (t > windowWidth + 1) + { + t = windowWidth + 1; + } + } + + angleToX[i] = t; + } + + for (var x = 0; x < windowWidth; x++) + { + var i = 0; + while (angleToX[i] > x) + { + i++; + } + xToAngle[x] = new Angle((uint)(i << Trig.AngleToFineShift)) - Angle.Ang90; + } + + for (var i = 0; i < Trig.FineAngleCount / 2; i++) + { + if (angleToX[i] == -1) + { + angleToX[i] = 0; + } + else if (angleToX[i] == windowWidth + 1) + { + angleToX[i] = windowWidth; + } + } + + clipAngle = xToAngle[0]; + clipAngle2 = new Angle(2 * clipAngle.Data); + } + + + + //////////////////////////////////////////////////////////// + // Plane rendering + //////////////////////////////////////////////////////////// + + private Fixed[] planeYSlope; + private Fixed[] planeDistScale; + private Fixed planeBaseXScale; + private Fixed planeBaseYScale; + + private Sector ceilingPrevSector; + private int ceilingPrevX; + private int ceilingPrevY1; + private int ceilingPrevY2; + private Fixed[] ceilingXFrac; + private Fixed[] ceilingYFrac; + private Fixed[] ceilingXStep; + private Fixed[] ceilingYStep; + private byte[][] ceilingLights; + + private Sector floorPrevSector; + private int floorPrevX; + private int floorPrevY1; + private int floorPrevY2; + private Fixed[] floorXFrac; + private Fixed[] floorYFrac; + private Fixed[] floorXStep; + private Fixed[] floorYStep; + private byte[][] floorLights; + + private void InitPlaneRendering() + { + planeYSlope = new Fixed[screenHeight]; + planeDistScale = new Fixed[screenWidth]; + ceilingXFrac = new Fixed[screenHeight]; + ceilingYFrac = new Fixed[screenHeight]; + ceilingXStep = new Fixed[screenHeight]; + ceilingYStep = new Fixed[screenHeight]; + ceilingLights = new byte[screenHeight][]; + floorXFrac = new Fixed[screenHeight]; + floorYFrac = new Fixed[screenHeight]; + floorXStep = new Fixed[screenHeight]; + floorYStep = new Fixed[screenHeight]; + floorLights = new byte[screenHeight][]; + } + + private void ResetPlaneRendering() + { + for (int i = 0; i < windowHeight; i++) + { + var dy = Fixed.FromInt(i - windowHeight / 2) + Fixed.One / 2; + dy = Fixed.Abs(dy); + planeYSlope[i] = Fixed.FromInt(windowWidth / 2) / dy; + } + + for (var i = 0; i < windowWidth; i++) + { + var cos = Fixed.Abs(Trig.Cos(xToAngle[i])); + planeDistScale[i] = Fixed.One / cos; + } + } + + private void ClearPlaneRendering() + { + var angle = viewAngle - Angle.Ang90; + planeBaseXScale = Trig.Cos(angle) / centerXFrac; + planeBaseYScale = -(Trig.Sin(angle) / centerXFrac); + + ceilingPrevSector = null; + ceilingPrevX = int.MaxValue; + + floorPrevSector = null; + floorPrevX = int.MaxValue; + } + + + + //////////////////////////////////////////////////////////// + // Sky rendering + //////////////////////////////////////////////////////////// + + private const int angleToSkyShift = 22; + private Fixed skyTextureAlt; + private Fixed skyInvScale; + + private void InitSkyRendering() + { + skyTextureAlt = Fixed.FromInt(100); + } + + private void ResetSkyRendering() + { + // The code below is based on PrBoom+' sky rendering implementation. + var num = (long)Fixed.FracUnit * screenWidth * 200; + var den = windowWidth * screenHeight; + skyInvScale = new Fixed((int)(num / den)); + } + + + + //////////////////////////////////////////////////////////// + // Lighting + //////////////////////////////////////////////////////////// + + private const int lightLevelCount = 16; + private const int lightSegShift = 4; + private const int scaleLightShift = 12; + private const int zLightShift = 20; + private const int colorMapCount = 32; + + private int maxScaleLight; + private const int maxZLight = 128; + + private byte[][][] diminishingScaleLight; + private byte[][][] diminishingZLight; + private byte[][][] fixedLight; + + private byte[][][] scaleLight; + private byte[][][] zLight; + + private int extraLight; + private int fixedColorMap; + + private void InitLighting() + { + maxScaleLight = 48 * (screenWidth / 320); + + diminishingScaleLight = new byte[lightLevelCount][][]; + diminishingZLight = new byte[lightLevelCount][][]; + fixedLight = new byte[lightLevelCount][][]; + + for (var i = 0; i < lightLevelCount; i++) + { + diminishingScaleLight[i] = new byte[maxScaleLight][]; + diminishingZLight[i] = new byte[maxZLight][]; + fixedLight[i] = new byte[Math.Max(maxScaleLight, maxZLight)][]; + } + + var distMap = 2; + + // Calculate the light levels to use for each level / distance combination. + for (var i = 0; i < lightLevelCount; i++) + { + var start = ((lightLevelCount - 1 - i) * 2) * colorMapCount / lightLevelCount; + for (var j = 0; j < maxZLight; j++) + { + var scale = Fixed.FromInt(320 / 2) / new Fixed((j + 1) << zLightShift); + scale = new Fixed(scale.Data >> scaleLightShift); + + var level = start - scale.Data / distMap; + if (level < 0) + { + level = 0; + } + if (level >= colorMapCount) + { + level = colorMapCount - 1; + } + + diminishingZLight[i][j] = colorMap[level]; + } + } + } + + private void ResetLighting() + { + var distMap = 2; + + // Calculate the light levels to use for each level / scale combination. + for (var i = 0; i < lightLevelCount; i++) + { + var start = ((lightLevelCount - 1 - i) * 2) * colorMapCount / lightLevelCount; + for (var j = 0; j < maxScaleLight; j++) + { + var level = start - j * 320 / windowWidth / distMap; + if (level < 0) + { + level = 0; + } + if (level >= colorMapCount) + { + level = colorMapCount - 1; + } + + diminishingScaleLight[i][j] = colorMap[level]; + } + } + } + + private void ClearLighting() + { + if (fixedColorMap == 0) + { + scaleLight = diminishingScaleLight; + zLight = diminishingZLight; + fixedLight[0][0] = null; + } + else if (fixedLight[0][0] != colorMap[fixedColorMap]) + { + for (var i = 0; i < lightLevelCount; i++) + { + for (var j = 0; j < fixedLight[i].Length; j++) + { + fixedLight[i][j] = colorMap[fixedColorMap]; + } + } + scaleLight = fixedLight; + zLight = fixedLight; + } + } + + + + //////////////////////////////////////////////////////////// + // Rendering history + //////////////////////////////////////////////////////////// + + private short[] upperClip; + private short[] lowerClip; + + private int negOneArray; + private int windowHeightArray; + + private int clipRangeCount; + private ClipRange[] clipRanges; + + private int clipDataLength; + private short[] clipData; + + private int visWallRangeCount; + private VisWallRange[] visWallRanges; + + private void InitRenderingHistory() + { + upperClip = new short[screenWidth]; + lowerClip = new short[screenWidth]; + + clipRanges = new ClipRange[256]; + for (var i = 0; i < clipRanges.Length; i++) + { + clipRanges[i] = new ClipRange(); + } + + clipData = new short[128 * screenWidth]; + + visWallRanges = new VisWallRange[512]; + for (var i = 0; i < visWallRanges.Length; i++) + { + visWallRanges[i] = new VisWallRange(); + } + } + + private void ResetRenderingHistory() + { + for (var i = 0; i < windowWidth; i++) + { + clipData[i] = -1; + } + negOneArray = 0; + + for (var i = windowWidth; i < 2 * windowWidth; i++) + { + clipData[i] = (short)windowHeight; + } + windowHeightArray = windowWidth; + } + + private void ClearRenderingHistory() + { + for (var x = 0; x < windowWidth; x++) + { + upperClip[x] = -1; + } + for (var x = 0; x < windowWidth; x++) + { + lowerClip[x] = (short)windowHeight; + } + + clipRanges[0].First = -0x7fffffff; + clipRanges[0].Last = -1; + clipRanges[1].First = windowWidth; + clipRanges[1].Last = 0x7fffffff; + clipRangeCount = 2; + + clipDataLength = 2 * windowWidth; + + visWallRangeCount = 0; + } + + + + //////////////////////////////////////////////////////////// + // Sprite rendering + //////////////////////////////////////////////////////////// + + private static readonly Fixed minZ = Fixed.FromInt(4); + + private int visSpriteCount; + private VisSprite[] visSprites; + + private void InitSpriteRendering() + { + visSprites = new VisSprite[256]; + for (var i = 0; i < visSprites.Length; i++) + { + visSprites[i] = new VisSprite(); + } + } + + private void ClearSpriteRendering() + { + visSpriteCount = 0; + } + + + + //////////////////////////////////////////////////////////// + // Weapon rendering + //////////////////////////////////////////////////////////// + + private VisSprite weaponSprite; + private Fixed weaponScale; + private Fixed weaponInvScale; + + private void InitWeaponRendering() + { + weaponSprite = new VisSprite(); + } + + private void ResetWeaponRendering() + { + weaponScale = new Fixed(Fixed.FracUnit * windowWidth / 320); + weaponInvScale = new Fixed(Fixed.FracUnit * 320 / windowWidth); + } + + + + //////////////////////////////////////////////////////////// + // Fuzz effect + //////////////////////////////////////////////////////////// + + private static sbyte[] fuzzTable = new sbyte[] + { + 1, -1, 1, -1, 1, 1, -1, + 1, 1, -1, 1, 1, 1, -1, + 1, 1, 1, -1, -1, -1, -1, + 1, -1, -1, 1, 1, 1, 1, -1, + 1, -1, 1, 1, -1, -1, 1, + 1, -1, -1, -1, -1, 1, 1, + 1, 1, -1, 1, 1, -1, 1 + }; + + private int fuzzPos; + + private void InitFuzzEffect() + { + fuzzPos = 0; + } + + + + //////////////////////////////////////////////////////////// + // Color translation + //////////////////////////////////////////////////////////// + + private byte[] greenToGray; + private byte[] greenToBrown; + private byte[] greenToRed; + + private void InitColorTranslation() + { + greenToGray = new byte[256]; + greenToBrown = new byte[256]; + greenToRed = new byte[256]; + for (var i = 0; i < 256; i++) + { + greenToGray[i] = (byte)i; + greenToBrown[i] = (byte)i; + greenToRed[i] = (byte)i; + } + for (var i = 112; i < 128; i++) + { + greenToGray[i] -= 16; + greenToBrown[i] -= 48; + greenToRed[i] -= 80; + } + } + + + + //////////////////////////////////////////////////////////// + // Window border + //////////////////////////////////////////////////////////// + + private Patch borderTopLeft; + private Patch borderTopRight; + private Patch borderBottomLeft; + private Patch borderBottomRight; + private Patch borderTop; + private Patch borderBottom; + private Patch borderLeft; + private Patch borderRight; + private Flat backFlat; + + private void InitWindowBorder(Wad wad) + { + borderTopLeft = Patch.FromWad(wad, "BRDR_TL"); + borderTopRight = Patch.FromWad(wad, "BRDR_TR"); + borderBottomLeft = Patch.FromWad(wad, "BRDR_BL"); + borderBottomRight = Patch.FromWad(wad, "BRDR_BR"); + borderTop = Patch.FromWad(wad, "BRDR_T"); + borderBottom = Patch.FromWad(wad, "BRDR_B"); + borderLeft = Patch.FromWad(wad, "BRDR_L"); + borderRight = Patch.FromWad(wad, "BRDR_R"); + + if (wad.GameMode == GameMode.Commercial) + { + backFlat = flats["GRNROCK"]; + } + else + { + backFlat = flats["FLOOR7_2"]; + } + } + + private void FillBackScreen() + { + var fillHeight = screenHeight - drawScale * StatusBarRenderer.Height; + FillRect(0, 0, windowX, fillHeight); + FillRect(screenWidth - windowX, 0, windowX, fillHeight); + FillRect(windowX, 0, screenWidth - 2 * windowX, windowY); + FillRect(windowX, fillHeight - windowY, screenWidth - 2 * windowX, windowY); + + var step = 8 * drawScale; + + for (var x = windowX; x < screenWidth - windowX; x += step) + { + screen.DrawPatch(borderTop, x, windowY - step, drawScale); + screen.DrawPatch(borderBottom, x, fillHeight - windowY, drawScale); + } + + for (var y = windowY; y < fillHeight - windowY; y += step) + { + screen.DrawPatch(borderLeft, windowX - step, y, drawScale); + screen.DrawPatch(borderRight, screenWidth - windowX, y, drawScale); + } + + screen.DrawPatch(borderTopLeft, windowX - step, windowY - step, drawScale); + screen.DrawPatch(borderTopRight, screenWidth - windowX, windowY - step, drawScale); + screen.DrawPatch(borderBottomLeft, windowX - step, fillHeight - windowY, drawScale); + screen.DrawPatch(borderBottomRight, screenWidth - windowX, fillHeight - windowY, drawScale); + } + + private void FillRect(int x, int y, int width, int height) + { + var data = backFlat.Data; + + var srcX = x / drawScale; + var srcY = y / drawScale; + + var invScale = Fixed.One / drawScale; + var xFrac = invScale - Fixed.Epsilon; + + for (var i = 0; i < width; i++) + { + var src = ((srcX + xFrac.ToIntFloor()) & 63) << 6; + var dst = screenHeight * (x + i) + y; + var yFrac = invScale - Fixed.Epsilon; + for (var j = 0; j < height; j++) + { + screenData[dst + j] = data[src | ((srcY + yFrac.ToIntFloor()) & 63)]; + yFrac += invScale; + } + xFrac += invScale; + } + } + + + + //////////////////////////////////////////////////////////// + // Camera view + //////////////////////////////////////////////////////////// + + private World world; + + private Fixed viewX; + private Fixed viewY; + private Fixed viewZ; + private Angle viewAngle; + + private Fixed viewSin; + private Fixed viewCos; + + private int validCount; + + + + public void Render(Player player) + { + //var watch = System.Diagnostics.Stopwatch.StartNew(); + world = player.Mobj.World; + + viewX = player.Mobj.X; + viewY = player.Mobj.Y; + viewZ = player.ViewZ; + viewAngle = player.Mobj.Angle; + + viewSin = Trig.Sin(viewAngle); + viewCos = Trig.Cos(viewAngle); + + validCount = world.GetNewValidCount(); + + extraLight = player.ExtraLight; + fixedColorMap = player.FixedColorMap; + + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("before clear {0} ms", watch.ElapsedMilliseconds); + // watch.Restart(); + ClearPlaneRendering(); + ClearLighting(); + ClearRenderingHistory(); + ClearSpriteRendering(); + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("clears (player={1}) {0} ms", watch.ElapsedMilliseconds, player); + //watch.Restart(); + + RenderBspNode(world.Map.Nodes.Length - 1); + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("RenderBspNode {0} ms", watch.ElapsedMilliseconds); + // watch.Restart(); + RenderSprites(); + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("RenderSprites {0} ms", watch.ElapsedMilliseconds); + // watch.Restart(); + RenderMaskedTextures(); + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("RenderMaskedTextures {0} ms", watch.ElapsedMilliseconds); + // watch.Restart(); + DrawPlayerSprites(player); + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("DrawPlayerSprites {0} ms", watch.ElapsedMilliseconds); + // watch.Restart(); + + + if (windowSize < 7) + { + FillBackScreen(); + } + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("FillBackScreen (winsize={1}) {0} ms", watch.ElapsedMilliseconds, windowSize); + + } + + + + private void RenderBspNode(int node) + { + // var watch = System.Diagnostics.Stopwatch.StartNew(); + if (Node.IsSubsector(node)) + { + if (node == -1) + { + DrawSubsector(0); + } + else + { + DrawSubsector(Node.GetSubsector(node)); + } + // TODO: culprit + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("inside RenderBspNode draw subsector ({1}) {0} ms", watch.ElapsedMilliseconds, node); + // watch.Restart(); + return; + } + + var bsp = world.Map.Nodes[node]; + + // Decide which side the view point is on. + var side = Geometry.PointOnSide(viewX, viewY, bsp); + + // Recursively divide front space. + RenderBspNode(bsp.Children[side]); + + // Possibly divide back space. + if (IsPotentiallyVisible(bsp.BoundingBox[side ^ 1])) + { + RenderBspNode(bsp.Children[side ^ 1]); + } + // TODO: culprit above + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("inside RenderBspNode end ({1}) {0} ms", watch.ElapsedMilliseconds, node); + // watch.Restart(); + } + + + + private void DrawSubsector(int subsector) + { + var target = world.Map.Subsectors[subsector]; + + // var watch = System.Diagnostics.Stopwatch.StartNew(); + AddSprites(target.Sector, validCount); + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("ThreeDRenderer.DrawSubsector add sprites: {0} ms", watch.ElapsedMilliseconds); + // watch.Restart(); + for (var i = 0; i < target.SegCount; i++) + { + DrawSeg(world.Map.Segs[target.FirstSeg + i]); + } + // TODO: culprit + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("ThreeDRenderer.DrawSubsector draw segs: {0} ms", watch.ElapsedMilliseconds); + // watch.Restart(); + } + + + + private static readonly int[][] viewPosToFrustumTangent = + { + new[] { 3, 0, 2, 1 }, + new[] { 3, 0, 2, 0 }, + new[] { 3, 1, 2, 0 }, + new[] { 0 }, + new[] { 2, 0, 2, 1 }, + new[] { 0, 0, 0, 0 }, + new[] { 3, 1, 3, 0 }, + new[] { 0 }, + new[] { 2, 0, 3, 1 }, + new[] { 2, 1, 3, 1 }, + new[] { 2, 1, 3, 0 } + }; + + private bool IsPotentiallyVisible(Fixed[] bbox) + { + int bx; + int by; + + // Find the corners of the box that define the edges from + // current viewpoint. + if (viewX <= bbox[Box.Left]) + { + bx = 0; + } + else if (viewX < bbox[Box.Right]) + { + bx = 1; + } + else + { + bx = 2; + } + + if (viewY >= bbox[Box.Top]) + { + by = 0; + } + else if (viewY > bbox[Box.Bottom]) + { + by = 1; + } + else + { + by = 2; + } + + var viewPos = (by << 2) + bx; + if (viewPos == 5) + { + return true; + } + + var x1 = bbox[viewPosToFrustumTangent[viewPos][0]]; + var y1 = bbox[viewPosToFrustumTangent[viewPos][1]]; + var x2 = bbox[viewPosToFrustumTangent[viewPos][2]]; + var y2 = bbox[viewPosToFrustumTangent[viewPos][3]]; + + // Check clip list for an open space. + var angle1 = Geometry.PointToAngle(viewX, viewY, x1, y1) - viewAngle; + var angle2 = Geometry.PointToAngle(viewX, viewY, x2, y2) - viewAngle; + + var span = angle1 - angle2; + + // Sitting on a line? + if (span >= Angle.Ang180) + { + return true; + } + + var tSpan1 = angle1 + clipAngle; + + if (tSpan1 > clipAngle2) + { + tSpan1 -= clipAngle2; + + // Totally off the left edge? + if (tSpan1 >= span) + { + return false; + } + + angle1 = clipAngle; + } + + var tSpan2 = clipAngle - angle2; + if (tSpan2 > clipAngle2) + { + tSpan2 -= clipAngle2; + + // Totally off the left edge? + if (tSpan2 >= span) + { + return false; + } + + angle2 = -clipAngle; + } + + // Find the first clippost that touches the source post + // (adjacent pixels are touching). + var sx1 = angleToX[(angle1 + Angle.Ang90).Data >> Trig.AngleToFineShift]; + var sx2 = angleToX[(angle2 + Angle.Ang90).Data >> Trig.AngleToFineShift]; + + // Does not cross a pixel. + if (sx1 == sx2) + { + return false; + } + + sx2--; + + var start = 0; + while (clipRanges[start].Last < sx2) + { + start++; + } + + if (sx1 >= clipRanges[start].First && sx2 <= clipRanges[start].Last) + { + // The clippost contains the new span. + return false; + } + + return true; + } + + + + private void DrawSeg(Seg seg) + { + // var watch = System.Diagnostics.Stopwatch.StartNew(); + // OPTIMIZE: quickly reject orthogonal back sides. + var angle1 = Geometry.PointToAngle(viewX, viewY, seg.Vertex1.X, seg.Vertex1.Y); + var angle2 = Geometry.PointToAngle(viewX, viewY, seg.Vertex2.X, seg.Vertex2.Y); + + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine($"ThreeDRenderer.DrawSeg {seg} angles: {watch.ElapsedMilliseconds} ms"); + // watch.Restart(); + + // Clip to view edges. + // OPTIMIZE: make constant out of 2 * clipangle (FIELDOFVIEW). + var span = angle1 - angle2; + + // Back side? I.e. backface culling? + if (span >= Angle.Ang180) + { + return; + } + + // Global angle needed by segcalc. + var rwAngle1 = angle1; + + angle1 -= viewAngle; + angle2 -= viewAngle; + + var tSpan1 = angle1 + clipAngle; + if (tSpan1 > clipAngle2) + { + tSpan1 -= clipAngle2; + + // Totally off the left edge? + if (tSpan1 >= span) + { + return; + } + + angle1 = clipAngle; + } + + var tSpan2 = clipAngle - angle2; + if (tSpan2 > clipAngle2) + { + tSpan2 -= clipAngle2; + + // Totally off the left edge? + if (tSpan2 >= span) + { + return; + } + + angle2 = -clipAngle; + } + + // The seg is in the view range, but not necessarily visible. + var x1 = angleToX[(angle1 + Angle.Ang90).Data >> Trig.AngleToFineShift]; + var x2 = angleToX[(angle2 + Angle.Ang90).Data >> Trig.AngleToFineShift]; + + // Does not cross a pixel? + if (x1 == x2) + { + return; + } + + var frontSector = seg.FrontSector; + var backSector = seg.BackSector; + + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine($"ThreeDRenderer.DrawSeg {seg} before draw: {watch.ElapsedMilliseconds} ms"); + // watch.Restart(); + // Single sided line? + if (backSector == null) + { + DrawSolidWall(seg, rwAngle1, x1, x2 - 1); + return; + } + + // Closed door. + if (backSector.CeilingHeight <= frontSector.FloorHeight || + backSector.FloorHeight >= frontSector.CeilingHeight) + { + DrawSolidWall(seg, rwAngle1, x1, x2 - 1); + return; + } + + // Window. + if (backSector.CeilingHeight != frontSector.CeilingHeight || + backSector.FloorHeight != frontSector.FloorHeight) + { + DrawPassWall(seg, rwAngle1, x1, x2 - 1); + return; + } + + // Reject empty lines used for triggers and special events. + // Identical floor and ceiling on both sides, identical + // light levels on both sides, and no middle texture. + if (backSector.CeilingFlat == frontSector.CeilingFlat && + backSector.FloorFlat == frontSector.FloorFlat && + backSector.LightLevel == frontSector.LightLevel && + seg.SideDef.MiddleTexture == 0) + { + return; + } + + DrawPassWall(seg, rwAngle1, x1, x2 - 1); + } + + + + private void DrawSolidWall(Seg seg, Angle rwAngle1, int x1, int x2) + { + // var watch = System.Diagnostics.Stopwatch.StartNew(); + + int next; + int start; + + // Find the first range that touches the range + // (adjacent pixels are touching). + start = 0; + while (clipRanges[start].Last < x1 - 1) + { + start++; + } + + if (x1 < clipRanges[start].First) + { + if (x2 < clipRanges[start].First - 1) + { + // Post is entirely visible (above start), + // so insert a new clippost. + DrawSolidWallRange(seg, rwAngle1, x1, x2); + next = clipRangeCount; + clipRangeCount++; + + while (next != start) + { + clipRanges[next].CopyFrom(clipRanges[next - 1]); + next--; + } + clipRanges[next].First = x1; + clipRanges[next].Last = x2; + return; + } + + // There is a fragment above *start. + DrawSolidWallRange(seg, rwAngle1, x1, clipRanges[start].First - 1); + + // Now adjust the clip size. + clipRanges[start].First = x1; + } + + // Bottom contained in start? + if (x2 <= clipRanges[start].Last) + { + return; + } + + next = start; + while (x2 >= clipRanges[next + 1].First - 1) + { + // There is a fragment between two posts. + DrawSolidWallRange(seg, rwAngle1, clipRanges[next].Last + 1, clipRanges[next + 1].First - 1); + next++; + + if (x2 <= clipRanges[next].Last) + { + // Bottom is contained in next. + // Adjust the clip size. + clipRanges[start].Last = clipRanges[next].Last; + goto crunch; + } + } + + // There is a fragment after *next. + DrawSolidWallRange(seg, rwAngle1, clipRanges[next].Last + 1, x2); + + // Adjust the clip size. + clipRanges[start].Last = x2; + + // Remove start + 1 to next from the clip list, + // because start now covers their area. + crunch: + if (next == start) + { + // Post just extended past the bottom of one post. + return; + } + + while (next++ != clipRangeCount) + { + // Remove a post. + clipRanges[++start].CopyFrom(clipRanges[next]); + } + + clipRangeCount = start + 1; + + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine($"ThreeDRenderer.DrawSolidWall {seg} before draw: {watch.ElapsedMilliseconds} ms"); + // watch.Restart(); + } + + + + private void DrawPassWall(Seg seg, Angle rwAngle1, int x1, int x2) + { + int start; + // var watch = System.Diagnostics.Stopwatch.StartNew(); + + // Find the first range that touches the range + // (adjacent pixels are touching). + start = 0; + while (clipRanges[start].Last < x1 - 1) + { + start++; + } + + if (x1 < clipRanges[start].First) + { + if (x2 < clipRanges[start].First - 1) + { + // Post is entirely visible (above start). + DrawPassWallRange(seg, rwAngle1, x1, x2, false); + return; + } + + // There is a fragment above *start. + DrawPassWallRange(seg, rwAngle1, x1, clipRanges[start].First - 1, false); + } + + // Bottom contained in start? + if (x2 <= clipRanges[start].Last) + { + return; + } + + while (x2 >= clipRanges[start + 1].First - 1) + { + // There is a fragment between two posts. + DrawPassWallRange(seg, rwAngle1, clipRanges[start].Last + 1, clipRanges[start + 1].First - 1, false); + start++; + + if (x2 <= clipRanges[start].Last) + { + return; + } + } + + // There is a fragment after *next. + DrawPassWallRange(seg, rwAngle1, clipRanges[start].Last + 1, x2, false); + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("ThreeDRederer.drawPassWall : {0} ms", watch.ElapsedMilliseconds); + + } + + + + private Fixed ScaleFromGlobalAngle(Angle visAngle, Angle viewAngle, Angle rwNormal, Fixed rwDistance) + { + var num = projection * Trig.Sin(Angle.Ang90 + (visAngle - rwNormal)); + var den = rwDistance * Trig.Sin(Angle.Ang90 + (visAngle - viewAngle)); + + Fixed scale; + if (den.Data > num.Data >> 16) + { + scale = num / den; + + if (scale > Fixed.FromInt(64)) + { + scale = Fixed.FromInt(64); + } + else if (scale.Data < 256) + { + scale = new Fixed(256); + } + } + else + { + scale = Fixed.FromInt(64); + } + + return scale; + } + + + + private const int heightBits = 12; + private const int heightUnit = 1 << heightBits; + + private void DrawSolidWallRange(Seg seg, Angle rwAngle1, int x1, int x2) + { + if (seg.BackSector != null) + { + DrawPassWallRange(seg, rwAngle1, x1, x2, true); + return; + } + + if (visWallRangeCount == visWallRanges.Length) + { + // Too many visible walls. + return; + } + + // Make some aliases to shorten the following code. + var line = seg.LineDef; + var side = seg.SideDef; + var frontSector = seg.FrontSector; + + // Mark the segment as visible for auto map. + line.Flags |= LineFlags.Mapped; + + // Calculate the relative plane heights of front and back sector. + var worldFrontZ1 = frontSector.CeilingHeight - viewZ; + var worldFrontZ2 = frontSector.FloorHeight - viewZ; + + // Check which parts must be rendered. + var drawWall = side.MiddleTexture != 0; + var drawCeiling = worldFrontZ1 > Fixed.Zero || frontSector.CeilingFlat == flats.SkyFlatNumber; + var drawFloor = worldFrontZ2 < Fixed.Zero; + + // + // Determine how the wall textures are vertically aligned. + // + + var wallTexture = textures[world.Specials.TextureTranslation[side.MiddleTexture]]; + var wallWidthMask = wallTexture.Width - 1; + + Fixed middleTextureAlt; + if ((line.Flags & LineFlags.DontPegBottom) != 0) + { + var vTop = frontSector.FloorHeight + Fixed.FromInt(wallTexture.Height); + middleTextureAlt = vTop - viewZ; + } + else + { + middleTextureAlt = worldFrontZ1; + } + middleTextureAlt += side.RowOffset; + + // + // Calculate the scaling factors of the left and right edges of the wall range. + // + + var rwNormalAngle = seg.Angle + Angle.Ang90; + + var offsetAngle = Angle.Abs(rwNormalAngle - rwAngle1); + if (offsetAngle > Angle.Ang90) + { + offsetAngle = Angle.Ang90; + } + + var distAngle = Angle.Ang90 - offsetAngle; + + var hypotenuse = Geometry.PointToDist(viewX, viewY, seg.Vertex1.X, seg.Vertex1.Y); + + var rwDistance = hypotenuse * Trig.Sin(distAngle); + + var rwScale = ScaleFromGlobalAngle(viewAngle + xToAngle[x1], viewAngle, rwNormalAngle, rwDistance); + + Fixed scale1 = rwScale; + Fixed scale2; + Fixed rwScaleStep; + if (x2 > x1) + { + scale2 = ScaleFromGlobalAngle(viewAngle + xToAngle[x2], viewAngle, rwNormalAngle, rwDistance); + rwScaleStep = (scale2 - rwScale) / (x2 - x1); + } + else + { + scale2 = scale1; + rwScaleStep = Fixed.Zero; + } + + // + // Determine how the wall textures are horizontally aligned + // and which color map is used according to the light level (if necessary). + // + + var textureOffsetAngle = rwNormalAngle - rwAngle1; + if (textureOffsetAngle > Angle.Ang180) + { + textureOffsetAngle = -textureOffsetAngle; + } + if (textureOffsetAngle > Angle.Ang90) + { + textureOffsetAngle = Angle.Ang90; + } + + var rwOffset = hypotenuse * Trig.Sin(textureOffsetAngle); + if (rwNormalAngle - rwAngle1 < Angle.Ang180) + { + rwOffset = -rwOffset; + } + rwOffset += seg.Offset + side.TextureOffset; + + var rwCenterAngle = Angle.Ang90 + viewAngle - rwNormalAngle; + + var wallLightLevel = (frontSector.LightLevel >> lightSegShift) + extraLight; + if (seg.Vertex1.Y == seg.Vertex2.Y) + { + wallLightLevel--; + } + else if (seg.Vertex1.X == seg.Vertex2.X) + { + wallLightLevel++; + } + + var wallLights = scaleLight[Math.Clamp(wallLightLevel, 0, lightLevelCount - 1)]; + + // + // Determine where on the screen the wall is drawn. + // + + // These values are right shifted to avoid overflow in the following process (maybe). + worldFrontZ1 >>= 4; + worldFrontZ2 >>= 4; + + // The Y positions of the top / bottom edges of the wall on the screen. + var wallY1Frac = (centerYFrac >> 4) - worldFrontZ1 * rwScale; + var wallY1Step = -(rwScaleStep * worldFrontZ1); + var wallY2Frac = (centerYFrac >> 4) - worldFrontZ2 * rwScale; + var wallY2Step = -(rwScaleStep * worldFrontZ2); + + // + // Determine which color map is used for the plane according to the light level. + // + + var planeLightLevel = (frontSector.LightLevel >> lightSegShift) + extraLight; + if (planeLightLevel >= lightLevelCount) + { + planeLightLevel = lightLevelCount - 1; + } + var planeLights = zLight[planeLightLevel]; + + // + // Prepare to record the rendering history. + // + + var visWallRange = visWallRanges[visWallRangeCount]; + visWallRangeCount++; + + visWallRange.Seg = seg; + visWallRange.X1 = x1; + visWallRange.X2 = x2; + visWallRange.Scale1 = scale1; + visWallRange.Scale2 = scale2; + visWallRange.ScaleStep = rwScaleStep; + visWallRange.Silhouette = Silhouette.Both; + visWallRange.LowerSilHeight = Fixed.MaxValue; + visWallRange.UpperSilHeight = Fixed.MinValue; + visWallRange.MaskedTextureColumn = -1; + visWallRange.UpperClip = windowHeightArray; + visWallRange.LowerClip = negOneArray; + + // + // Floor and ceiling. + // + + var ceilingFlat = flats[world.Specials.FlatTranslation[frontSector.CeilingFlat]]; + var floorFlat = flats[world.Specials.FlatTranslation[frontSector.FloorFlat]]; + + // + // Now the rendering is carried out. + // + + for (var x = x1; x <= x2; x++) + { + var drawWallY1 = (wallY1Frac.Data + heightUnit - 1) >> heightBits; + var drawWallY2 = wallY2Frac.Data >> heightBits; + + if (drawCeiling) + { + var cy1 = upperClip[x] + 1; + var cy2 = Math.Min(drawWallY1 - 1, lowerClip[x] - 1); + DrawCeilingColumn(frontSector, ceilingFlat, planeLights, x, cy1, cy2); + } + + if (drawWall) + { + var wy1 = Math.Max(drawWallY1, upperClip[x] + 1); + var wy2 = Math.Min(drawWallY2, lowerClip[x] - 1); + + var angle = rwCenterAngle + xToAngle[x]; + angle = new Angle(angle.Data & 0x7FFFFFFF); + + var textureColumn = (rwOffset - Trig.Tan(angle) * rwDistance).ToIntFloor(); + var source = wallTexture.Composite.Columns[textureColumn & wallWidthMask]; + + if (source.Length > 0) + { + var lightIndex = rwScale.Data >> scaleLightShift; + if (lightIndex >= maxScaleLight) + { + lightIndex = maxScaleLight - 1; + } + + var invScale = new Fixed((int)(0xffffffffu / (uint)rwScale.Data)); + DrawColumn(source[0], wallLights[lightIndex], x, wy1, wy2, invScale, middleTextureAlt); + } + } + + if (drawFloor) + { + var fy1 = Math.Max(drawWallY2 + 1, upperClip[x] + 1); + var fy2 = lowerClip[x] - 1; + DrawFloorColumn(frontSector, floorFlat, planeLights, x, fy1, fy2); + } + + rwScale += rwScaleStep; + wallY1Frac += wallY1Step; + wallY2Frac += wallY2Step; + } + } + + + + private void DrawPassWallRange(Seg seg, Angle rwAngle1, int x1, int x2, bool drawAsSolidWall) + { + if (visWallRangeCount == visWallRanges.Length) + { + // Too many visible walls. + return; + } + + var range = x2 - x1 + 1; + + if (clipDataLength + 3 * range >= clipData.Length) + { + // Clip info buffer is not sufficient. + return; + } + + // Make some aliases to shorten the following code. + var line = seg.LineDef; + var side = seg.SideDef; + var frontSector = seg.FrontSector; + var backSector = seg.BackSector; + + // Mark the segment as visible for auto map. + line.Flags |= LineFlags.Mapped; + + // Calculate the relative plane heights of front and back sector. + // These values are later 4 bits right shifted to calculate the rendering area. + var worldFrontZ1 = frontSector.CeilingHeight - viewZ; + var worldFrontZ2 = frontSector.FloorHeight - viewZ; + var worldBackZ1 = backSector.CeilingHeight - viewZ; + var worldBackZ2 = backSector.FloorHeight - viewZ; + + // The hack below enables ceiling height change in outdoor area without showing the upper wall. + if (frontSector.CeilingFlat == flats.SkyFlatNumber && + backSector.CeilingFlat == flats.SkyFlatNumber) + { + worldFrontZ1 = worldBackZ1; + } + + // + // Check which parts must be rendered. + // + + bool drawUpperWall; + bool drawCeiling; + if (drawAsSolidWall || + worldFrontZ1 != worldBackZ1 || + frontSector.CeilingFlat != backSector.CeilingFlat || + frontSector.LightLevel != backSector.LightLevel) + { + drawUpperWall = side.TopTexture != 0 && worldBackZ1 < worldFrontZ1; + drawCeiling = worldFrontZ1 >= Fixed.Zero || frontSector.CeilingFlat == flats.SkyFlatNumber; + } + else + { + drawUpperWall = false; + drawCeiling = false; + } + + bool drawLowerWall; + bool drawFloor; + if (drawAsSolidWall || + worldFrontZ2 != worldBackZ2 || + frontSector.FloorFlat != backSector.FloorFlat || + frontSector.LightLevel != backSector.LightLevel) + { + drawLowerWall = side.BottomTexture != 0 && worldBackZ2 > worldFrontZ2; + drawFloor = worldFrontZ2 <= Fixed.Zero; + } + else + { + drawLowerWall = false; + drawFloor = false; + } + + var drawMaskedTexture = side.MiddleTexture != 0; + + // If nothing must be rendered, we can skip this seg. + if (!drawUpperWall && !drawCeiling && !drawLowerWall && !drawFloor && !drawMaskedTexture) + { + return; + } + + var segTextured = drawUpperWall || drawLowerWall || drawMaskedTexture; + + // + // Determine how the wall textures are vertically aligned (if necessary). + // + + var upperWallTexture = default(Texture); + var upperWallWidthMask = default(int); + var uperTextureAlt = default(Fixed); + if (drawUpperWall) + { + upperWallTexture = textures[world.Specials.TextureTranslation[side.TopTexture]]; + upperWallWidthMask = upperWallTexture.Width - 1; + + if ((line.Flags & LineFlags.DontPegTop) != 0) + { + uperTextureAlt = worldFrontZ1; + } + else + { + var vTop = backSector.CeilingHeight + Fixed.FromInt(upperWallTexture.Height); + uperTextureAlt = vTop - viewZ; + } + uperTextureAlt += side.RowOffset; + } + + var lowerWallTexture = default(Texture); + var lowerWallWidthMask = default(int); + var lowerTextureAlt = default(Fixed); + if (drawLowerWall) + { + lowerWallTexture = textures[world.Specials.TextureTranslation[side.BottomTexture]]; + lowerWallWidthMask = lowerWallTexture.Width - 1; + + if ((line.Flags & LineFlags.DontPegBottom) != 0) + { + lowerTextureAlt = worldFrontZ1; + } + else + { + lowerTextureAlt = worldBackZ2; + } + lowerTextureAlt += side.RowOffset; + } + + // + // Calculate the scaling factors of the left and right edges of the wall range. + // + + var rwNormalAngle = seg.Angle + Angle.Ang90; + + var offsetAngle = Angle.Abs(rwNormalAngle - rwAngle1); + if (offsetAngle > Angle.Ang90) + { + offsetAngle = Angle.Ang90; + } + + var distAngle = Angle.Ang90 - offsetAngle; + + var hypotenuse = Geometry.PointToDist(viewX, viewY, seg.Vertex1.X, seg.Vertex1.Y); + + var rwDistance = hypotenuse * Trig.Sin(distAngle); + + var rwScale = ScaleFromGlobalAngle(viewAngle + xToAngle[x1], viewAngle, rwNormalAngle, rwDistance); + + Fixed scale1 = rwScale; + Fixed scale2; + Fixed rwScaleStep; + if (x2 > x1) + { + scale2 = ScaleFromGlobalAngle(viewAngle + xToAngle[x2], viewAngle, rwNormalAngle, rwDistance); + rwScaleStep = (scale2 - rwScale) / (x2 - x1); + } + else + { + scale2 = scale1; + rwScaleStep = Fixed.Zero; + } + + // + // Determine how the wall textures are horizontally aligned + // and which color map is used according to the light level (if necessary). + // + + var rwOffset = default(Fixed); + var rwCenterAngle = default(Angle); + var wallLights = default(byte[][]); + if (segTextured) + { + var textureOffsetAngle = rwNormalAngle - rwAngle1; + if (textureOffsetAngle > Angle.Ang180) + { + textureOffsetAngle = -textureOffsetAngle; + } + if (textureOffsetAngle > Angle.Ang90) + { + textureOffsetAngle = Angle.Ang90; + } + + rwOffset = hypotenuse * Trig.Sin(textureOffsetAngle); + if (rwNormalAngle - rwAngle1 < Angle.Ang180) + { + rwOffset = -rwOffset; + } + rwOffset += seg.Offset + side.TextureOffset; + + rwCenterAngle = Angle.Ang90 + viewAngle - rwNormalAngle; + + var wallLightLevel = (frontSector.LightLevel >> lightSegShift) + extraLight; + if (seg.Vertex1.Y == seg.Vertex2.Y) + { + wallLightLevel--; + } + else if (seg.Vertex1.X == seg.Vertex2.X) + { + wallLightLevel++; + } + + wallLights = scaleLight[Math.Clamp(wallLightLevel, 0, lightLevelCount - 1)]; + } + + // + // Determine where on the screen the wall is drawn. + // + + // These values are right shifted to avoid overflow in the following process. + worldFrontZ1 >>= 4; + worldFrontZ2 >>= 4; + worldBackZ1 >>= 4; + worldBackZ2 >>= 4; + + // The Y positions of the top / bottom edges of the wall on the screen.. + var wallY1Frac = (centerYFrac >> 4) - worldFrontZ1 * rwScale; + var wallY1Step = -(rwScaleStep * worldFrontZ1); + var wallY2Frac = (centerYFrac >> 4) - worldFrontZ2 * rwScale; + var wallY2Step = -(rwScaleStep * worldFrontZ2); + + // The Y position of the top edge of the portal (if visible). + var portalY1Frac = default(Fixed); + var portalY1Step = default(Fixed); + if (drawUpperWall) + { + if (worldBackZ1 > worldFrontZ2) + { + portalY1Frac = (centerYFrac >> 4) - worldBackZ1 * rwScale; + portalY1Step = -(rwScaleStep * worldBackZ1); + } + else + { + portalY1Frac = (centerYFrac >> 4) - worldFrontZ2 * rwScale; + portalY1Step = -(rwScaleStep * worldFrontZ2); + } + } + + // The Y position of the bottom edge of the portal (if visible). + var portalY2Frac = default(Fixed); + var portalY2Step = default(Fixed); + if (drawLowerWall) + { + if (worldBackZ2 < worldFrontZ1) + { + portalY2Frac = (centerYFrac >> 4) - worldBackZ2 * rwScale; + portalY2Step = -(rwScaleStep * worldBackZ2); + } + else + { + portalY2Frac = (centerYFrac >> 4) - worldFrontZ1 * rwScale; + portalY2Step = -(rwScaleStep * worldFrontZ1); + } + } + + // + // Determine which color map is used for the plane according to the light level. + // + + var planeLightLevel = (frontSector.LightLevel >> lightSegShift) + extraLight; + if (planeLightLevel >= lightLevelCount) + { + planeLightLevel = lightLevelCount - 1; + } + var planeLights = zLight[planeLightLevel]; + + // + // Prepare to record the rendering history. + // + + var visWallRange = visWallRanges[visWallRangeCount]; + visWallRangeCount++; + + visWallRange.Seg = seg; + visWallRange.X1 = x1; + visWallRange.X2 = x2; + visWallRange.Scale1 = scale1; + visWallRange.Scale2 = scale2; + visWallRange.ScaleStep = rwScaleStep; + + visWallRange.UpperClip = -1; + visWallRange.LowerClip = -1; + visWallRange.Silhouette = 0; + + if (frontSector.FloorHeight > backSector.FloorHeight) + { + visWallRange.Silhouette = Silhouette.Lower; + visWallRange.LowerSilHeight = frontSector.FloorHeight; + } + else if (backSector.FloorHeight > viewZ) + { + visWallRange.Silhouette = Silhouette.Lower; + visWallRange.LowerSilHeight = Fixed.MaxValue; + } + + if (frontSector.CeilingHeight < backSector.CeilingHeight) + { + visWallRange.Silhouette |= Silhouette.Upper; + visWallRange.UpperSilHeight = frontSector.CeilingHeight; + } + else if (backSector.CeilingHeight < viewZ) + { + visWallRange.Silhouette |= Silhouette.Upper; + visWallRange.UpperSilHeight = Fixed.MinValue; + } + + if (backSector.CeilingHeight <= frontSector.FloorHeight) + { + visWallRange.LowerClip = negOneArray; + visWallRange.LowerSilHeight = Fixed.MaxValue; + visWallRange.Silhouette |= Silhouette.Lower; + } + + if (backSector.FloorHeight >= frontSector.CeilingHeight) + { + visWallRange.UpperClip = windowHeightArray; + visWallRange.UpperSilHeight = Fixed.MinValue; + visWallRange.Silhouette |= Silhouette.Upper; + } + + var maskedTextureColumn = default(int); + if (drawMaskedTexture) + { + maskedTextureColumn = clipDataLength - x1; + visWallRange.MaskedTextureColumn = maskedTextureColumn; + clipDataLength += range; + } + else + { + visWallRange.MaskedTextureColumn = -1; + } + + // + // Floor and ceiling. + // + + var ceilingFlat = flats[world.Specials.FlatTranslation[frontSector.CeilingFlat]]; + var floorFlat = flats[world.Specials.FlatTranslation[frontSector.FloorFlat]]; + + // + // Now the rendering is carried out. + // + + for (var x = x1; x <= x2; x++) + { + var drawWallY1 = (wallY1Frac.Data + heightUnit - 1) >> heightBits; + var drawWallY2 = wallY2Frac.Data >> heightBits; + + var textureColumn = default(int); + var lightIndex = default(int); + var invScale = default(Fixed); + if (segTextured) + { + var angle = rwCenterAngle + xToAngle[x]; + angle = new Angle(angle.Data & 0x7FFFFFFF); + textureColumn = (rwOffset - Trig.Tan(angle) * rwDistance).ToIntFloor(); + + lightIndex = rwScale.Data >> scaleLightShift; + if (lightIndex >= maxScaleLight) + { + lightIndex = maxScaleLight - 1; + } + + invScale = new Fixed((int)(0xffffffffu / (uint)rwScale.Data)); + } + + if (drawUpperWall) + { + var drawUpperWallY1 = (wallY1Frac.Data + heightUnit - 1) >> heightBits; + var drawUpperWallY2 = portalY1Frac.Data >> heightBits; + + if (drawCeiling) + { + var cy1 = upperClip[x] + 1; + var cy2 = Math.Min(drawWallY1 - 1, lowerClip[x] - 1); + DrawCeilingColumn(frontSector, ceilingFlat, planeLights, x, cy1, cy2); + } + + var wy1 = Math.Max(drawUpperWallY1, upperClip[x] + 1); + var wy2 = Math.Min(drawUpperWallY2, lowerClip[x] - 1); + var source = upperWallTexture.Composite.Columns[textureColumn & upperWallWidthMask]; + if (source.Length > 0) + { + DrawColumn(source[0], wallLights[lightIndex], x, wy1, wy2, invScale, uperTextureAlt); + } + + if (upperClip[x] < wy2) + { + upperClip[x] = (short)wy2; + } + + portalY1Frac += portalY1Step; + } + else if (drawCeiling) + { + var cy1 = upperClip[x] + 1; + var cy2 = Math.Min(drawWallY1 - 1, lowerClip[x] - 1); + DrawCeilingColumn(frontSector, ceilingFlat, planeLights, x, cy1, cy2); + + if (upperClip[x] < cy2) + { + upperClip[x] = (short)cy2; + } + } + + if (drawLowerWall) + { + var drawLowerWallY1 = (portalY2Frac.Data + heightUnit - 1) >> heightBits; + var drawLowerWallY2 = wallY2Frac.Data >> heightBits; + + var wy1 = Math.Max(drawLowerWallY1, upperClip[x] + 1); + var wy2 = Math.Min(drawLowerWallY2, lowerClip[x] - 1); + var source = lowerWallTexture.Composite.Columns[textureColumn & lowerWallWidthMask]; + if (source.Length > 0) + { + DrawColumn(source[0], wallLights[lightIndex], x, wy1, wy2, invScale, lowerTextureAlt); + } + + if (drawFloor) + { + var fy1 = Math.Max(drawWallY2 + 1, upperClip[x] + 1); + var fy2 = lowerClip[x] - 1; + DrawFloorColumn(frontSector, floorFlat, planeLights, x, fy1, fy2); + } + + if (lowerClip[x] > wy1) + { + lowerClip[x] = (short)wy1; + } + + portalY2Frac += portalY2Step; + } + else if (drawFloor) + { + var fy1 = Math.Max(drawWallY2 + 1, upperClip[x] + 1); + var fy2 = lowerClip[x] - 1; + DrawFloorColumn(frontSector, floorFlat, planeLights, x, fy1, fy2); + + if (lowerClip[x] > drawWallY2 + 1) + { + lowerClip[x] = (short)fy1; + } + } + + if (drawMaskedTexture) + { + clipData[maskedTextureColumn + x] = (short)textureColumn; + } + + rwScale += rwScaleStep; + wallY1Frac += wallY1Step; + wallY2Frac += wallY2Step; + } + + // + // Save sprite clipping info. + // + + if (((visWallRange.Silhouette & Silhouette.Upper) != 0 || + drawMaskedTexture) && visWallRange.UpperClip == -1) + { + Array.Copy(upperClip, x1, clipData, clipDataLength, range); + visWallRange.UpperClip = clipDataLength - x1; + clipDataLength += range; + } + + if (((visWallRange.Silhouette & Silhouette.Lower) != 0 || + drawMaskedTexture) && visWallRange.LowerClip == -1) + { + Array.Copy(lowerClip, x1, clipData, clipDataLength, range); + visWallRange.LowerClip = clipDataLength - x1; + clipDataLength += range; + } + + if (drawMaskedTexture && (visWallRange.Silhouette & Silhouette.Upper) == 0) + { + visWallRange.Silhouette |= Silhouette.Upper; + visWallRange.UpperSilHeight = Fixed.MinValue; + } + + if (drawMaskedTexture && (visWallRange.Silhouette & Silhouette.Lower) == 0) + { + visWallRange.Silhouette |= Silhouette.Lower; + visWallRange.LowerSilHeight = Fixed.MaxValue; + } + } + + + + private void RenderMaskedTextures() + { + for (var i = visWallRangeCount - 1; i >= 0; i--) + { + var drawSeg = visWallRanges[i]; + if (drawSeg.MaskedTextureColumn != -1) + { + DrawMaskedRange(drawSeg, drawSeg.X1, drawSeg.X2); + } + } + } + + + + private void DrawMaskedRange(VisWallRange drawSeg, int x1, int x2) + { + var seg = drawSeg.Seg; + + var wallLightLevel = (seg.FrontSector.LightLevel >> lightSegShift) + extraLight; + if (seg.Vertex1.Y == seg.Vertex2.Y) + { + wallLightLevel--; + } + else if (seg.Vertex1.X == seg.Vertex2.X) + { + wallLightLevel++; + } + + var wallLights = scaleLight[Math.Clamp(wallLightLevel, 0, lightLevelCount - 1)]; + + var wallTexture = textures[world.Specials.TextureTranslation[seg.SideDef.MiddleTexture]]; + var mask = wallTexture.Width - 1; + + Fixed midTextureAlt; + if ((seg.LineDef.Flags & LineFlags.DontPegBottom) != 0) + { + midTextureAlt = seg.FrontSector.FloorHeight > seg.BackSector.FloorHeight + ? seg.FrontSector.FloorHeight : seg.BackSector.FloorHeight; + midTextureAlt = midTextureAlt + Fixed.FromInt(wallTexture.Height) - viewZ; + } + else + { + midTextureAlt = seg.FrontSector.CeilingHeight < seg.BackSector.CeilingHeight + ? seg.FrontSector.CeilingHeight : seg.BackSector.CeilingHeight; + midTextureAlt = midTextureAlt - viewZ; + } + midTextureAlt += seg.SideDef.RowOffset; + + var scaleStep = drawSeg.ScaleStep; + var scale = drawSeg.Scale1 + (x1 - drawSeg.X1) * scaleStep; + + for (var x = x1; x <= x2; x++) + { + var index = Math.Min(scale.Data >> scaleLightShift, maxScaleLight - 1); + + var col = clipData[drawSeg.MaskedTextureColumn + x]; + + if (col != short.MaxValue) + { + var topY = centerYFrac - midTextureAlt * scale; + var invScale = new Fixed((int)(0xffffffffu / (uint)scale.Data)); + var ceilClip = clipData[drawSeg.UpperClip + x]; + var floorClip = clipData[drawSeg.LowerClip + x]; + DrawMaskedColumn( + wallTexture.Composite.Columns[col & mask], + wallLights[index], + x, + topY, + scale, + invScale, + midTextureAlt, + ceilClip, + floorClip); + + clipData[drawSeg.MaskedTextureColumn + x] = short.MaxValue; + } + + scale += scaleStep; + } + } + + + + private void DrawCeilingColumn( + Sector sector, + Flat flat, + byte[][] planeLights, + int x, + int y1, + int y2) + { + if (flat == flats.SkyFlat) + { + DrawSkyColumn(x, y1, y2); + return; + } + + if (y2 - y1 < 0) + { + return; + } + + var height = Fixed.Abs(sector.CeilingHeight - viewZ); + + var flatData = flat.Data; + + if (sector == ceilingPrevSector && ceilingPrevX == x - 1) + { + var p1 = Math.Max(y1, ceilingPrevY1); + var p2 = Math.Min(y2, ceilingPrevY2); + + var pos = screenHeight * (windowX + x) + windowY + y1; + + for (var y = y1; y < p1; y++) + { + var distance = height * planeYSlope[y]; + ceilingXStep[y] = distance * planeBaseXScale; + ceilingYStep[y] = distance * planeBaseYScale; + + var length = distance * planeDistScale[x]; + var angle = viewAngle + xToAngle[x]; + var xFrac = viewX + Trig.Cos(angle) * length; + var yFrac = -viewY - Trig.Sin(angle) * length; + ceilingXFrac[y] = xFrac; + ceilingYFrac[y] = yFrac; + + var colorMap = planeLights[Math.Min((uint)(distance.Data >> zLightShift), maxZLight - 1)]; + ceilingLights[y] = colorMap; + + var spot = ((yFrac.Data >> (16 - 6)) & (63 * 64)) + ((xFrac.Data >> 16) & 63); + screenData[pos] = colorMap[flatData[spot]]; + pos++; + } + + for (var y = p1; y <= p2; y++) + { + var xFrac = ceilingXFrac[y] + ceilingXStep[y]; + var yFrac = ceilingYFrac[y] + ceilingYStep[y]; + + var spot = ((yFrac.Data >> (16 - 6)) & (63 * 64)) + ((xFrac.Data >> 16) & 63); + screenData[pos] = ceilingLights[y][flatData[spot]]; + pos++; + + ceilingXFrac[y] = xFrac; + ceilingYFrac[y] = yFrac; + } + + for (var y = p2 + 1; y <= y2; y++) + { + var distance = height * planeYSlope[y]; + ceilingXStep[y] = distance * planeBaseXScale; + ceilingYStep[y] = distance * planeBaseYScale; + + var length = distance * planeDistScale[x]; + var angle = viewAngle + xToAngle[x]; + var xFrac = viewX + Trig.Cos(angle) * length; + var yFrac = -viewY - Trig.Sin(angle) * length; + ceilingXFrac[y] = xFrac; + ceilingYFrac[y] = yFrac; + + var colorMap = planeLights[Math.Min((uint)(distance.Data >> zLightShift), maxZLight - 1)]; + ceilingLights[y] = colorMap; + + var spot = ((yFrac.Data >> (16 - 6)) & (63 * 64)) + ((xFrac.Data >> 16) & 63); + screenData[pos] = colorMap[flatData[spot]]; + pos++; + } + } + else + { + var pos = screenHeight * (windowX + x) + windowY + y1; + + for (var y = y1; y <= y2; y++) + { + var distance = height * planeYSlope[y]; + ceilingXStep[y] = distance * planeBaseXScale; + ceilingYStep[y] = distance * planeBaseYScale; + + var length = distance * planeDistScale[x]; + var angle = viewAngle + xToAngle[x]; + var xFrac = viewX + Trig.Cos(angle) * length; + var yFrac = -viewY - Trig.Sin(angle) * length; + ceilingXFrac[y] = xFrac; + ceilingYFrac[y] = yFrac; + + var colorMap = planeLights[Math.Min((uint)(distance.Data >> zLightShift), maxZLight - 1)]; + ceilingLights[y] = colorMap; + + var spot = ((yFrac.Data >> (16 - 6)) & (63 * 64)) + ((xFrac.Data >> 16) & 63); + screenData[pos] = colorMap[flatData[spot]]; + pos++; + } + } + + ceilingPrevSector = sector; + ceilingPrevX = x; + ceilingPrevY1 = y1; + ceilingPrevY2 = y2; + } + + private void DrawFloorColumn( + Sector sector, + Flat flat, + byte[][] planeLights, + int x, + int y1, + int y2) + { + if (flat == flats.SkyFlat) + { + DrawSkyColumn(x, y1, y2); + return; + } + + if (y2 - y1 < 0) + { + return; + } + + var height = Fixed.Abs(sector.FloorHeight - viewZ); + + var flatData = flat.Data; + + if (sector == floorPrevSector && floorPrevX == x - 1) + { + var p1 = Math.Max(y1, floorPrevY1); + var p2 = Math.Min(y2, floorPrevY2); + + var pos = screenHeight * (windowX + x) + windowY + y1; + + for (var y = y1; y < p1; y++) + { + var distance = height * planeYSlope[y]; + floorXStep[y] = distance * planeBaseXScale; + floorYStep[y] = distance * planeBaseYScale; + + var length = distance * planeDistScale[x]; + var angle = viewAngle + xToAngle[x]; + var xFrac = viewX + Trig.Cos(angle) * length; + var yFrac = -viewY - Trig.Sin(angle) * length; + floorXFrac[y] = xFrac; + floorYFrac[y] = yFrac; + + var colorMap = planeLights[Math.Min((uint)(distance.Data >> zLightShift), maxZLight - 1)]; + floorLights[y] = colorMap; + + var spot = ((yFrac.Data >> (16 - 6)) & (63 * 64)) + ((xFrac.Data >> 16) & 63); + screenData[pos] = colorMap[flatData[spot]]; + pos++; + } + + for (var y = p1; y <= p2; y++) + { + var xFrac = floorXFrac[y] + floorXStep[y]; + var yFrac = floorYFrac[y] + floorYStep[y]; + + var spot = ((yFrac.Data >> (16 - 6)) & (63 * 64)) + ((xFrac.Data >> 16) & 63); + screenData[pos] = floorLights[y][flatData[spot]]; + pos++; + + floorXFrac[y] = xFrac; + floorYFrac[y] = yFrac; + } + + for (var y = p2 + 1; y <= y2; y++) + { + var distance = height * planeYSlope[y]; + floorXStep[y] = distance * planeBaseXScale; + floorYStep[y] = distance * planeBaseYScale; + + var length = distance * planeDistScale[x]; + var angle = viewAngle + xToAngle[x]; + var xFrac = viewX + Trig.Cos(angle) * length; + var yFrac = -viewY - Trig.Sin(angle) * length; + floorXFrac[y] = xFrac; + floorYFrac[y] = yFrac; + + var colorMap = planeLights[Math.Min((uint)(distance.Data >> zLightShift), maxZLight - 1)]; + floorLights[y] = colorMap; + + var spot = ((yFrac.Data >> (16 - 6)) & (63 * 64)) + ((xFrac.Data >> 16) & 63); + screenData[pos] = colorMap[flatData[spot]]; + pos++; + } + } + else + { + var pos = screenHeight * (windowX + x) + windowY + y1; + + for (var y = y1; y <= y2; y++) + { + var distance = height * planeYSlope[y]; + floorXStep[y] = distance * planeBaseXScale; + floorYStep[y] = distance * planeBaseYScale; + + var length = distance * planeDistScale[x]; + var angle = viewAngle + xToAngle[x]; + var xFrac = viewX + Trig.Cos(angle) * length; + var yFrac = -viewY - Trig.Sin(angle) * length; + floorXFrac[y] = xFrac; + floorYFrac[y] = yFrac; + + var colorMap = planeLights[Math.Min((uint)(distance.Data >> zLightShift), maxZLight - 1)]; + floorLights[y] = colorMap; + + var spot = ((yFrac.Data >> (16 - 6)) & (63 * 64)) + ((xFrac.Data >> 16) & 63); + screenData[pos] = colorMap[flatData[spot]]; + pos++; + } + } + + floorPrevSector = sector; + floorPrevX = x; + floorPrevY1 = y1; + floorPrevY2 = y2; + } + + + + private void DrawColumn( + Column column, + byte[] map, + int x, + int y1, + int y2, + Fixed invScale, + Fixed textureAlt) + { + if (y2 - y1 < 0) + { + return; + } + // var watch = System.Diagnostics.Stopwatch.StartNew(); + + + // Framebuffer destination address. + // Use ylookup LUT to avoid multiply with ScreenWidth. + // Use columnofs LUT for subwindows? + var pos1 = screenHeight * (windowX + x) + windowY + y1; + var pos2 = pos1 + (y2 - y1); + + // Determine scaling, which is the only mapping to be done. + var fracStep = invScale; + var frac = textureAlt + (y1 - centerY) * fracStep; + + // Inner loop that does the actual texture mapping, + // e.g. a DDA-lile scaling. + // This is as fast as it gets. + var source = column.Data; + var offset = column.Offset; + for (var pos = pos1; pos <= pos2; pos++) + { + // Re-map color indices from wall texture column + // using a lighting/special effects LUT. + screenData[pos] = map[source[offset + ((frac.Data >> Fixed.FracBits) & 127)]]; + frac += fracStep; + } + //Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine($"ThreeDRenderer.drwColumn. x:{x}, y1: {y1}, y2: {y2}. {watch.ElapsedMilliseconds} ms"); + + } + + private void DrawColumnTranslation( + Column column, + byte[] translation, + byte[] map, + int x, + int y1, + int y2, + Fixed invScale, + Fixed textureAlt) + { + if (y2 - y1 < 0) + { + return; + } + + // Framebuffer destination address. + // Use ylookup LUT to avoid multiply with ScreenWidth. + // Use columnofs LUT for subwindows? + var pos1 = screenHeight * (windowX + x) + windowY + y1; + var pos2 = pos1 + (y2 - y1); + + // Determine scaling, which is the only mapping to be done. + var fracStep = invScale; + var frac = textureAlt + (y1 - centerY) * fracStep; + + // Inner loop that does the actual texture mapping, + // e.g. a DDA-lile scaling. + // This is as fast as it gets. + var source = column.Data; + var offset = column.Offset; + for (var pos = pos1; pos <= pos2; pos++) + { + // Re-map color indices from wall texture column + // using a lighting/special effects LUT. + screenData[pos] = map[translation[source[offset + ((frac.Data >> Fixed.FracBits) & 127)]]]; + frac += fracStep; + } + } + + private void DrawFuzzColumn( + Column column, + int x, + int y1, + int y2) + { + if (y2 - y1 < 0) + { + return; + } + + if (y1 == 0) + { + y1 = 1; + } + + if (y2 == windowHeight - 1) + { + y2 = windowHeight - 2; + } + + var pos1 = screenHeight * (windowX + x) + windowY + y1; + var pos2 = pos1 + (y2 - y1); + + var map = colorMap[6]; + for (var pos = pos1; pos <= pos2; pos++) + { + screenData[pos] = map[screenData[pos + fuzzTable[fuzzPos]]]; + + if (++fuzzPos == fuzzTable.Length) + { + fuzzPos = 0; + } + } + } + + private void DrawSkyColumn(int x, int y1, int y2) + { + var angle = (viewAngle + xToAngle[x]).Data >> angleToSkyShift; + var mask = world.Map.SkyTexture.Width - 1; + var source = world.Map.SkyTexture.Composite.Columns[angle & mask]; + DrawColumn(source[0], colorMap[0], x, y1, y2, skyInvScale, skyTextureAlt); + } + + private void DrawMaskedColumn( + Column[] columns, + byte[] map, + int x, + Fixed topY, + Fixed scale, + Fixed invScale, + Fixed textureAlt, + int upperClip, + int lowerClip) + { + foreach (var column in columns) + { + var y1Frac = topY + scale * column.TopDelta; + var y2Frac = y1Frac + scale * column.Length; + var y1 = (y1Frac.Data + Fixed.FracUnit - 1) >> Fixed.FracBits; + var y2 = (y2Frac.Data - 1) >> Fixed.FracBits; + + y1 = Math.Max(y1, upperClip + 1); + y2 = Math.Min(y2, lowerClip - 1); + + if (y1 <= y2) + { + var alt = new Fixed(textureAlt.Data - (column.TopDelta << Fixed.FracBits)); + DrawColumn(column, map, x, y1, y2, invScale, alt); + } + } + } + + private void DrawMaskedColumnTranslation( + Column[] columns, + byte[] translation, + byte[] map, + int x, + Fixed topY, + Fixed scale, + Fixed invScale, + Fixed textureAlt, + int upperClip, + int lowerClip) + { + foreach (var column in columns) + { + var y1Frac = topY + scale * column.TopDelta; + var y2Frac = y1Frac + scale * column.Length; + var y1 = (y1Frac.Data + Fixed.FracUnit - 1) >> Fixed.FracBits; + var y2 = (y2Frac.Data - 1) >> Fixed.FracBits; + + y1 = Math.Max(y1, upperClip + 1); + y2 = Math.Min(y2, lowerClip - 1); + + if (y1 <= y2) + { + var alt = new Fixed(textureAlt.Data - (column.TopDelta << Fixed.FracBits)); + DrawColumnTranslation(column, translation, map, x, y1, y2, invScale, alt); + } + } + } + + private void DrawMaskedFuzzColumn( + Column[] columns, + int x, + Fixed topY, + Fixed scale, + int upperClip, + int lowerClip) + { + foreach (var column in columns) + { + var y1Frac = topY + scale * column.TopDelta; + var y2Frac = y1Frac + scale * column.Length; + var y1 = (y1Frac.Data + Fixed.FracUnit - 1) >> Fixed.FracBits; + var y2 = (y2Frac.Data - 1) >> Fixed.FracBits; + + y1 = Math.Max(y1, upperClip + 1); + y2 = Math.Min(y2, lowerClip - 1); + + if (y1 <= y2) + { + DrawFuzzColumn(column, x, y1, y2); + } + } + } + + + + private void AddSprites(Sector sector, int validCount) + { + // BSP is traversed by subsector. + // A sector might have been split into several subsectors during BSP building. + // Thus we check whether its already added. + if (sector.ValidCount == validCount) + { + return; + } + + // Well, now it will be done. + sector.ValidCount = validCount; + + var spriteLightLevel = (sector.LightLevel >> lightSegShift) + extraLight; + var spriteLights = scaleLight[Math.Clamp(spriteLightLevel, 0, lightLevelCount - 1)]; + + // Handle all things in sector. + foreach (var thing in sector) + { + ProjectSprite(thing, spriteLights); + } + } + + private void ProjectSprite(Mobj thing, byte[][] spriteLights) + { + if (visSpriteCount == visSprites.Length) + { + // Too many sprites. + return; + } + + // Transform the origin point. + var trX = thing.X - viewX; + var trY = thing.Y - viewY; + + var gxt = (trX * viewCos); + var gyt = -(trY * viewSin); + + var tz = gxt - gyt; + + // Thing is behind view plane? + if (tz < minZ) + { + return; + } + + var xScale = projection / tz; + + gxt = -trX * viewSin; + gyt = trY * viewCos; + var tx = -(gyt + gxt); + + // Too far off the side? + if (Fixed.Abs(tx) > (tz << 2)) + { + return; + } + + var spriteDef = sprites[thing.Sprite]; + var frameNumber = thing.Frame & 0x7F; + var spriteFrame = spriteDef.Frames[frameNumber]; + + Patch lump; + bool flip; + if (spriteFrame.Rotate) + { + // Choose a different rotation based on player view. + var ang = Geometry.PointToAngle(viewX, viewY, thing.X, thing.Y); + var rot = (ang.Data - thing.Angle.Data + (uint)(Angle.Ang45.Data / 2) * 9) >> 29; + lump = spriteFrame.Patches[rot]; + flip = spriteFrame.Flip[rot]; + } + else + { + // Use single rotation for all views. + lump = spriteFrame.Patches[0]; + flip = spriteFrame.Flip[0]; + } + + // Calculate edges of the shape. + tx -= Fixed.FromInt(lump.LeftOffset); + var x1 = (centerXFrac + (tx * xScale)).Data >> Fixed.FracBits; + + // Off the right side? + if (x1 > windowWidth) + { + return; + } + + tx += Fixed.FromInt(lump.Width); + var x2 = ((centerXFrac + (tx * xScale)).Data >> Fixed.FracBits) - 1; + + // Off the left side? + if (x2 < 0) + { + return; + } + + // Store information in a vissprite. + var vis = visSprites[visSpriteCount]; + visSpriteCount++; + + vis.MobjFlags = thing.Flags; + vis.Scale = xScale; + vis.GlobalX = thing.X; + vis.GlobalY = thing.Y; + vis.GlobalBottomZ = thing.Z; + vis.GlobalTopZ = thing.Z + Fixed.FromInt(lump.TopOffset); + vis.TextureAlt = vis.GlobalTopZ - viewZ; + vis.X1 = x1 < 0 ? 0 : x1; + vis.X2 = x2 >= windowWidth ? windowWidth - 1 : x2; + + var invScale = Fixed.One / xScale; + + if (flip) + { + vis.StartFrac = new Fixed(Fixed.FromInt(lump.Width).Data - 1); + vis.InvScale = -invScale; + } + else + { + vis.StartFrac = Fixed.Zero; + vis.InvScale = invScale; + } + + if (vis.X1 > x1) + { + vis.StartFrac += vis.InvScale * (vis.X1 - x1); + } + + vis.Patch = lump; + + if (fixedColorMap == 0) + { + if ((thing.Frame & 0x8000) == 0) + { + vis.ColorMap = spriteLights[Math.Min(xScale.Data >> scaleLightShift, maxScaleLight - 1)]; + } + else + { + vis.ColorMap = colorMap.FullBright; + } + } + else + { + vis.ColorMap = colorMap[fixedColorMap]; + } + } + + private void RenderSprites() + { + for (var i = 0; i < visSpriteCount - 1; i++) + { + for (var j = i + 1; j > 0; j--) + { + if (visSprites[j - 1].Scale < visSprites[j].Scale) + { + var temp = visSprites[j - 1]; + visSprites[j - 1] = visSprites[j]; + visSprites[j] = temp; + } + } + } + + for (var i = visSpriteCount - 1; i >= 0; i--) + { + DrawSprite(visSprites[i]); + } + } + + private void DrawSprite(VisSprite sprite) + { + // var watch = System.Diagnostics.Stopwatch.StartNew(); + for (var x = sprite.X1; x <= sprite.X2; x++) + { + lowerClip[x] = -2; + upperClip[x] = -2; + } + + // Scan drawsegs from end to start for obscuring segs. + // The first drawseg that has a greater scale is the clip seg. + for (var i = visWallRangeCount - 1; i >= 0; i--) + { + var wall = visWallRanges[i]; + + // Determine if the drawseg obscures the sprite. + if (wall.X1 > sprite.X2 || + wall.X2 < sprite.X1 || + (wall.Silhouette == 0 && wall.MaskedTextureColumn == -1)) + { + // Does not cover sprite. + continue; + } + + var r1 = wall.X1 < sprite.X1 ? sprite.X1 : wall.X1; + var r2 = wall.X2 > sprite.X2 ? sprite.X2 : wall.X2; + + Fixed lowScale; + Fixed scale; + if (wall.Scale1 > wall.Scale2) + { + lowScale = wall.Scale2; + scale = wall.Scale1; + } + else + { + lowScale = wall.Scale1; + scale = wall.Scale2; + } + + if (scale < sprite.Scale || + (lowScale < sprite.Scale && + Geometry.PointOnSegSide(sprite.GlobalX, sprite.GlobalY, wall.Seg) == 0)) + { + // Masked mid texture? + if (wall.MaskedTextureColumn != -1) + { + DrawMaskedRange(wall, r1, r2); + } + // Seg is behind sprite. + continue; + } + + // Clip this piece of the sprite. + var silhouette = wall.Silhouette; + + if (sprite.GlobalBottomZ >= wall.LowerSilHeight) + { + silhouette &= ~Silhouette.Lower; + } + + if (sprite.GlobalTopZ <= wall.UpperSilHeight) + { + silhouette &= ~Silhouette.Upper; + } + + if (silhouette == Silhouette.Lower) + { + // Bottom sil. + for (var x = r1; x <= r2; x++) + { + if (lowerClip[x] == -2) + { + lowerClip[x] = clipData[wall.LowerClip + x]; + } + } + } + else if (silhouette == Silhouette.Upper) + { + // Top sil. + for (var x = r1; x <= r2; x++) + { + if (upperClip[x] == -2) + { + upperClip[x] = clipData[wall.UpperClip + x]; + } + } + } + else if (silhouette == Silhouette.Both) + { + // Both. + for (var x = r1; x <= r2; x++) + { + if (lowerClip[x] == -2) + { + lowerClip[x] = clipData[wall.LowerClip + x]; + } + if (upperClip[x] == -2) + { + upperClip[x] = clipData[wall.UpperClip + x]; + } + } + } + } + + // All clipping has been performed, so draw the sprite. + + // Check for unclipped columns. + for (var x = sprite.X1; x <= sprite.X2; x++) + { + if (lowerClip[x] == -2) + { + lowerClip[x] = (short)windowHeight; + } + if (upperClip[x] == -2) + { + upperClip[x] = -1; + } + } + + if ((sprite.MobjFlags & MobjFlags.Shadow) != 0) + { + var frac = sprite.StartFrac; + for (var x = sprite.X1; x <= sprite.X2; x++) + { + var textureColumn = frac.ToIntFloor(); + DrawMaskedFuzzColumn( + sprite.Patch.Columns[textureColumn], + x, + centerYFrac - (sprite.TextureAlt * sprite.Scale), + sprite.Scale, + upperClip[x], + lowerClip[x]); + frac += sprite.InvScale; + } + } + else if (((int)(sprite.MobjFlags & MobjFlags.Translation) >> (int)MobjFlags.TransShift) != 0) + { + byte[] translation; + switch (((int)(sprite.MobjFlags & MobjFlags.Translation) >> (int)MobjFlags.TransShift)) + { + case 1: + translation = greenToGray; + break; + case 2: + translation = greenToBrown; + break; + default: + translation = greenToRed; + break; + } + var frac = sprite.StartFrac; + for (var x = sprite.X1; x <= sprite.X2; x++) + { + var textureColumn = frac.ToIntFloor(); + DrawMaskedColumnTranslation( + sprite.Patch.Columns[textureColumn], + translation, + sprite.ColorMap, + x, + centerYFrac - (sprite.TextureAlt * sprite.Scale), + sprite.Scale, + Fixed.Abs(sprite.InvScale), + sprite.TextureAlt, + upperClip[x], + lowerClip[x]); + frac += sprite.InvScale; + } + } + else + { + var frac = sprite.StartFrac; + for (var x = sprite.X1; x <= sprite.X2; x++) + { + var textureColumn = frac.ToIntFloor(); + DrawMaskedColumn( + sprite.Patch.Columns[textureColumn], + sprite.ColorMap, + x, + centerYFrac - (sprite.TextureAlt * sprite.Scale), + sprite.Scale, + Fixed.Abs(sprite.InvScale), + sprite.TextureAlt, + upperClip[x], + lowerClip[x]); + frac += sprite.InvScale; + } + } + // Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Inside draw sprite {1} {0} ms", watch.ElapsedMilliseconds, sprite); + } + + + + private void DrawPlayerSprite(PlayerSpriteDef psp, byte[][] spriteLights, bool fuzz) + { + // Decide which patch to use. + var spriteDef = sprites[psp.State.Sprite]; + + var spriteFrame = spriteDef.Frames[psp.State.Frame & 0x7fff]; + + var lump = spriteFrame.Patches[0]; + var flip = spriteFrame.Flip[0]; + + // Calculate edges of the shape. + var tx = psp.Sx - Fixed.FromInt(160); + tx -= Fixed.FromInt(lump.LeftOffset); + var x1 = (centerXFrac + tx * weaponScale).Data >> Fixed.FracBits; + + // Off the right side? + if (x1 > windowWidth) + { + return; + } + + tx += Fixed.FromInt(lump.Width); + var x2 = ((centerXFrac + tx * weaponScale).Data >> Fixed.FracBits) - 1; + + // Off the left side? + if (x2 < 0) + { + return; + } + + // Store information in a vissprite. + var vis = weaponSprite; + vis.MobjFlags = 0; + // The code below is based on Crispy Doom's weapon rendering code. + vis.TextureAlt = Fixed.FromInt(100) + Fixed.One / 4 - (psp.Sy - Fixed.FromInt(lump.TopOffset)); + vis.X1 = x1 < 0 ? 0 : x1; + vis.X2 = x2 >= windowWidth ? windowWidth - 1 : x2; + vis.Scale = weaponScale; + + if (flip) + { + vis.InvScale = -weaponInvScale; + vis.StartFrac = Fixed.FromInt(lump.Width) - new Fixed(1); + } + else + { + vis.InvScale = weaponInvScale; + vis.StartFrac = Fixed.Zero; + } + + if (vis.X1 > x1) + { + vis.StartFrac += vis.InvScale * (vis.X1 - x1); + } + + vis.Patch = lump; + + if (fixedColorMap == 0) + { + if ((psp.State.Frame & 0x8000) == 0) + { + vis.ColorMap = spriteLights[maxScaleLight - 1]; + } + else + { + vis.ColorMap = colorMap.FullBright; + } + } + else + { + vis.ColorMap = colorMap[fixedColorMap]; + } + + if (fuzz) + { + var frac = vis.StartFrac; + for (var x = vis.X1; x <= vis.X2; x++) + { + var texturecolumn = frac.Data >> Fixed.FracBits; + DrawMaskedFuzzColumn( + vis.Patch.Columns[texturecolumn], + x, + centerYFrac - (vis.TextureAlt * vis.Scale), + vis.Scale, + -1, + windowHeight); + frac += vis.InvScale; + } + } + else + { + var frac = vis.StartFrac; + for (var x = vis.X1; x <= vis.X2; x++) + { + var texturecolumn = frac.Data >> Fixed.FracBits; + DrawMaskedColumn( + vis.Patch.Columns[texturecolumn], + vis.ColorMap, + x, + centerYFrac - (vis.TextureAlt * vis.Scale), + vis.Scale, + Fixed.Abs(vis.InvScale), + vis.TextureAlt, + -1, + windowHeight); + frac += vis.InvScale; + } + } + } + + + + private void DrawPlayerSprites(Player player) + { + // Get light level. + var spriteLightLevel = (player.Mobj.Subsector.Sector.LightLevel >> lightSegShift) + extraLight; + + byte[][] spriteLights; + if (spriteLightLevel < 0) + { + spriteLights = scaleLight[0]; + } + else if (spriteLightLevel >= lightLevelCount) + { + spriteLights = scaleLight[lightLevelCount - 1]; + } + else + { + spriteLights = scaleLight[spriteLightLevel]; + } + + bool fuzz; + if (player.Powers[(int)PowerType.Invisibility] > 4 * 32 || + (player.Powers[(int)PowerType.Invisibility] & 8) != 0) + { + // Shadow draw. + fuzz = true; + } + else + { + fuzz = false; + } + + // Add all active psprites. + for (var i = 0; i < (int)PlayerSprite.Count; i++) + { + var psp = player.PlayerSprites[i]; + if (psp.State != null) + { + DrawPlayerSprite(psp, spriteLights, fuzz); + } + } + } + + + + public int WindowSize + { + get + { + return windowSize; + } + + set + { + windowSize = value; + SetWindowSize(windowSize); + } + } + + + + private class ClipRange + { + public int First; + public int Last; + + public void CopyFrom(ClipRange range) + { + First = range.First; + Last = range.Last; + } + } + + private class VisWallRange + { + public Seg Seg; + + public int X1; + public int X2; + + public Fixed Scale1; + public Fixed Scale2; + public Fixed ScaleStep; + + public Silhouette Silhouette; + public Fixed UpperSilHeight; + public Fixed LowerSilHeight; + + public int UpperClip; + public int LowerClip; + public int MaskedTextureColumn; + } + + [Flags] + private enum Silhouette + { + Upper = 1, + Lower = 2, + Both = 3 + } + + private class VisSprite + { + public int X1; + public int X2; + + // For line side calculation. + public Fixed GlobalX; + public Fixed GlobalY; + + // Global bottom / top for silhouette clipping. + public Fixed GlobalBottomZ; + public Fixed GlobalTopZ; + + // Horizontal position of x1. + public Fixed StartFrac; + + public Fixed Scale; + + // Negative if flipped. + public Fixed InvScale; + + public Fixed TextureAlt; + public Patch Patch; + + // For color translation and shadow draw. + public byte[] ColorMap; + + public MobjFlags MobjFlags; + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/WipeEffect.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/WipeEffect.cs new file mode 100644 index 00000000..f7a2dbeb --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/SoftwareRendering/WipeEffect.cs @@ -0,0 +1,88 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.SoftwareRendering +{ + public sealed class WipeEffect + { + private short[] y; + private int height; + private DoomRandom random; + + public WipeEffect(int width, int height) + { + y = new short[width]; + this.height = height; + random = new DoomRandom(DateTime.Now.Millisecond); + } + + public void Start() + { + y[0] = (short)(-(random.Next() % 16)); + for (var i = 1; i < y.Length; i++) + { + var r = (random.Next() % 3) - 1; + y[i] = (short)(y[i - 1] + r); + if (y[i] > 0) + { + y[i] = 0; + } + else if (y[i] == -16) + { + y[i] = -15; + } + } + } + + public UpdateResult Update() + { + var done = true; + + for (var i = 0; i < y.Length; i++) + { + if (y[i] < 0) + { + y[i]++; + done = false; + } + else if (y[i] < height) + { + var dy = (y[i] < 16) ? y[i] + 1 : 8; + if (y[i] + dy >= height) + { + dy = height - y[i]; + } + y[i] += (short)dy; + done = false; + } + } + + if (done) + { + return UpdateResult.Completed; + } + else + { + return UpdateResult.None; + } + } + + public short[] Y => y; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomKey.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomKey.cs new file mode 100644 index 00000000..0c44cd89 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomKey.cs @@ -0,0 +1,995 @@ + +namespace ManagedDoom +{ + + public enum DoomKey + { + Unknown = -1, + /// + /// The bit mask to extract a key code from a key value. + /// + KeyCode = 0x0000FFFF, + + /// + /// The bit mask to extract modifiers from a key value. + /// + Modifiers = unchecked((int)0xFFFF0000), + + /// + /// No key pressed. + /// + None = 0x00, + + /// + /// The left mouse button. + /// + LButton = 0x01, + + /// + /// The right mouse button. + /// + RButton = 0x02, + + /// + /// The CANCEL key. + /// + Cancel = 0x03, + + /// + /// The middle mouse button (three-button mouse). + /// + MButton = 0x04, + + /// + /// The first x mouse button (five-button mouse). + /// + XButton1 = 0x05, + + /// + /// The second x mouse button (five-button mouse). + /// + XButton2 = 0x06, + + /// + /// The BACKSPACE key. + /// + Backspace = 0x08, + + /// + /// The TAB key. + /// + Tab = 0x09, + + /// + /// The CLEAR key. + /// + LineFeed = 0x0A, + + /// + /// The CLEAR key. + /// + Clear = 0x0C, + + /// + /// The RETURN key. + /// + Return = 0x0D, + + /// + /// The ENTER key. + /// + Enter = Return, + + /// + /// The SHIFT key. + /// + ShiftKey = 0x10, + + /// + /// The CTRL key. + /// + ControlKey = 0x11, + + /// + /// The ALT key. + /// + Menu = 0x12, + + /// + /// The PAUSE key. + /// + Pause = 0x13, + + /// + /// The CAPS LOCK key. + /// + Capital = 0x14, + + /// + /// The CAPS LOCK key. + /// + CapsLock = 0x14, + + /// + /// The IME Kana mode key. + /// + KanaMode = 0x15, + + /// + /// The IME Hanguel mode key. + /// + HanguelMode = 0x15, + + /// + /// The IME Hangul mode key. + /// + HangulMode = 0x15, + + /// + /// The IME Junja mode key. + /// + JunjaMode = 0x17, + + /// + /// The IME Final mode key. + /// + FinalMode = 0x18, + + /// + /// The IME Hanja mode key. + /// + HanjaMode = 0x19, + + /// + /// The IME Kanji mode key. + /// + KanjiMode = 0x19, + + /// + /// The ESC key. + /// + Escape = 0x1B, + + /// + /// The IME Convert key. + /// + IMEConvert = 0x1C, + + /// + /// The IME NonConvert key. + /// + IMENonconvert = 0x1D, + + /// + /// The IME Accept key. + /// + IMEAccept = 0x1E, + + /// + /// The IME Accept key. + /// + IMEAceept = IMEAccept, + + /// + /// The IME Mode change request. + /// + IMEModeChange = 0x1F, + + /// + /// The SPACEBAR key. + /// + Space = 0x20, + + /// + /// The PAGE UP key. + /// + Prior = 0x21, + + /// + /// The PAGE UP key. + /// + PageUp = Prior, + + /// + /// The PAGE DOWN key. + /// + Next = 0x22, + + /// + /// The PAGE DOWN key. + /// + PageDown = Next, + + /// + /// The END key. + /// + End = 0x23, + + /// + /// The HOME key. + /// + Home = 0x24, + + /// + /// The LEFT ARROW key. + /// + Left = 0x25, + + /// + /// The UP ARROW key. + /// + Up = 0x26, + + /// + /// The RIGHT ARROW key. + /// + Right = 0x27, + + /// + /// The DOWN ARROW key. + /// + Down = 0x28, + + /// + /// The SELECT key. + /// + Select = 0x29, + + /// + /// The PRINT key. + /// + Print = 0x2A, + + /// + /// The EXECUTE key. + /// + Execute = 0x2B, + + /// + /// The PRINT SCREEN key. + /// + Snapshot = 0x2C, + + /// + /// The PRINT SCREEN key. + /// + PrintScreen = Snapshot, + + /// + /// The INS key. + /// + Insert = 0x2D, + + /// + /// The DEL key. + /// + Delete = 0x2E, + + /// + /// The HELP key. + /// + Help = 0x2F, + + /// + /// The 0 key. + /// + D0 = 0x30, // 0 + + /// + /// The 1 key. + /// + D1 = 0x31, // 1 + + /// + /// The 2 key. + /// + D2 = 0x32, // 2 + + /// + /// The 3 key. + /// + D3 = 0x33, // 3 + + /// + /// The 4 key. + /// + D4 = 0x34, // 4 + + /// + /// The 5 key. + /// + D5 = 0x35, // 5 + + /// + /// The 6 key. + /// + D6 = 0x36, // 6 + + /// + /// The 7 key. + /// + D7 = 0x37, // 7 + + /// + /// The 8 key. + /// + D8 = 0x38, // 8 + + /// + /// The 9 key. + /// + D9 = 0x39, // 9 + + /// + /// The A key. + /// + A = 0x41, + + /// + /// The B key. + /// + B = 0x42, + + /// + /// The C key. + /// + C = 0x43, + + /// + /// The D key. + /// + D = 0x44, + + /// + /// The E key. + /// + E = 0x45, + + /// + /// The F key. + /// + F = 0x46, + + /// + /// The G key. + /// + G = 0x47, + + /// + /// The H key. + /// + H = 0x48, + + /// + /// The I key. + /// + I = 0x49, + + /// + /// The J key. + /// + J = 0x4A, + + /// + /// The K key. + /// + K = 0x4B, + + /// + /// The L key. + /// + L = 0x4C, + + /// + /// The M key. + /// + M = 0x4D, + + /// + /// The N key. + /// + N = 0x4E, + + /// + /// The O key. + /// + O = 0x4F, + + /// + /// The P key. + /// + P = 0x50, + + /// + /// The Q key. + /// + Q = 0x51, + + /// + /// The R key. + /// + R = 0x52, + + /// + /// The S key. + /// + S = 0x53, + + /// + /// The T key. + /// + T = 0x54, + + /// + /// The U key. + /// + U = 0x55, + + /// + /// The V key. + /// + V = 0x56, + + /// + /// The W key. + /// + W = 0x57, + + /// + /// The X key. + /// + X = 0x58, + + /// + /// The Y key. + /// + Y = 0x59, + + /// + /// The Z key. + /// + Z = 0x5A, + + /// + /// The left Windows logo key (Microsoft Natural Keyboard). + /// + LWin = 0x5B, + + /// + /// The right Windows logo key (Microsoft Natural Keyboard). + /// + RWin = 0x5C, + + /// + /// The Application key (Microsoft Natural Keyboard). + /// + Apps = 0x5D, + + /// + /// The Computer Sleep key. + /// + Sleep = 0x5F, + + /// + /// The 0 key on the numeric keypad. + /// + Numpad0 = 0x60, + + /// + /// The 1 key on the numeric keypad. + /// + Numpad1 = 0x61, + + /// + /// The 2 key on the numeric keypad. + /// + Numpad2 = 0x62, + + /// + /// The 3 key on the numeric keypad. + /// + Numpad3 = 0x63, + + /// + /// The 4 key on the numeric keypad. + /// + Numpad4 = 0x64, + + /// + /// The 5 key on the numeric keypad. + /// + Numpad5 = 0x65, + + /// + /// The 6 key on the numeric keypad. + /// + Numpad6 = 0x66, + + /// + /// The 7 key on the numeric keypad. + /// + Numpad7 = 0x67, + + /// + /// The 8 key on the numeric keypad. + /// + Numpad8 = 0x68, + + /// + /// The 9 key on the numeric keypad. + /// + Numpad9 = 0x69, + + /// + /// The Multiply key. + /// + Multiply = 0x6A, + + /// + /// The Add key. + /// + Add = 0x6B, + + /// + /// The Separator key. + /// + Separator = 0x6C, + + /// + /// The Subtract key. + /// + Subtract = 0x6D, + + /// + /// The Decimal key. + /// + Decimal = 0x6E, + + /// + /// The Divide key. + /// + Divide = 0x6F, + + /// + /// The F1 key. + /// + F1 = 0x70, + + /// + /// The F2 key. + /// + F2 = 0x71, + + /// + /// The F3 key. + /// + F3 = 0x72, + + /// + /// The F4 key. + /// + F4 = 0x73, + + /// + /// The F5 key. + /// + F5 = 0x74, + + /// + /// The F6 key. + /// + F6 = 0x75, + + /// + /// The F7 key. + /// + F7 = 0x76, + + /// + /// The F8 key. + /// + F8 = 0x77, + + /// + /// The F9 key. + /// + F9 = 0x78, + + /// + /// The F10 key. + /// + F10 = 0x79, + + /// + /// The F11 key. + /// + F11 = 0x7A, + + /// + /// The F12 key. + /// + F12 = 0x7B, + + /// + /// The F13 key. + /// + F13 = 0x7C, + + /// + /// The F14 key. + /// + F14 = 0x7D, + + /// + /// The F15 key. + /// + F15 = 0x7E, + + /// + /// The F16 key. + /// + F16 = 0x7F, + + /// + /// The F17 key. + /// + F17 = 0x80, + + /// + /// The F18 key. + /// + F18 = 0x81, + + /// + /// The F19 key. + /// + F19 = 0x82, + + /// + /// The F20 key. + /// + F20 = 0x83, + + /// + /// The F21 key. + /// + F21 = 0x84, + + /// + /// The F22 key. + /// + F22 = 0x85, + + /// + /// The F23 key. + /// + F23 = 0x86, + + /// + /// The F24 key. + /// + F24 = 0x87, + + /// + /// The NUM LOCK key. + /// + NumLock = 0x90, + + /// + /// The SCROLL LOCK key. + /// + Scroll = 0x91, + + /// + /// The left SHIFT key. + /// + LShift = 0xA0, + + /// + /// The right SHIFT key. + /// + RShift = 0xA1, + + /// + /// The left CTRL key. + /// + LControl = 0xA2, + + /// + /// The right CTRL key. + /// + RControl = 0xA3, + + /// + /// The left ALT key. + /// + LAlt = 0xA4, + + /// + /// The right ALT key. + /// + RAlt = 0xA5, + + /// + /// The Browser Back key. + /// + BrowserBack = 0xA6, + + /// + /// The Browser Forward key. + /// + BrowserForward = 0xA7, + + /// + /// The Browser Refresh key. + /// + BrowserRefresh = 0xA8, + + /// + /// The Browser Stop key. + /// + BrowserStop = 0xA9, + + /// + /// The Browser Search key. + /// + BrowserSearch = 0xAA, + + /// + /// The Browser Favorites key. + /// + BrowserFavorites = 0xAB, + + /// + /// The Browser Home key. + /// + BrowserHome = 0xAC, + + /// + /// The Volume Mute key. + /// + VolumeMute = 0xAD, + + /// + /// The Volume Down key. + /// + VolumeDown = 0xAE, + + /// + /// The Volume Up key. + /// + VolumeUp = 0xAF, + + /// + /// The Media Next Track key. + /// + MediaNextTrack = 0xB0, + + /// + /// The Media Previous Track key. + /// + MediaPreviousTrack = 0xB1, + + /// + /// The Media Stop key. + /// + MediaStop = 0xB2, + + /// + /// The Media Play Pause key. + /// + MediaPlayPause = 0xB3, + + /// + /// The Launch Mail key. + /// + LaunchMail = 0xB4, + + /// + /// The Select Media key. + /// + SelectMedia = 0xB5, + + /// + /// The Launch Application1 key. + /// + LaunchApplication1 = 0xB6, + + /// + /// The Launch Application2 key. + /// + LaunchApplication2 = 0xB7, + + /// + /// The Oem Semicolon key. + /// + Semicolon = 0xBA, + + /// + /// The Oem 1 key. + /// + Oem1 = Semicolon, + + /// + /// The Oem plus key. + /// + Oemplus = 0xBB, + + /// + /// The Oem comma key. + /// + Comma = 0xBC, + + /// + /// The Oem Minus key. + /// + OemMinus = 0xBD, + + /// + /// The Oem Period key. + /// + Period = 0xBE, + + /// + /// The Oem Question key. + /// + Question = 0xBF, + + /// + /// The Oem 2 key. + /// + Oem2 = Question, + + /// + /// The Oem tilde key. + /// + Tilde = 0xC0, + + /// + /// The Oem 3 key. + /// + Oem3 = Tilde, + + /// + /// The Oem Open Brackets key. + /// + LBracket = 0xDB, + + /// + /// The Oem 4 key. + /// + Oem4 = LBracket, + + /// + /// The Oem Pipe key. + /// + Pipe = 0xDC, + + /// + /// The Oem 5 key. + /// + Oem5 = Pipe, + + /// + /// The Oem Close Brackets key. + /// + RBracket = 0xDD, + + /// + /// The Oem 6 key. + /// + Oem6 = RBracket, + + /// + /// The Oem Quotes key. + /// + Quote = 0xDE, + + /// + /// The Oem 7 key. + /// + Oem7 = Quote, + + /// + /// The Oem8 key. + /// + Oem8 = 0xDF, + + /// + /// The Oem Backslash key. + /// + Backslash = 0xE2, + + /// + /// The Oem 102 key. + /// + Oem102 = Backslash, + + /// + /// The PROCESS KEY key. + /// + ProcessKey = 0xE5, + + /// + /// The Packet KEY key. + /// + Packet = 0xE7, + + /// + /// The ATTN key. + /// + Attn = 0xF6, + + /// + /// The CRSEL key. + /// + Crsel = 0xF7, + + /// + /// The EXSEL key. + /// + Exsel = 0xF8, + + /// + /// The ERASE EOF key. + /// + EraseEof = 0xF9, + + /// + /// The PLAY key. + /// + Play = 0xFA, + + /// + /// The ZOOM key. + /// + Zoom = 0xFB, + + /// + /// A constant reserved for future use. + /// + NoName = 0xFC, + + /// + /// The PA1 key. + /// + Pa1 = 0xFD, + + /// + /// The CLEAR key. + /// + OemClear = 0xFE, + + /// + /// The SHIFT modifier key. + /// + Shift = 0x00010000, + + /// + /// The CTRL modifier key. + /// + Control = 0x00020000, + + /// + /// The ALT modifier key. + /// + Alt = 0x00040000, + + // TODO: define: + Num0, + Num1, + Num2, + Num3, + Num4, + Num5, + Num6, + Num7, + Num8, + Num9, + RSystem, + Slash, + LSystem, + Equal, + Hyphen + } +} \ No newline at end of file diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomKeyEx.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomKeyEx.cs new file mode 100644 index 00000000..ba1bbdcf --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomKeyEx.cs @@ -0,0 +1,577 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static class DoomKeyEx + { + public static char GetChar(this DoomKey key) + { + switch (key) + { + case DoomKey.A: + return 'a'; + case DoomKey.B: + return 'b'; + case DoomKey.C: + return 'c'; + case DoomKey.D: + return 'd'; + case DoomKey.E: + return 'e'; + case DoomKey.F: + return 'f'; + case DoomKey.G: + return 'g'; + case DoomKey.H: + return 'h'; + case DoomKey.I: + return 'i'; + case DoomKey.J: + return 'j'; + case DoomKey.K: + return 'k'; + case DoomKey.L: + return 'l'; + case DoomKey.M: + return 'm'; + case DoomKey.N: + return 'n'; + case DoomKey.O: + return 'o'; + case DoomKey.P: + return 'p'; + case DoomKey.Q: + return 'q'; + case DoomKey.R: + return 'r'; + case DoomKey.S: + return 's'; + case DoomKey.T: + return 't'; + case DoomKey.U: + return 'u'; + case DoomKey.V: + return 'v'; + case DoomKey.W: + return 'w'; + case DoomKey.X: + return 'x'; + case DoomKey.Y: + return 'y'; + case DoomKey.Z: + return 'z'; + case DoomKey.Num0: + return '0'; + case DoomKey.Num1: + return '1'; + case DoomKey.Num2: + return '2'; + case DoomKey.Num3: + return '3'; + case DoomKey.Num4: + return '4'; + case DoomKey.Num5: + return '5'; + case DoomKey.Num6: + return '6'; + case DoomKey.Num7: + return '7'; + case DoomKey.Num8: + return '8'; + case DoomKey.Num9: + return '9'; + case DoomKey.LBracket: + return '['; + case DoomKey.RBracket: + return ']'; + case DoomKey.Semicolon: + return ';'; + case DoomKey.Comma: + return ','; + case DoomKey.Period: + return '.'; + case DoomKey.Quote: + return '"'; + case DoomKey.Slash: + return '/'; + case DoomKey.Backslash: + return '\\'; + case DoomKey.Equal: + return '='; + case DoomKey.Hyphen: + return '-'; + case DoomKey.Space: + return ' '; + case DoomKey.Add: + return '+'; + case DoomKey.Subtract: + return '-'; + case DoomKey.Multiply: + return '*'; + case DoomKey.Divide: + return '/'; + case DoomKey.Numpad0: + return '0'; + case DoomKey.Numpad1: + return '1'; + case DoomKey.Numpad2: + return '2'; + case DoomKey.Numpad3: + return '3'; + case DoomKey.Numpad4: + return '4'; + case DoomKey.Numpad5: + return '5'; + case DoomKey.Numpad6: + return '6'; + case DoomKey.Numpad7: + return '7'; + case DoomKey.Numpad8: + return '8'; + case DoomKey.Numpad9: + return '9'; + default: + return '\0'; + } + } + + public static string ToString(DoomKey key) + { + switch (key) + { + case DoomKey.A: + return "a"; + case DoomKey.B: + return "b"; + case DoomKey.C: + return "c"; + case DoomKey.D: + return "d"; + case DoomKey.E: + return "e"; + case DoomKey.F: + return "f"; + case DoomKey.G: + return "g"; + case DoomKey.H: + return "h"; + case DoomKey.I: + return "i"; + case DoomKey.J: + return "j"; + case DoomKey.K: + return "k"; + case DoomKey.L: + return "l"; + case DoomKey.M: + return "m"; + case DoomKey.N: + return "n"; + case DoomKey.O: + return "o"; + case DoomKey.P: + return "p"; + case DoomKey.Q: + return "q"; + case DoomKey.R: + return "r"; + case DoomKey.S: + return "s"; + case DoomKey.T: + return "t"; + case DoomKey.U: + return "u"; + case DoomKey.V: + return "v"; + case DoomKey.W: + return "w"; + case DoomKey.X: + return "x"; + case DoomKey.Y: + return "y"; + case DoomKey.Z: + return "z"; + case DoomKey.Num0: + return "num0"; + case DoomKey.Num1: + return "num1"; + case DoomKey.Num2: + return "num2"; + case DoomKey.Num3: + return "num3"; + case DoomKey.Num4: + return "num4"; + case DoomKey.Num5: + return "num5"; + case DoomKey.Num6: + return "num6"; + case DoomKey.Num7: + return "num7"; + case DoomKey.Num8: + return "num8"; + case DoomKey.Num9: + return "num9"; + case DoomKey.Escape: + return "escape"; + case DoomKey.LControl: + return "lcontrol"; + case DoomKey.LShift: + return "lshift"; + case DoomKey.LAlt: + return "lalt"; + case DoomKey.LSystem: + return "lsystem"; + case DoomKey.RControl: + return "rcontrol"; + case DoomKey.RShift: + return "rshift"; + case DoomKey.RAlt: + return "ralt"; + case DoomKey.RSystem: + return "rsystem"; + case DoomKey.Menu: + return "menu"; + case DoomKey.LBracket: + return "lbracket"; + case DoomKey.RBracket: + return "rbracket"; + case DoomKey.Semicolon: + return "semicolon"; + case DoomKey.Comma: + return "comma"; + case DoomKey.Period: + return "period"; + case DoomKey.Quote: + return "quote"; + case DoomKey.Slash: + return "slash"; + case DoomKey.Backslash: + return "backslash"; + case DoomKey.Tilde: + return "tilde"; + case DoomKey.Equal: + return "equal"; + case DoomKey.Hyphen: + return "hyphen"; + case DoomKey.Space: + return "space"; + case DoomKey.Enter: + return "enter"; + case DoomKey.Backspace: + return "backspace"; + case DoomKey.Tab: + return "tab"; + case DoomKey.PageUp: + return "pageup"; + case DoomKey.PageDown: + return "pagedown"; + case DoomKey.End: + return "end"; + case DoomKey.Home: + return "home"; + case DoomKey.Insert: + return "insert"; + case DoomKey.Delete: + return "delete"; + case DoomKey.Add: + return "add"; + case DoomKey.Subtract: + return "subtract"; + case DoomKey.Multiply: + return "multiply"; + case DoomKey.Divide: + return "divide"; + case DoomKey.Left: + return "left"; + case DoomKey.Right: + return "right"; + case DoomKey.Up: + return "up"; + case DoomKey.Down: + return "down"; + case DoomKey.Numpad0: + return "numpad0"; + case DoomKey.Numpad1: + return "numpad1"; + case DoomKey.Numpad2: + return "numpad2"; + case DoomKey.Numpad3: + return "numpad3"; + case DoomKey.Numpad4: + return "numpad4"; + case DoomKey.Numpad5: + return "numpad5"; + case DoomKey.Numpad6: + return "numpad6"; + case DoomKey.Numpad7: + return "numpad7"; + case DoomKey.Numpad8: + return "numpad8"; + case DoomKey.Numpad9: + return "numpad9"; + case DoomKey.F1: + return "f1"; + case DoomKey.F2: + return "f2"; + case DoomKey.F3: + return "f3"; + case DoomKey.F4: + return "f4"; + case DoomKey.F5: + return "f5"; + case DoomKey.F6: + return "f6"; + case DoomKey.F7: + return "f7"; + case DoomKey.F8: + return "f8"; + case DoomKey.F9: + return "f9"; + case DoomKey.F10: + return "f10"; + case DoomKey.F11: + return "f11"; + case DoomKey.F12: + return "f12"; + case DoomKey.F13: + return "f13"; + case DoomKey.F14: + return "f14"; + case DoomKey.F15: + return "f15"; + case DoomKey.Pause: + return "pause"; + default: + return "unknown"; + } + } + + public static DoomKey Parse(string value) + { + switch (value) + { + case "a": + return DoomKey.A; + case "b": + return DoomKey.B; + case "c": + return DoomKey.C; + case "d": + return DoomKey.D; + case "e": + return DoomKey.E; + case "f": + return DoomKey.F; + case "g": + return DoomKey.G; + case "h": + return DoomKey.H; + case "i": + return DoomKey.I; + case "j": + return DoomKey.J; + case "k": + return DoomKey.K; + case "l": + return DoomKey.L; + case "m": + return DoomKey.M; + case "n": + return DoomKey.N; + case "o": + return DoomKey.O; + case "p": + return DoomKey.P; + case "q": + return DoomKey.Q; + case "r": + return DoomKey.R; + case "s": + return DoomKey.S; + case "t": + return DoomKey.T; + case "u": + return DoomKey.U; + case "v": + return DoomKey.V; + case "w": + return DoomKey.W; + case "x": + return DoomKey.X; + case "y": + return DoomKey.Y; + case "z": + return DoomKey.Z; + case "num0": + return DoomKey.Num0; + case "num1": + return DoomKey.Num1; + case "num2": + return DoomKey.Num2; + case "num3": + return DoomKey.Num3; + case "num4": + return DoomKey.Num4; + case "num5": + return DoomKey.Num5; + case "num6": + return DoomKey.Num6; + case "num7": + return DoomKey.Num7; + case "num8": + return DoomKey.Num8; + case "num9": + return DoomKey.Num9; + case "escape": + return DoomKey.Escape; + case "lcontrol": + return DoomKey.LControl; + case "lshift": + return DoomKey.LShift; + case "lalt": + return DoomKey.LAlt; + case "lsystem": + return DoomKey.LSystem; + case "rcontrol": + return DoomKey.RControl; + case "rshift": + return DoomKey.RShift; + case "ralt": + return DoomKey.RAlt; + case "rsystem": + return DoomKey.RSystem; + case "menu": + return DoomKey.Menu; + case "lbracket": + return DoomKey.LBracket; + case "rbracket": + return DoomKey.RBracket; + case "semicolon": + return DoomKey.Semicolon; + case "comma": + return DoomKey.Comma; + case "period": + return DoomKey.Period; + case "quote": + return DoomKey.Quote; + case "slash": + return DoomKey.Slash; + case "backslash": + return DoomKey.Backslash; + case "tilde": + return DoomKey.Tilde; + case "equal": + return DoomKey.Equal; + case "hyphen": + return DoomKey.Hyphen; + case "space": + return DoomKey.Space; + case "enter": + return DoomKey.Enter; + case "backspace": + return DoomKey.Backspace; + case "tab": + return DoomKey.Tab; + case "pageup": + return DoomKey.PageUp; + case "pagedown": + return DoomKey.PageDown; + case "end": + return DoomKey.End; + case "home": + return DoomKey.Home; + case "insert": + return DoomKey.Insert; + case "delete": + return DoomKey.Delete; + case "add": + return DoomKey.Add; + case "subtract": + return DoomKey.Subtract; + case "multiply": + return DoomKey.Multiply; + case "divide": + return DoomKey.Divide; + case "left": + return DoomKey.Left; + case "right": + return DoomKey.Right; + case "up": + return DoomKey.Up; + case "down": + return DoomKey.Down; + case "numpad0": + return DoomKey.Numpad0; + case "numpad1": + return DoomKey.Numpad1; + case "numpad2": + return DoomKey.Numpad2; + case "numpad3": + return DoomKey.Numpad3; + case "numpad4": + return DoomKey.Numpad4; + case "numpad5": + return DoomKey.Numpad5; + case "numpad6": + return DoomKey.Numpad6; + case "numpad7": + return DoomKey.Numpad7; + case "numpad8": + return DoomKey.Numpad8; + case "numpad9": + return DoomKey.Numpad9; + case "f1": + return DoomKey.F1; + case "f2": + return DoomKey.F2; + case "f3": + return DoomKey.F3; + case "f4": + return DoomKey.F4; + case "f5": + return DoomKey.F5; + case "f6": + return DoomKey.F6; + case "f7": + return DoomKey.F7; + case "f8": + return DoomKey.F8; + case "f9": + return DoomKey.F9; + case "f10": + return DoomKey.F10; + case "f11": + return DoomKey.F11; + case "f12": + return DoomKey.F12; + case "f13": + return DoomKey.F13; + case "f14": + return DoomKey.F14; + case "f15": + return DoomKey.F15; + case "pause": + return DoomKey.Pause; + default: + return DoomKey.Unknown; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomMouseButton.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomMouseButton.cs new file mode 100644 index 00000000..0a775bdc --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomMouseButton.cs @@ -0,0 +1,32 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public enum DoomMouseButton + { + Unknown = -1, + Mouse1 = 0, + Mouse2, + Mouse3, + Mouse4, + Mouse5, + Count + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomMouseButtonEx.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomMouseButtonEx.cs new file mode 100644 index 00000000..5a4b3179 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/DoomMouseButtonEx.cs @@ -0,0 +1,62 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + public static class DoomMouseButtonEx + { + public static string ToString(DoomMouseButton button) + { + switch (button) + { + case DoomMouseButton.Mouse1: + return "mouse1"; + case DoomMouseButton.Mouse2: + return "mouse2"; + case DoomMouseButton.Mouse3: + return "mouse3"; + case DoomMouseButton.Mouse4: + return "mouse4"; + case DoomMouseButton.Mouse5: + return "mouse5"; + default: + return "unknown"; + } + } + + public static DoomMouseButton Parse(string value) + { + switch (value) + { + case "mouse1": + return DoomMouseButton.Mouse1; + case "mouse2": + return DoomMouseButton.Mouse2; + case "mouse3": + return DoomMouseButton.Mouse3; + case "mouse4": + return DoomMouseButton.Mouse4; + case "mouse5": + return DoomMouseButton.Mouse5; + default: + return DoomMouseButton.Unknown; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/IUserInput.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/IUserInput.cs new file mode 100644 index 00000000..6ce4b037 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/IUserInput.cs @@ -0,0 +1,30 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.UserInput +{ + public interface IUserInput + { + void BuildTicCmd(TicCmd cmd); + void Reset(); + + public int MaxMouseSensitivity { get; } + public int MouseSensitivity { get; set; } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/KeyBinding.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/KeyBinding.cs new file mode 100644 index 00000000..269354e9 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/KeyBinding.cs @@ -0,0 +1,98 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace ManagedDoom +{ + public sealed class KeyBinding + { + public static readonly KeyBinding Empty = new KeyBinding(); + + private DoomKey[] keys; + private DoomMouseButton[] mouseButtons; + + private KeyBinding() + { + keys = Array.Empty(); + mouseButtons = Array.Empty(); + } + + public KeyBinding(IReadOnlyList keys) + { + this.keys = keys.ToArray(); + this.mouseButtons = Array.Empty(); + } + + public KeyBinding(IReadOnlyList keys, IReadOnlyList mouseButtons) + { + this.keys = keys.ToArray(); + this.mouseButtons = mouseButtons.ToArray(); + } + + public override string ToString() + { + var keyValues = keys.Select(key => DoomKeyEx.ToString(key)); + var mouseValues = mouseButtons.Select(button => DoomMouseButtonEx.ToString(button)); + var values = keyValues.Concat(mouseValues).ToArray(); + if (values.Length > 0) + { + return string.Join(", ", values); + } + else + { + return "none"; + } + } + + public static KeyBinding Parse(string value) + { + if (value == "none") + { + return Empty; + } + + var keys = new List(); + var mouseButtons = new List(); + + var split = value.Split(',').Select(x => x.Trim()); + foreach (var s in split) + { + var key = DoomKeyEx.Parse(s); + if (key != DoomKey.Unknown) + { + keys.Add(key); + continue; + } + + var mouseButton = DoomMouseButtonEx.Parse(s); + if (mouseButton != DoomMouseButton.Unknown) + { + mouseButtons.Add(mouseButton); + } + } + + return new KeyBinding(keys, mouseButtons); + } + + public IReadOnlyList Keys => keys; + public IReadOnlyList MouseButtons => mouseButtons; + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/NullUserInput.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/NullUserInput.cs new file mode 100644 index 00000000..f1647703 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/NullUserInput.cs @@ -0,0 +1,64 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom.UserInput +{ + public sealed class NullUserInput : IUserInput + { + private static NullUserInput instance; + + public static NullUserInput GetInstance() + { + if (instance == null) + { + instance = new NullUserInput(); + } + + return instance; + } + + public void BuildTicCmd(TicCmd cmd) + { + } + + public void Reset() + { + } + + public int MaxMouseSensitivity + { + get + { + return 9; + } + } + + public int MouseSensitivity + { + get + { + return 3; + } + + set + { + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/OldDoomKey.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/OldDoomKey.cs new file mode 100644 index 00000000..93592a4d --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/OldDoomKey.cs @@ -0,0 +1,129 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; + +namespace ManagedDoom +{ + + public enum OldDoomKey + { + Unknown = -1, + A = 0, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + Num0, + Num1, + Num2, + Num3, + Num4, + Num5, + Num6, + Num7, + Num8, + Num9, + Escape, + LControl, + LShift, + LAlt, + LSystem, + RControl, + RShift, + RAlt, + RSystem, + Menu, + LBracket, + RBracket, + Semicolon, + Comma, + Period, + Quote, + Slash, + Backslash, + Tilde, + Equal, + Hyphen, + Space, + Enter, + Backspace, + Tab, + PageUp, + PageDown, + End, + Home, + Insert, + Delete, + Add, + Subtract, + Multiply, + Divide, + Left, + Right, + Up, + Down, + Numpad0, + Numpad1, + Numpad2, + Numpad3, + Numpad4, + Numpad5, + Numpad6, + Numpad7, + Numpad8, + Numpad9, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + Pause, + Count + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/SfmlUserInput.cs b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/SfmlUserInput.cs new file mode 100644 index 00000000..aebd17d6 --- /dev/null +++ b/SRC/Aura_OS/System/Processing/Application/Doom/ManagedDoom/UserInput/SfmlUserInput.cs @@ -0,0 +1,323 @@ +// +// Copyright (C) 1993-1996 Id Software, Inc. +// Copyright (C) 2019-2020 Nobuaki Tanaka +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// + + + +using System; +using System.Runtime.ExceptionServices; +using SFML.Graphics; +using SFML.System; +using SFML.Window; + +namespace ManagedDoom.UserInput +{ + public sealed class SfmlUserInput : IUserInput, IDisposable + { + private Config config; + + private RenderWindow window; + + private bool useMouse; + + private bool[] weaponKeys; + private int turnHeld; + + private int windowCenterX; + private int windowCenterY; + private int mouseX; + private int mouseY; + private bool cursorCentered; + + public SfmlUserInput(Config config, RenderWindow window, bool useMouse) + { + try + { + Aura_OS.System.Processing.Application.DoomApp.debugger.Write("Initialize user input: "); + + this.config = config; + + config.mouse_sensitivity = Math.Max(config.mouse_sensitivity, 0); + + this.window = window; + + this.useMouse = useMouse; + + weaponKeys = new bool[7]; + turnHeld = 0; + + windowCenterX = (int)window.Size.X / 2; + windowCenterY = (int)window.Size.Y / 2; + mouseX = 0; + mouseY = 0; + cursorCentered = false; + + if (useMouse) + { + window.SetMouseCursorGrabbed(true); + window.SetMouseCursorVisible(false); + } + + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("OK"); + } + catch (Exception e) + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Failed"); + Dispose(); + ExceptionDispatchInfo.Throw(e); + } + } + + public void BuildTicCmd(TicCmd cmd) + { + var keyForward = IsPressed(config.key_forward); + var keyBackward = IsPressed(config.key_backward); + var keyStrafeLeft = IsPressed(config.key_strafeleft); + var keyStrafeRight = IsPressed(config.key_straferight); + var keyTurnLeft = IsPressed(config.key_turnleft); + var keyTurnRight = IsPressed(config.key_turnright); + var keyFire = IsPressed(config.key_fire); + var keyUse = IsPressed(config.key_use); + var keyRun = IsPressed(config.key_run); + var keyStrafe = IsPressed(config.key_strafe); + + weaponKeys[0] = DoomApplication.IsKeyPressed(DoomKey.Num1); + weaponKeys[1] = DoomApplication.IsKeyPressed(DoomKey.Num2); + weaponKeys[2] = DoomApplication.IsKeyPressed(DoomKey.Num3); + weaponKeys[3] = DoomApplication.IsKeyPressed(DoomKey.Num4); + weaponKeys[4] = DoomApplication.IsKeyPressed(DoomKey.Num5); + weaponKeys[5] = DoomApplication.IsKeyPressed(DoomKey.Num6); + weaponKeys[6] = DoomApplication.IsKeyPressed(DoomKey.Num7); + + cmd.Clear(); + + var strafe = keyStrafe; + var speed = keyRun ? 1 : 0; + var forward = 0; + var side = 0; + + if (config.game_alwaysrun) + { + speed = 1 - speed; + } + + if (keyTurnLeft || keyTurnRight) + { + turnHeld++; + } + else + { + turnHeld = 0; + } + + int turnSpeed; + if (turnHeld < PlayerBehavior.SlowTurnTics) + { + turnSpeed = 2; + } + else + { + turnSpeed = speed; + } + + if (strafe) + { + if (keyTurnRight) + { + side += PlayerBehavior.SideMove[speed]; + } + if (keyTurnLeft) + { + side -= PlayerBehavior.SideMove[speed]; + } + } + else + { + if (keyTurnRight) + { + cmd.AngleTurn -= (short)PlayerBehavior.AngleTurn[turnSpeed]; + } + if (keyTurnLeft) + { + cmd.AngleTurn += (short)PlayerBehavior.AngleTurn[turnSpeed]; + } + } + + if (keyForward) + { + forward += PlayerBehavior.ForwardMove[speed]; + } + if (keyBackward) + { + forward -= PlayerBehavior.ForwardMove[speed]; + } + + if (keyStrafeLeft) + { + side -= PlayerBehavior.SideMove[speed]; + } + if (keyStrafeRight) + { + side += PlayerBehavior.SideMove[speed]; + } + + if (keyFire) + { + cmd.Buttons |= TicCmdButtons.Attack; + } + + if (keyUse) + { + cmd.Buttons |= TicCmdButtons.Use; + } + + // Check weapon keys. + for (var i = 0; i < weaponKeys.Length; i++) + { + if (weaponKeys[i]) + { + cmd.Buttons |= TicCmdButtons.Change; + cmd.Buttons |= (byte)(i << TicCmdButtons.WeaponShift); + break; + } + } + + if (useMouse) + { + UpdateMouse(); + var ms = 0.5F * config.mouse_sensitivity; + var mx = (int)MathF.Round(ms * mouseX); + var my = (int)MathF.Round(ms * mouseY); + forward += my; + if (strafe) + { + side += mx * 2; + } + else + { + cmd.AngleTurn -= (short)(mx * 0x8); + } + } + + if (forward > PlayerBehavior.MaxMove) + { + forward = PlayerBehavior.MaxMove; + } + else if (forward < -PlayerBehavior.MaxMove) + { + forward = -PlayerBehavior.MaxMove; + } + if (side > PlayerBehavior.MaxMove) + { + side = PlayerBehavior.MaxMove; + } + else if (side < -PlayerBehavior.MaxMove) + { + side = -PlayerBehavior.MaxMove; + } + + cmd.ForwardMove += (sbyte)forward; + cmd.SideMove += (sbyte)side; + } + + private static bool IsPressed(KeyBinding keyBinding) + { + foreach (var key in keyBinding.Keys) + { + if (DoomApplication.IsKeyPressed(key)) + { + return true; + } + } + + foreach (var mouseButton in keyBinding.MouseButtons) + { + if (Mouse.IsButtonPressed((Mouse.Button)mouseButton)) + { + return true; + } + } + + return false; + } + + public void Reset() + { + mouseX = 0; + mouseY = 0; + cursorCentered = false; + } + + private void UpdateMouse() + { + if (cursorCentered) + { + var current = Mouse.GetPosition(window); + + mouseX = current.X - windowCenterX; + + if (config.mouse_disableyaxis) + { + mouseY = 0; + } + else + { + mouseY = -(current.Y - windowCenterY); + } + } + else + { + mouseX = 0; + mouseY = 0; + } + + Mouse.SetPosition(new Vector2i(windowCenterX, windowCenterY), window); + var pos = Mouse.GetPosition(window); + cursorCentered = (pos.X == windowCenterX && pos.Y == windowCenterY); + } + + public void Dispose() + { + Aura_OS.System.Processing.Application.DoomApp.debugger.WriteLine("Shutdown user input."); + Console.ReadKey(); + + if (useMouse) + { + window.SetMouseCursorVisible(true); + window.SetMouseCursorGrabbed(false); + } + } + + public int MaxMouseSensitivity + { + get + { + return 9; + } + } + + public int MouseSensitivity + { + get + { + return config.mouse_sensitivity; + } + + set + { + config.mouse_sensitivity = value; + } + } + } +} diff --git a/SRC/Aura_OS/System/Processing/Application/Emulators/GameBoyEmu/DMG/PPU.cs b/SRC/Aura_OS/System/Processing/Application/Emulators/GameBoyEmu/DMG/PPU.cs index 3bb576ce..3a1c63c6 100644 --- a/SRC/Aura_OS/System/Processing/Application/Emulators/GameBoyEmu/DMG/PPU.cs +++ b/SRC/Aura_OS/System/Processing/Application/Emulators/GameBoyEmu/DMG/PPU.cs @@ -1,4 +1,4 @@ -using Aura_OS.System.Processing.Application.Emulators.GameBoyEmu.Utils; +using Aura_OS.System.Graphics.UI.GUI.Components; using System.Runtime.CompilerServices; using static Aura_OS.System.Processing.Application.Emulators.GameBoyEmu.Utils.BitOps;