commit 0132cbcf33a3ae568c361751a42ff53c41e08f68 Author: Green Sky Date: Fri Mar 8 21:33:52 2024 +0100 Squashed 'external/pure_doom/PureDOOM/' content from commit 5694fa4 git-subtree-dir: external/pure_doom/PureDOOM git-subtree-split: 5694fa42089714cb3c9edc1c8e10e4e6b6e9adb9 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a256d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +build/ +*.wad +.doomrc +references/ +.DS_Store +.vscode +*.DLL diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9b3bd18 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "thirdparty/SDL"] + path = thirdparty/SDL + url = https://github.com/SDL-mirror/SDL.git +[submodule "thirdparty/SFML"] + path = thirdparty/SFML + url = https://github.com/SFML/SFML.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..766d0d6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.0) + +project(PureDOOM) + +add_subdirectory(examples/SDL) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/PureDOOM.h b/PureDOOM.h new file mode 100644 index 0000000..4277784 --- /dev/null +++ b/PureDOOM.h @@ -0,0 +1,48583 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +//----------------------------------------------------------------------------- +/* Pure DOOM usage + + Do this: + #define DOOM_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define DOOM_IMPLEMENTATION + #include "PureDOOM.h" + +*/ +//----------------------------------------------------------------------------- + + +#ifndef __DOOM_H__ +#define __DOOM_H__ + + +// Sample rate of sound samples from doom +#define DOOM_SAMPLERATE 11025 + +// MIDI tick needs to be called 140 times per seconds +#define DOOM_MIDI_RATE 140 + +// Hide menu options. If for say your platform doesn't support mouse or +// MIDI playback, you can hide these settings from the menu. +#define DOOM_FLAG_HIDE_MOUSE_OPTIONS 1 // Remove mouse options from menu +#define DOOM_FLAG_HIDE_SOUND_OPTIONS 2 // Remove sound options from menu +#define DOOM_FLAG_HIDE_MUSIC_OPTIONS 4 // Remove music options from menu + +// Darken background when menu is open, making it more readable. This +// uses a bit more CPU and redraws the HUD every frame +#define DOOM_FLAG_MENU_DARKEN_BG 8 + + +#if __cplusplus +extern "C" { +#endif + +typedef enum +{ + DOOM_SEEK_CUR = 1, + DOOM_SEEK_END = 2, + DOOM_SEEK_SET = 0 +} doom_seek_t; + + +typedef void(*doom_print_fn)(const char* str); +typedef void*(*doom_malloc_fn)(int size); +typedef void(*doom_free_fn)(void* ptr); +typedef void*(*doom_open_fn)(const char* filename, const char* mode); +typedef void(*doom_close_fn)(void* handle); +typedef int(*doom_read_fn)(void* handle, void *buf, int count); +typedef int(*doom_write_fn)(void* handle, const void *buf, int count); +typedef int(*doom_seek_fn)(void* handle, int offset, doom_seek_t origin); +typedef int(*doom_tell_fn)(void* handle); +typedef int(*doom_eof_fn)(void* handle); +typedef void(*doom_gettime_fn)(int* sec, int* usec); +typedef void(*doom_exit_fn)(int code); +typedef char*(*doom_getenv_fn)(const char* var); + + +// Doom key mapping +typedef enum +{ + DOOM_KEY_UNKNOWN = -1, + DOOM_KEY_TAB = 9, + DOOM_KEY_ENTER = 13, + DOOM_KEY_ESCAPE = 27, + DOOM_KEY_SPACE = 32, + DOOM_KEY_APOSTROPHE = '\'', + DOOM_KEY_MULTIPLY = '*', + DOOM_KEY_COMMA = ',', + DOOM_KEY_MINUS = 0x2d, + DOOM_KEY_PERIOD = '.', + DOOM_KEY_SLASH = '/', + DOOM_KEY_0 = '0', + DOOM_KEY_1 = '1', + DOOM_KEY_2 = '2', + DOOM_KEY_3 = '3', + DOOM_KEY_4 = '4', + DOOM_KEY_5 = '5', + DOOM_KEY_6 = '6', + DOOM_KEY_7 = '7', + DOOM_KEY_8 = '8', + DOOM_KEY_9 = '9', + DOOM_KEY_SEMICOLON = ';', + DOOM_KEY_EQUALS = 0x3d, + DOOM_KEY_LEFT_BRACKET = '[', + DOOM_KEY_RIGHT_BRACKET = ']', + DOOM_KEY_A = 'a', + DOOM_KEY_B = 'b', + DOOM_KEY_C = 'c', + DOOM_KEY_D = 'd', + DOOM_KEY_E = 'e', + DOOM_KEY_F = 'f', + DOOM_KEY_G = 'g', + DOOM_KEY_H = 'h', + DOOM_KEY_I = 'i', + DOOM_KEY_J = 'j', + DOOM_KEY_K = 'k', + DOOM_KEY_L = 'l', + DOOM_KEY_M = 'm', + DOOM_KEY_N = 'n', + DOOM_KEY_O = 'o', + DOOM_KEY_P = 'p', + DOOM_KEY_Q = 'q', + DOOM_KEY_R = 'r', + DOOM_KEY_S = 's', + DOOM_KEY_T = 't', + DOOM_KEY_U = 'u', + DOOM_KEY_V = 'v', + DOOM_KEY_W = 'w', + DOOM_KEY_X = 'x', + DOOM_KEY_Y = 'y', + DOOM_KEY_Z = 'z', + DOOM_KEY_BACKSPACE = 127, + DOOM_KEY_CTRL = (0x80 + 0x1d), // Both left and right + DOOM_KEY_LEFT_ARROW = 0xac, + DOOM_KEY_UP_ARROW = 0xad, + DOOM_KEY_RIGHT_ARROW = 0xae, + DOOM_KEY_DOWN_ARROW = 0xaf, + DOOM_KEY_SHIFT = (0x80 + 0x36), // Both left and right + DOOM_KEY_ALT = (0x80 + 0x38), // Both left and right + DOOM_KEY_F1 = (0x80 + 0x3b), + DOOM_KEY_F2 = (0x80 + 0x3c), + DOOM_KEY_F3 = (0x80 + 0x3d), + DOOM_KEY_F4 = (0x80 + 0x3e), + DOOM_KEY_F5 = (0x80 + 0x3f), + DOOM_KEY_F6 = (0x80 + 0x40), + DOOM_KEY_F7 = (0x80 + 0x41), + DOOM_KEY_F8 = (0x80 + 0x42), + DOOM_KEY_F9 = (0x80 + 0x43), + DOOM_KEY_F10 = (0x80 + 0x44), + DOOM_KEY_F11 = (0x80 + 0x57), + DOOM_KEY_F12 = (0x80 + 0x58), + DOOM_KEY_PAUSE = 0xff +} doom_key_t; + + +// Mouse button mapping +typedef enum +{ + DOOM_LEFT_BUTTON = 0, + DOOM_RIGHT_BUTTON = 1, + DOOM_MIDDLE_BUTTON = 2 +} doom_button_t; + + +// For the software renderer. Default is 320x200 +void doom_set_resolution(int width, int height); + +// Set default configurations. Lets say, changing arrows to WASD as default controls +void doom_set_default_int(const char* name, int value); +void doom_set_default_string(const char* name, const char* value); + +// set callbacks +void doom_set_print(doom_print_fn print_fn); +void doom_set_malloc(doom_malloc_fn malloc_fn, doom_free_fn free_fn); +void doom_set_file_io(doom_open_fn open_fn, + doom_close_fn close_fn, + doom_read_fn read_fn, + doom_write_fn write_fn, + doom_seek_fn seek_fn, + doom_tell_fn tell_fn, + doom_eof_fn eof_fn); +void doom_set_gettime(doom_gettime_fn gettime_fn); +void doom_set_exit(doom_exit_fn exit_fn); +void doom_set_getenv(doom_getenv_fn getenv_fn); + +// Initializes DOOM and start things up. Call only call one +void doom_init(int argc, char** argv, int flags); + +// Call this every frame +void doom_update(); + +// Channels: 1 = indexed, 3 = RGB, 4 = RGBA +const unsigned char* doom_get_framebuffer(int channels); + +// It is always 2048 bytes in size +short* doom_get_sound_buffer(); + +// Call this 140 times per second. Or about every 7ms. +// Returns midi message. Keep calling it until it returns 0. +unsigned long doom_tick_midi(); + +// Events +void doom_key_down(doom_key_t key); +void doom_key_up(doom_key_t key); +void doom_button_down(doom_button_t button); +void doom_button_up(doom_button_t button); +void doom_mouse_move(int delta_x, int delta_y); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif +#ifndef __D_ENGLSH__ +#define __D_ENGLSH__ + + +// +// Printed strings for translation +// + +// +// D_Main.C +// +#define D_DEVSTR "Development mode ON.\n" +#define D_CDROM "CD-ROM Version: default.cfg from c:\\doomdata\n" + +// +// M_Menu.C +// +#define PRESSKEY "press a key." +#define PRESSYN "press y or n." +#define QUITMSG "are you sure you want to\nquit this great game?" +#define LOADNET "you can't do load while in a net game!\n\n" PRESSKEY +#define QLOADNET "you can't quickload during a netgame!\n\n" PRESSKEY +#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n" PRESSKEY +#define SAVEDEAD "you can't save if you aren't playing!\n\n" PRESSKEY +#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n" PRESSYN +#define QLPROMPT "do you want to quickload the game named\n\n'%s'?\n\n" PRESSYN + +#define QSPROMPT_1 "quicksave over your game named\n\n'" +#define QSPROMPT_2 "'?\n\n" PRESSYN +#define QLPROMPT_1 "do you want to quickload the game named\n\n'%s" +#define QLPROMPT_2 "'?\n\n" PRESSYN + +#define NEWGAME \ +"you can't start a new game\n"\ +"while in a network game.\n\n" PRESSKEY + +#define NIGHTMARE \ +"are you sure? this skill level\n"\ +"isn't even remotely fair.\n\n" PRESSYN + +#define SWSTRING \ +"this is the shareware version of doom.\n\n"\ +"you need to order the entire trilogy.\n\n" PRESSKEY + +#define MSGOFF "Messages OFF" +#define MSGON "Messages ON" +#define CROSSOFF "Crosshair OFF" +#define CROSSON "Crosshair ON" +#define ALWAYSRUNOFF "Always run OFF" +#define ALWAYSRUNON "Always run ON" +#define NETEND "you can't end a netgame!\n\n" PRESSKEY +#define ENDGAME "are you sure you want to end the game?\n\n" PRESSYN + +#define DOSY "(press y to quit)" + +#define DETAILHI "High detail" +#define DETAILLO "Low detail" +#define GAMMALVL0 "Gamma correction OFF" +#define GAMMALVL1 "Gamma correction level 1" +#define GAMMALVL2 "Gamma correction level 2" +#define GAMMALVL3 "Gamma correction level 3" +#define GAMMALVL4 "Gamma correction level 4" +#define EMPTYSTRING "empty slot" + +// +// P_inter.C +// +#define GOTARMOR "Picked up the armor." +#define GOTMEGA "Picked up the MegaArmor!" +#define GOTHTHBONUS "Picked up a health bonus." +#define GOTARMBONUS "Picked up an armor bonus." +#define GOTSTIM "Picked up a stimpack." +#define GOTMEDINEED "Picked up a medikit that you REALLY need!" +#define GOTMEDIKIT "Picked up a medikit." +#define GOTSUPER "Supercharge!" + +#define GOTBLUECARD "Picked up a blue keycard." +#define GOTYELWCARD "Picked up a yellow keycard." +#define GOTREDCARD "Picked up a red keycard." +#define GOTBLUESKUL "Picked up a blue skull key." +#define GOTYELWSKUL "Picked up a yellow skull key." +#define GOTREDSKULL "Picked up a red skull key." + +#define GOTINVUL "Invulnerability!" +#define GOTBERSERK "Berserk!" +#define GOTINVIS "Partial Invisibility" +#define GOTSUIT "Radiation Shielding Suit" +#define GOTMAP "Computer Area Map" +#define GOTVISOR "Light Amplification Visor" +#define GOTMSPHERE "MegaSphere!" + +#define GOTCLIP "Picked up a clip." +#define GOTCLIPBOX "Picked up a box of bullets." +#define GOTROCKET "Picked up a rocket." +#define GOTROCKBOX "Picked up a box of rockets." +#define GOTCELL "Picked up an energy cell." +#define GOTCELLBOX "Picked up an energy cell pack." +#define GOTSHELLS "Picked up 4 shotgun shells." +#define GOTSHELLBOX "Picked up a box of shotgun shells." +#define GOTBACKPACK "Picked up a backpack full of ammo!" + +#define GOTBFG9000 "You got the BFG9000! Oh, yes." +#define GOTCHAINGUN "You got the chaingun!" +#define GOTCHAINSAW "A chainsaw! Find some meat!" +#define GOTLAUNCHER "You got the rocket launcher!" +#define GOTPLASMA "You got the plasma gun!" +#define GOTSHOTGUN "You got the shotgun!" +#define GOTSHOTGUN2 "You got the super shotgun!" + +// +// P_Doors.C +// +#define PD_BLUEO "You need a blue key to activate this object" +#define PD_REDO "You need a red key to activate this object" +#define PD_YELLOWO "You need a yellow key to activate this object" +#define PD_BLUEK "You need a blue key to open this door" +#define PD_REDK "You need a red key to open this door" +#define PD_YELLOWK "You need a yellow key to open this door" + +// +// G_game.C +// +#define GGSAVED "game saved." + +// +// HU_stuff.C +// +#define HUSTR_MSGU "[Message unsent]" + +#define HUSTR_E1M1 "E1M1: Hangar" +#define HUSTR_E1M2 "E1M2: Nuclear Plant" +#define HUSTR_E1M3 "E1M3: Toxin Refinery" +#define HUSTR_E1M4 "E1M4: Command Control" +#define HUSTR_E1M5 "E1M5: Phobos Lab" +#define HUSTR_E1M6 "E1M6: Central Processing" +#define HUSTR_E1M7 "E1M7: Computer Station" +#define HUSTR_E1M8 "E1M8: Phobos Anomaly" +#define HUSTR_E1M9 "E1M9: Military Base" + +#define HUSTR_E2M1 "E2M1: Deimos Anomaly" +#define HUSTR_E2M2 "E2M2: Containment Area" +#define HUSTR_E2M3 "E2M3: Refinery" +#define HUSTR_E2M4 "E2M4: Deimos Lab" +#define HUSTR_E2M5 "E2M5: Command Center" +#define HUSTR_E2M6 "E2M6: Halls of the Damned" +#define HUSTR_E2M7 "E2M7: Spawning Vats" +#define HUSTR_E2M8 "E2M8: Tower of Babel" +#define HUSTR_E2M9 "E2M9: Fortress of Mystery" + +#define HUSTR_E3M1 "E3M1: Hell Keep" +#define HUSTR_E3M2 "E3M2: Slough of Despair" +#define HUSTR_E3M3 "E3M3: Pandemonium" +#define HUSTR_E3M4 "E3M4: House of Pain" +#define HUSTR_E3M5 "E3M5: Unholy Cathedral" +#define HUSTR_E3M6 "E3M6: Mt. Erebus" +#define HUSTR_E3M7 "E3M7: Limbo" +#define HUSTR_E3M8 "E3M8: Dis" +#define HUSTR_E3M9 "E3M9: Warrens" + +#define HUSTR_E4M1 "E4M1: Hell Beneath" +#define HUSTR_E4M2 "E4M2: Perfect Hatred" +#define HUSTR_E4M3 "E4M3: Sever The Wicked" +#define HUSTR_E4M4 "E4M4: Unruly Evil" +#define HUSTR_E4M5 "E4M5: They Will Repent" +#define HUSTR_E4M6 "E4M6: Against Thee Wickedly" +#define HUSTR_E4M7 "E4M7: And Hell Followed" +#define HUSTR_E4M8 "E4M8: Unto The Cruel" +#define HUSTR_E4M9 "E4M9: Fear" + +#define HUSTR_1 "level 1: entryway" +#define HUSTR_2 "level 2: underhalls" +#define HUSTR_3 "level 3: the gantlet" +#define HUSTR_4 "level 4: the focus" +#define HUSTR_5 "level 5: the waste tunnels" +#define HUSTR_6 "level 6: the crusher" +#define HUSTR_7 "level 7: dead simple" +#define HUSTR_8 "level 8: tricks and traps" +#define HUSTR_9 "level 9: the pit" +#define HUSTR_10 "level 10: refueling base" +#define HUSTR_11 "level 11: 'o' of destruction!" + +#define HUSTR_12 "level 12: the factory" +#define HUSTR_13 "level 13: downtown" +#define HUSTR_14 "level 14: the inmost dens" +#define HUSTR_15 "level 15: industrial zone" +#define HUSTR_16 "level 16: suburbs" +#define HUSTR_17 "level 17: tenements" +#define HUSTR_18 "level 18: the courtyard" +#define HUSTR_19 "level 19: the citadel" +#define HUSTR_20 "level 20: gotcha!" + +#define HUSTR_21 "level 21: nirvana" +#define HUSTR_22 "level 22: the catacombs" +#define HUSTR_23 "level 23: barrels o' fun" +#define HUSTR_24 "level 24: the chasm" +#define HUSTR_25 "level 25: bloodfalls" +#define HUSTR_26 "level 26: the abandoned mines" +#define HUSTR_27 "level 27: monster condo" +#define HUSTR_28 "level 28: the spirit world" +#define HUSTR_29 "level 29: the living end" +#define HUSTR_30 "level 30: icon of sin" + +#define HUSTR_31 "level 31: wolfenstein" +#define HUSTR_32 "level 32: grosse" + +#define PHUSTR_1 "level 1: congo" +#define PHUSTR_2 "level 2: well of souls" +#define PHUSTR_3 "level 3: aztec" +#define PHUSTR_4 "level 4: caged" +#define PHUSTR_5 "level 5: ghost town" +#define PHUSTR_6 "level 6: baron's lair" +#define PHUSTR_7 "level 7: caughtyard" +#define PHUSTR_8 "level 8: realm" +#define PHUSTR_9 "level 9: abattoire" +#define PHUSTR_10 "level 10: onslaught" +#define PHUSTR_11 "level 11: hunted" + +#define PHUSTR_12 "level 12: speed" +#define PHUSTR_13 "level 13: the crypt" +#define PHUSTR_14 "level 14: genesis" +#define PHUSTR_15 "level 15: the twilight" +#define PHUSTR_16 "level 16: the omen" +#define PHUSTR_17 "level 17: compound" +#define PHUSTR_18 "level 18: neurosphere" +#define PHUSTR_19 "level 19: nme" +#define PHUSTR_20 "level 20: the death domain" + +#define PHUSTR_21 "level 21: slayer" +#define PHUSTR_22 "level 22: impossible mission" +#define PHUSTR_23 "level 23: tombstone" +#define PHUSTR_24 "level 24: the final frontier" +#define PHUSTR_25 "level 25: the temple of darkness" +#define PHUSTR_26 "level 26: bunker" +#define PHUSTR_27 "level 27: anti-christ" +#define PHUSTR_28 "level 28: the sewers" +#define PHUSTR_29 "level 29: odyssey of noises" +#define PHUSTR_30 "level 30: the gateway of hell" + +#define PHUSTR_31 "level 31: cyberden" +#define PHUSTR_32 "level 32: go 2 it" + +#define THUSTR_1 "level 1: system control" +#define THUSTR_2 "level 2: human bbq" +#define THUSTR_3 "level 3: power control" +#define THUSTR_4 "level 4: wormhole" +#define THUSTR_5 "level 5: hanger" +#define THUSTR_6 "level 6: open season" +#define THUSTR_7 "level 7: prison" +#define THUSTR_8 "level 8: metal" +#define THUSTR_9 "level 9: stronghold" +#define THUSTR_10 "level 10: redemption" +#define THUSTR_11 "level 11: storage facility" + +#define THUSTR_12 "level 12: crater" +#define THUSTR_13 "level 13: nukage processing" +#define THUSTR_14 "level 14: steel works" +#define THUSTR_15 "level 15: dead zone" +#define THUSTR_16 "level 16: deepest reaches" +#define THUSTR_17 "level 17: processing area" +#define THUSTR_18 "level 18: mill" +#define THUSTR_19 "level 19: shipping/respawning" +#define THUSTR_20 "level 20: central processing" + +#define THUSTR_21 "level 21: administration center" +#define THUSTR_22 "level 22: habitat" +#define THUSTR_23 "level 23: lunar mining project" +#define THUSTR_24 "level 24: quarry" +#define THUSTR_25 "level 25: baron's den" +#define THUSTR_26 "level 26: ballistyx" +#define THUSTR_27 "level 27: mount pain" +#define THUSTR_28 "level 28: heck" +#define THUSTR_29 "level 29: river styx" +#define THUSTR_30 "level 30: last call" + +#define THUSTR_31 "level 31: pharaoh" +#define THUSTR_32 "level 32: caribbean" + +#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +#define HUSTR_CHATMACRO2 "I'm OK." +#define HUSTR_CHATMACRO3 "I'm not looking too good!" +#define HUSTR_CHATMACRO4 "Help!" +#define HUSTR_CHATMACRO5 "You suck!" +#define HUSTR_CHATMACRO6 "Next time, scumbag..." +#define HUSTR_CHATMACRO7 "Come here!" +#define HUSTR_CHATMACRO8 "I'll take care of it." +#define HUSTR_CHATMACRO9 "Yes" +#define HUSTR_CHATMACRO0 "No" + +#define HUSTR_TALKTOSELF1 "You mumble to yourself" +#define HUSTR_TALKTOSELF2 "Who's there?" +#define HUSTR_TALKTOSELF3 "You scare yourself" +#define HUSTR_TALKTOSELF4 "You start to rave" +#define HUSTR_TALKTOSELF5 "You've lost it..." + +#define HUSTR_MESSAGESENT "[Message Sent]" + +// The following should NOT be changed unless it seems +// just AWFULLY necessary + +#define HUSTR_PLRGREEN "Green: " +#define HUSTR_PLRINDIGO "Indigo: " +#define HUSTR_PLRBROWN "Brown: " +#define HUSTR_PLRRED "Red: " + +#define HUSTR_KEYGREEN 'g' +#define HUSTR_KEYINDIGO 'i' +#define HUSTR_KEYBROWN 'b' +#define HUSTR_KEYRED 'r' + +// +// AM_map.C +// + +#define AMSTR_FOLLOWON "Follow Mode ON" +#define AMSTR_FOLLOWOFF "Follow Mode OFF" + +#define AMSTR_GRIDON "Grid ON" +#define AMSTR_GRIDOFF "Grid OFF" + +#define AMSTR_MARKEDSPOT "Marked Spot" +#define AMSTR_MARKSCLEARED "All Marks Cleared" + +// +// ST_stuff.C +// + +#define STSTR_MUS "Music Change" +#define STSTR_NOMUS "IMPOSSIBLE SELECTION" +#define STSTR_DQDON "Degreelessness Mode On" +#define STSTR_DQDOFF "Degreelessness Mode Off" + +#define STSTR_KFAADDED "Very Happy Ammo Added" +#define STSTR_FAADDED "Ammo (no keys) Added" + +#define STSTR_NCON "No Clipping Mode ON" +#define STSTR_NCOFF "No Clipping Mode OFF" + +#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp" +#define STSTR_BEHOLDX "Power-up Toggled" + +#define STSTR_CHOPPERS "... doesn't suck - GM" +#define STSTR_CLEV "Changing Level..." + +// +// F_Finale.C +// +#define E1TEXT \ +"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" + + +#define E2TEXT \ +"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." + + +#define E3TEXT \ +"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 ..." + + +#define E4TEXT \ +"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!" + + +// after level 6, put this: +#define C1TEXT \ +"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." + +// After level 11, put this: +#define C2TEXT \ +"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." + + +// After level 20, put this: +#define C3TEXT \ +"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?" + + +// After level 29, put this: +#define C4TEXT \ +"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" + + + +// Before level 31, put this: +#define C5TEXT \ +"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." + + +// Before level 32, put this: +#define C6TEXT \ +"CONGRATULATIONS, YOU'VE FOUND THE\n"\ +"SUPER SECRET LEVEL! YOU'D BETTER\n"\ +"BLAZE THROUGH THIS ONE!\n" + + +// after map 06 +#define P1TEXT \ +"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." + + +// after map 11 +#define P2TEXT \ +"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." + + +// after map 20 +#define P3TEXT \ +"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!" + +// after map 30 +#define P4TEXT \ +"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 ..." + +// before map 31 +#define P5TEXT \ +"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." + +// before map 32 +#define P6TEXT \ +"Betcha wondered just what WAS the hardest\n"\ +"level we had ready for ya? Now you know.\n"\ +"No one gets out alive." + + +#define T1TEXT \ +"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." + + +#define T2TEXT \ +"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." + + +#define T3TEXT \ +"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." + +#define T4TEXT \ +"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." + + +#define T5TEXT \ +"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.." + + +#define T6TEXT \ +"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." + + +// +// Character cast strings F_FINALE.C +// +#define CC_ZOMBIE "ZOMBIEMAN" +#define CC_SHOTGUN "SHOTGUN GUY" +#define CC_HEAVY "HEAVY WEAPON DUDE" +#define CC_IMP "IMP" +#define CC_DEMON "DEMON" +#define CC_LOST "LOST SOUL" +#define CC_CACO "CACODEMON" +#define CC_HELL "HELL KNIGHT" +#define CC_BARON "BARON OF HELL" +#define CC_ARACH "ARACHNOTRON" +#define CC_PAIN "PAIN ELEMENTAL" +#define CC_REVEN "REVENANT" +#define CC_MANCU "MANCUBUS" +#define CC_ARCH "ARCH-VILE" +#define CC_SPIDER "THE SPIDER MASTERMIND" +#define CC_CYBER "THE CYBERDEMON" +#define CC_HERO "OUR HERO" + + +#endif + + +#ifndef __D_THINK__ +#define __D_THINK__ + + +// +// Experimental stuff. +// To compile this as "ANSI C with classes" +// we will need to handle the various +// action functions cleanly. +// +typedef void (*actionf_v)(); +typedef void (*actionf_p1)(void*); +typedef void (*actionf_p2)(void*, void*); + +typedef union +{ + actionf_p1 acp1; + actionf_v acv; + actionf_p2 acp2; +} actionf_t; + + +// Historically, "think_t" is yet another +// function pointer to a routine to handle +// an actor. +typedef actionf_t think_t; + + +// Doubly linked list of actors. +typedef struct thinker_s +{ + struct thinker_s* prev; + struct thinker_s* next; + think_t function; +} thinker_t; + + +#endif + + +#ifndef __DOOM_CONFIG_H__ +#define __DOOM_CONFIG_H__ + + +#if defined(WIN32) +#define DOOM_WIN32 +#elif defined(__APPLE__) +#define DOOM_APPLE +#else +#define DOOM_LINUX +#endif + + +//#include "DOOM.h" + + +#define doom_abs(x) ((x) < 0 ? -(x) : (x)) + + +extern char error_buf[260]; +extern int doom_flags; +extern doom_print_fn doom_print; +extern doom_malloc_fn doom_malloc; +extern doom_free_fn doom_free; +extern doom_open_fn doom_open; +extern doom_close_fn doom_close; +extern doom_read_fn doom_read; +extern doom_write_fn doom_write; +extern doom_seek_fn doom_seek; +extern doom_tell_fn doom_tell; +extern doom_eof_fn doom_eof; +extern doom_gettime_fn doom_gettime; +extern doom_exit_fn doom_exit; +extern doom_getenv_fn doom_getenv; + + +const char* doom_itoa(int i, int radix); +const char* doom_ctoa(char c); +const char* doom_ptoa(void* p); +void doom_memset(void* ptr, int value, int num); +void* doom_memcpy(void* destination, const void* source, int num); +int doom_fprint(void* handle, const char* str); +int doom_strlen(const char* str); +char* doom_concat(char* dst, const char* src); +char* doom_strcpy(char* destination, const char* source); +char* doom_strncpy(char* destination, const char* source, int num); +int doom_strcmp(const char* str1, const char* str2); +int doom_strncmp(const char* str1, const char* str2, int n); +int doom_strcasecmp(const char* str1, const char* str2); +int doom_strncasecmp(const char* str1, const char* str2, int n); +int doom_atoi(const char* str); +int doom_atox(const char* str); +int doom_toupper(int c); + + +#endif +#ifndef __DOOMDEF__ +#define __DOOMDEF__ + + + + + + +// +// Global parameters/defines. +// +// DOOM version +enum +{ + VERSION = 110 +}; + + +// Game mode handling - identify IWAD version +// to handle IWAD dependend animations etc. +typedef enum +{ + 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. +} GameMode_t; + + +// Mission packs - might be useful for TC stuff? +typedef enum +{ + doom, // DOOM 1 + doom2, // DOOM 2 + pack_tnt, // TNT mission pack + pack_plut, // Plutonia pack + none +} GameMission_t; + + +// Identify language to use, software localization. +typedef enum +{ + english, + french, + german, + unknown +} Language_t; + + +// If rangecheck is undefined, +// most parameter validation debugging code will not be compiled +#define RANGECHECK + +// +// For resize of screen, at start of game. +// It will not work dynamically, see visplanes. +// +#define BASE_WIDTH 320 + +// It is educational but futile to change this +// scaling e.g. to 2. Drawing of status bar, +// menues etc. is tied to the scale implied +// by the graphics. +#define SCREEN_MUL 1 +#define INV_ASPECT_RATIO 0.625 // 0.75, ideally + +// Defines suck. C sucks. +// C++ might sucks for OOP, but it sure is a better C. +// So there. +// extern int SCREENWIDTH; +#define SCREENWIDTH 320 +#define SCREENHEIGHT 200 + +// The maximum number of players, multiplayer/networking. +#define MAXPLAYERS 4 + +// State updates, number of tics / second. +#if defined(DOOM_FAST_TICK) +#define TICKMUL 2 +#else +#define TICKMUL 1 +#endif +#define TICRATE (35 * TICKMUL) + + +// The current state of the game: whether we are +// playing, gazing at the intermission screen, +// the game final animation, or a demo. +typedef enum +{ + GS_LEVEL, + GS_INTERMISSION, + GS_FINALE, + GS_DEMOSCREEN +} gamestate_t; + + +// +// Difficulty/skill settings/filters. +// + +// Skill flags. +#define MTF_EASY 1 +#define MTF_NORMAL 2 +#define MTF_HARD 4 + +// Deaf monsters/do not react to sound. +#define MTF_AMBUSH 8 + + +typedef enum +{ + sk_baby, + sk_easy, + sk_medium, + sk_hard, + sk_nightmare +} skill_t; + + +// +// Key cards. +// +typedef enum +{ + it_bluecard, + it_yellowcard, + it_redcard, + it_blueskull, + it_yellowskull, + it_redskull, + NUMCARDS +} card_t; + + +// The defined weapons, +// including a marker indicating +// user has not changed weapon. +typedef enum +{ + wp_fist, + wp_pistol, + wp_shotgun, + wp_chaingun, + wp_missile, + wp_plasma, + wp_bfg, + wp_chainsaw, + wp_supershotgun, + NUMWEAPONS, + // No pending weapon change. + wp_nochange +} weapontype_t; + + +// Ammunition types defined. +typedef enum +{ + am_clip, // Pistol / chaingun ammo. + am_shell, // Shotgun / double barreled shotgun. + am_cell, // Plasma rifle, BFG. + am_misl, // Missile launcher. + NUMAMMO, + am_noammo // Unlimited for chainsaw / fist. +} ammotype_t; + + +// Power up artifacts. +typedef enum +{ + pw_invulnerability, + pw_strength, + pw_invisibility, + pw_ironfeet, + pw_allmap, + pw_infrared, + NUMPOWERS +} powertype_t; + + +// +// Power up durations, +// how many seconds till expiration, +// assuming TICRATE is 35 ticks/second. +// +typedef enum +{ + INVULNTICS = (30 * TICRATE), + INVISTICS = (60 * TICRATE), + INFRATICS = (120 * TICRATE), + IRONTICS = (60 * TICRATE) +} powerduration_t; + + +// +// DOOM keyboard definition. +// This is the stuff configured by Setup.Exe. +// Most key data are simple ascii (uppercased). +// +#define KEY_RIGHTARROW 0xae +#define KEY_LEFTARROW 0xac +#define KEY_UPARROW 0xad +#define KEY_DOWNARROW 0xaf +#define KEY_ESCAPE 27 +#define KEY_ENTER 13 +#define KEY_TAB 9 +#define KEY_F1 (0x80+0x3b) +#define KEY_F2 (0x80+0x3c) +#define KEY_F3 (0x80+0x3d) +#define KEY_F4 (0x80+0x3e) +#define KEY_F5 (0x80+0x3f) +#define KEY_F6 (0x80+0x40) +#define KEY_F7 (0x80+0x41) +#define KEY_F8 (0x80+0x42) +#define KEY_F9 (0x80+0x43) +#define KEY_F10 (0x80+0x44) +#define KEY_F11 (0x80+0x57) +#define KEY_F12 (0x80+0x58) + +#define KEY_BACKSPACE 127 +#define KEY_PAUSE 0xff + +#define KEY_EQUALS 0x3d +#define KEY_MINUS 0x2d + +#define KEY_RSHIFT (0x80+0x36) +#define KEY_RCTRL (0x80+0x1d) +#define KEY_RALT (0x80+0x38) + +#define KEY_LALT KEY_RALT + + +#endif // __DOOMDEF__ + + +#ifndef __D_ITEMS__ +#define __D_ITEMS__ + + +//#include "doomdef.h" + + +// Weapon info: sprite frames, ammunition use. +typedef struct +{ + ammotype_t ammo; + int upstate; + int downstate; + int readystate; + int atkstate; + int flashstate; +} weaponinfo_t; + + +extern weaponinfo_t weaponinfo[NUMWEAPONS]; + + +#endif + + +#ifndef __DOOMTYPE__ +#define __DOOMTYPE__ + + +// Fixed to use builtin bool type with C++. +#ifdef __cplusplus +typedef bool doom_boolean; +#else +#if !defined(false) && !defined(true) +typedef enum +{ + false, true +} doom_boolean; +#else +typedef int doom_boolean; +#endif +#endif + + +typedef unsigned char byte; + + +#define DOOM_MAXCHAR ((char)0x7f) +#define DOOM_MAXSHORT ((short)0x7fff) + +// Max pos 32-bit int. +#define DOOM_MAXINT ((int)0x7fffffff) +#define DOOM_MAXLONG ((long)0x7fffffff) +#define DOOM_MINCHAR ((char)0x80) +#define DOOM_MINSHORT ((short)0x8000) + +// Max negative 32-bit integer. +#define DOOM_MININT ((int)0x80000000) +#define DOOM_MINLONG ((long)0x80000000) + + +#endif + + +#ifndef __D_EVENT__ +#define __D_EVENT__ + + +//#include "doomtype.h" + + +// +// Event handling. +// + +// Input event types. +typedef enum +{ + ev_keydown, + ev_keyup, + ev_mouse, + ev_joystick +} evtype_t; + + +// Event structure. +typedef struct +{ + evtype_t type; + int data1; // keys / mouse/joystick buttons + int data2; // mouse/joystick x move + int data3; // mouse/joystick y move +} event_t; + + +typedef enum +{ + ga_nothing, + ga_loadlevel, + ga_newgame, + ga_loadgame, + ga_savegame, + ga_playdemo, + ga_completed, + ga_victory, + ga_worlddone, + ga_screenshot +} gameaction_t; + + +// +// Button/action code definitions. +// +typedef enum +{ + // Press "Fire". + BT_ATTACK = 1, + // Use button, to open doors, activate switches. + BT_USE = 2, + + // Flag: game events, not really buttons. + BT_SPECIAL = 128, + BT_SPECIALMASK = 3, + + // Flag, weapon change pending. + // If true, the next 3 bits hold weapon num. + BT_CHANGE = 4, + // The 3bit weapon mask and shift, convenience. + BT_WEAPONMASK = (8 + 16 + 32), + BT_WEAPONSHIFT = 3, + + // Pause the game. + BTS_PAUSE = 1, + // Save the game at each console. + BTS_SAVEGAME = 2, + + // Savegame slot numbers + // occupy the second byte of buttons. + BTS_SAVEMASK = (4 + 8 + 16), + BTS_SAVESHIFT = 2, +} buttoncode_t; + + +// +// GLOBAL VARIABLES +// +#define MAXEVENTS (64 * 64) // [pd] Crank up the number because we pump them faster + +extern event_t events[MAXEVENTS]; +extern int eventhead; +extern int eventtail; + +extern gameaction_t gameaction; + + +#endif + + +#ifndef __AMMAP_H__ +#define __AMMAP_H__ + + +//#include "d_event.h" + + +// Used by ST StatusBar stuff. +#define AM_MSGHEADER (('a'<<24)+('m'<<16)) +#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) +#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) + + +// Called by main loop. +doom_boolean AM_Responder(event_t* ev); + +// Called by main loop. +void AM_Ticker(void); + +// Called by main loop, +// called instead of view drawer if automap active. +void AM_Drawer(void); + +// Called to force the automap to quit +// if the level is completed while it is up. +void AM_Stop(void); + + +#endif + + +#ifndef __D_MAIN__ +#define __D_MAIN__ + + +//#include "d_event.h" + + +#define MAXWADFILES 20 + + +extern char* wadfiles[MAXWADFILES]; + + +void D_AddFile(char* file); + +// +// D_DoomMain() +// Not a globally visible function, just included for source reference, +// calls all startup code, parses command line options. +// If not overrided by user input, calls N_AdvanceDemo. +// +void D_DoomMain(void); + +// Called by IO functions when input is detected. +void D_PostEvent(event_t* ev); + +// +// BASE LEVEL +// +void D_PageTicker(void); +void D_PageDrawer(void); +void D_AdvanceDemo(void); +void D_StartTitle(void); + + +#endif +#ifndef __D_TEXTUR__ +#define __D_TEXTUR__ + + +//#include "doomtype.h" + + +// +// Flats? +// +// a pic is an unmasked block of pixels +typedef struct +{ + byte width; + byte height; + byte data; +} pic_t; + + +#endif + + +#ifndef __D_TICCMD__ +#define __D_TICCMD__ + + +//#include "doomtype.h" + + +// The data sampled per tick (single player) +// and transmitted to other peers (multiplayer). +// Mainly movements/button commands per game tick, +// plus a checksum for internal state consistency. +typedef struct +{ + char forwardmove; // *2048 for move + char sidemove; // *2048 for move + short angleturn; // <<16 for angle delta + short consistancy; // checks for net game + byte chatchar; + byte buttons; +} ticcmd_t; + + +#endif + + +#ifndef __DOOMDATA__ +#define __DOOMDATA__ + + +// The most basic types we use, portability. +//#include "doomtype.h" + +// Some global defines, that configure the game. +//#include "doomdef.h" + + +// +// Map level types. +// The following data structures define the persistent format +// used in the lumps of the WAD files. +// + +// Lump order in a map WAD: each map needs a couple of lumps +// to provide a complete scene geometry description. +enum +{ + ML_LABEL, // A separator, name, ExMx or MAPxx + ML_THINGS, // Monsters, items.. + ML_LINEDEFS, // LineDefs, from editing + ML_SIDEDEFS, // SideDefs, from editing + ML_VERTEXES, // Vertices, edited and BSP splits generated + ML_SEGS, // LineSegs, from LineDefs split by BSP + ML_SSECTORS, // SubSectors, list of LineSegs + ML_NODES, // BSP nodes + ML_SECTORS, // Sectors, from editing + ML_REJECT, // LUT, sector-sector visibility + ML_BLOCKMAP // LUT, motion clipping, walls/grid element +}; + + +// A single Vertex. +typedef struct +{ + short x; + short y; +} mapvertex_t; + + +// A SideDef, defining the visual appearance of a wall, +// by setting textures and offsets. +typedef struct +{ + short textureoffset; + short rowoffset; + char toptexture[8]; + char bottomtexture[8]; + char midtexture[8]; + // Front sector, towards viewer. + short sector; +} mapsidedef_t; + + +// A LineDef, as used for editing, and as input +// to the BSP builder. +typedef struct +{ + short v1; + short v2; + short flags; + short special; + short tag; + // sidenum[1] will be -1 if one sided + short sidenum[2]; +} maplinedef_t; + + +// +// LineDef attributes. +// + +// Solid, is an obstacle. +#define ML_BLOCKING 1 + +// Blocks monsters only. +#define ML_BLOCKMONSTERS 2 + +// Backside will not be present at all +// if not two sided. +#define ML_TWOSIDED 4 + +// If a texture is pegged, the texture will have +// the end exposed to air held constant at the +// top or bottom of the texture (stairs or pulled +// down things) and will move with a height change +// of one of the neighbor sectors. +// Unpegged textures allways have the first row of +// the texture at the top pixel of the line for both +// top and bottom textures (use next to windows). + +// upper texture unpegged +#define ML_DONTPEGTOP 8 + +// lower texture unpegged +#define ML_DONTPEGBOTTOM 16 + +// In AutoMap: don't map as two sided: IT'S A SECRET! +#define ML_SECRET 32 + +// Sound rendering: don't let sound cross two of these. +#define ML_SOUNDBLOCK 64 + +// Don't draw on the automap at all. +#define ML_DONTDRAW 128 + +// Set if already seen, thus drawn in automap. +#define ML_MAPPED 256 + + +// Sector definition, from editing. +typedef struct +{ + short floorheight; + short ceilingheight; + char floorpic[8]; + char ceilingpic[8]; + short lightlevel; + short special; + short tag; +} mapsector_t; + + +// SubSector, as generated by BSP. +typedef struct +{ + short numsegs; + // Index of first one, segs are stored sequentially. + short firstseg; +} mapsubsector_t; + + +// LineSeg, generated by splitting LineDefs +// using partition lines selected by BSP builder. +typedef struct +{ + short v1; + short v2; + short angle; + short linedef; + short side; + short offset; +} mapseg_t; + + +// +// BSP node structure. +// + +// Indicate a leaf. +#define NF_SUBSECTOR 0x8000 + +typedef struct +{ + // Partition line from (x,y) to x+dx,y+dy) + short x; + short y; + short dx; + short dy; + + // Bounding box for each child, + // clip against view frustum. + short bbox[2][4]; + + // If NF_SUBSECTOR its a subsector, + // else it's a node of another subtree. + unsigned short children[2]; +} mapnode_t; + + +// Thing definition, position, orientation and type, +// plus skill/visibility flags and attributes. +typedef struct +{ + short x; + short y; + short angle; + short type; + short options; +} mapthing_t; + + +#endif // __DOOMDATA__ + + +#ifndef __DSTRINGS__ +#define __DSTRINGS__ + + +// All important printed strings. +// Language selection (message strings). +// Use -DFRENCH etc. + +#ifdef FRENCH +//#include "d_french.h" // Leave the extra space there, to throw off regex in PureDOOM.h creation +#else +//#include "d_englsh.h" +#endif + + +// Misc. other strings. +#define SAVEGAMENAME "doomsav" + +// +// File locations, +// relative to current position. +// Path names are OS-sensitive. +// +#define DEVMAPS "devmaps" +#define DEVDATA "devdata" + +// Not done in french? + +// QuitDOOM messages +#define NUM_QUITMESSAGES 22 + + +extern char* endmsg[]; + + +#endif + + +#ifndef __F_FINALE__ +#define __F_FINALE__ + + +//#include "doomtype.h" +//#include "d_event.h" + + +// +// FINALE +// + +// Called by main loop. +doom_boolean F_Responder(event_t* ev); + +// Called by main loop. +void F_Ticker(void); + +// Called by main loop. +void F_Drawer(void); + +void F_StartFinale(void); + + +#endif + + +#ifndef __F_WIPE_H__ +#define __F_WIPE_H__ + + +// +// SCREEN WIPE PACKAGE +// + +enum +{ + // simple gradual pixel change for 8-bit only + wipe_ColorXForm, + + // weird screen melt + wipe_Melt, + + wipe_NUMWIPES +}; + + +int wipe_StartScreen(int x, int y, int width, int height); +int wipe_EndScreen(int x, int y, int width, int height); +int wipe_ScreenWipe(int wipeno, int x, int y, int width, int height, int ticks); + + +#endif + + +#ifndef __G_GAME__ +#define __G_GAME__ + + +//#include "doomdef.h" +//#include "d_event.h" + + +// +// GAME +// +void G_DeathMatchSpawnPlayer(int playernum); + +void G_InitNew(skill_t skill, int episode, int map); + +// Can be called by the startup code or M_Responder. +// A normal game starts at map 1, +// but a warp test can start elsewhere +void G_DeferedInitNew(skill_t skill, int episode, int map); + +void G_DeferedPlayDemo(char* demo); + +// Can be called by the startup code or M_Responder, +// calls P_SetupLevel or W_EnterWorld. +void G_LoadGame(char* name); + +void G_DoLoadGame(void); + +// Called by M_Responder. +void G_SaveGame(int slot, char* description); + +// Only called by startup code. +void G_RecordDemo(char* name); + +void G_BeginRecording(void); + +void G_TimeDemo(char* name); +doom_boolean G_CheckDemoStatus(void); + +void G_ExitLevel(void); +void G_SecretExitLevel(void); + +void G_WorldDone(void); + +void G_Ticker(void); +doom_boolean G_Responder(event_t* ev); + +void G_ScreenShot(void); + + +#endif + + +#ifndef __HU_STUFF_H__ +#define __HU_STUFF_H__ + + +//#include "d_event.h" + + +// +// Globally visible constants. +// +#define HU_FONTSTART '!' // the first font characters +#define HU_FONTEND '_' // the last font characters + +// Calculate # of glyphs in font. +#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) + +#define HU_BROADCAST 5 + +#define HU_MSGREFRESH KEY_ENTER +#define HU_MSGX 0 +#define HU_MSGY 0 +#define HU_MSGWIDTH 64 // in characters +#define HU_MSGHEIGHT 1 // in lines + +#define HU_MSGTIMEOUT (4*TICRATE) + + +// +// HEADS UP TEXT +// + +void HU_Init(void); +void HU_Start(void); +doom_boolean HU_Responder(event_t* ev); +void HU_Ticker(void); +void HU_Drawer(void); +char HU_dequeueChatChar(void); +void HU_Erase(void); + + +#endif + + +#ifndef __I_NET__ +#define __I_NET__ + + +// Called by D_DoomMain. + +void I_InitNetwork (void); +void I_NetCmd (void); + + +#endif + + +#ifndef __I_SYSTEM__ +#define __I_SYSTEM__ + + +//#include "d_ticcmd.h" +//#include "d_event.h" + + +// Called by DoomMain. +void I_Init(void); + +// Called by startup code +// to get the ammount of memory to malloc +// for the zone management. +byte* I_ZoneBase(int* size); + +// Called by D_DoomLoop, +// returns current time in tics. +int I_GetTime(void); + +// Called by D_DoomLoop, +// called before processing any tics in a frame +// (just after displaying a frame). +// Time consuming syncronous operations +// are performed here (joystick reading). +// Can call D_PostEvent. +void I_StartFrame(void); + +// Called by D_DoomLoop, +// called before processing each tic in a frame. +// Quick syncronous operations are performed here. +// Can call D_PostEvent. +void I_StartTic(void); + +// Asynchronous interrupt functions should maintain private queues +// that are read by the synchronous functions +// to be converted into events. + +// Either returns a null ticcmd, +// or calls a loadable driver to build it. +// This ticcmd will then be modified by the gameloop +// for normal input. +ticcmd_t* I_BaseTiccmd(void); + +// Called by M_Responder when quit is selected. +// Clean exit, displays sell blurb. +void I_Quit(void); + +// Allocates from low memory under dos, +// just mallocs under unix +byte* I_AllocLow(int length); + +void I_Tactile(int on, int off, int total); + +void I_Error(char* error); + + +#endif + + +#ifndef __I_VIDEO__ +#define __I_VIDEO__ + + +//#include "doomtype.h" + + +// Called by D_DoomMain, +// determines the hardware configuration +// and sets up the video mode +void I_InitGraphics(void); + +void I_ShutdownGraphics(void); + +// Takes full 8 bit values. +void I_SetPalette(byte* palette); + +void I_UpdateNoBlit(void); +void I_FinishUpdate(void); + +// Wait for vertical retrace or pause a bit. +void I_WaitVBL(int count); + +void I_ReadScreen(byte* scr); + +void I_BeginRead(void); +void I_EndRead(void); + + +#endif + + +#ifndef __INFO__ +#define __INFO__ + + +// Needed for action function pointer handling. +//#include "d_think.h" + + +typedef enum +{ + SPR_TROO, + SPR_SHTG, + SPR_PUNG, + SPR_PISG, + SPR_PISF, + SPR_SHTF, + SPR_SHT2, + SPR_CHGG, + SPR_CHGF, + SPR_MISG, + SPR_MISF, + SPR_SAWG, + SPR_PLSG, + SPR_PLSF, + SPR_BFGG, + SPR_BFGF, + SPR_BLUD, + SPR_PUFF, + SPR_BAL1, + SPR_BAL2, + SPR_PLSS, + SPR_PLSE, + SPR_MISL, + SPR_BFS1, + SPR_BFE1, + SPR_BFE2, + SPR_TFOG, + SPR_IFOG, + SPR_PLAY, + SPR_POSS, + SPR_SPOS, + SPR_VILE, + SPR_FIRE, + SPR_FATB, + SPR_FBXP, + SPR_SKEL, + SPR_MANF, + SPR_FATT, + SPR_CPOS, + SPR_SARG, + SPR_HEAD, + SPR_BAL7, + SPR_BOSS, + SPR_BOS2, + SPR_SKUL, + SPR_SPID, + SPR_BSPI, + SPR_APLS, + SPR_APBX, + SPR_CYBR, + SPR_PAIN, + SPR_SSWV, + SPR_KEEN, + SPR_BBRN, + SPR_BOSF, + SPR_ARM1, + SPR_ARM2, + SPR_BAR1, + SPR_BEXP, + SPR_FCAN, + SPR_BON1, + SPR_BON2, + SPR_BKEY, + SPR_RKEY, + SPR_YKEY, + SPR_BSKU, + SPR_RSKU, + SPR_YSKU, + SPR_STIM, + SPR_MEDI, + SPR_SOUL, + SPR_PINV, + SPR_PSTR, + SPR_PINS, + SPR_MEGA, + SPR_SUIT, + SPR_PMAP, + SPR_PVIS, + SPR_CLIP, + SPR_AMMO, + SPR_ROCK, + SPR_BROK, + SPR_CELL, + SPR_CELP, + SPR_SHEL, + SPR_SBOX, + SPR_BPAK, + SPR_BFUG, + SPR_MGUN, + SPR_CSAW, + SPR_LAUN, + SPR_PLAS, + SPR_SHOT, + SPR_SGN2, + SPR_COLU, + SPR_SMT2, + SPR_GOR1, + SPR_POL2, + SPR_POL5, + SPR_POL4, + SPR_POL3, + SPR_POL1, + SPR_POL6, + SPR_GOR2, + SPR_GOR3, + SPR_GOR4, + SPR_GOR5, + SPR_SMIT, + SPR_COL1, + SPR_COL2, + SPR_COL3, + SPR_COL4, + SPR_CAND, + SPR_CBRA, + SPR_COL6, + SPR_TRE1, + SPR_TRE2, + SPR_ELEC, + SPR_CEYE, + SPR_FSKU, + SPR_COL5, + SPR_TBLU, + SPR_TGRN, + SPR_TRED, + SPR_SMBT, + SPR_SMGT, + SPR_SMRT, + SPR_HDB1, + SPR_HDB2, + SPR_HDB3, + SPR_HDB4, + SPR_HDB5, + SPR_HDB6, + SPR_POB1, + SPR_POB2, + SPR_BRS1, + SPR_TLMP, + SPR_TLP2, + NUMSPRITES +} spritenum_t; + + +typedef enum +{ + S_NULL, + S_LIGHTDONE, + S_PUNCH, + S_PUNCHDOWN, + S_PUNCHUP, + S_PUNCH1, + S_PUNCH2, + S_PUNCH3, + S_PUNCH4, + S_PUNCH5, + S_PISTOL, + S_PISTOLDOWN, + S_PISTOLUP, + S_PISTOL1, + S_PISTOL2, + S_PISTOL3, + S_PISTOL4, + S_PISTOLFLASH, + S_SGUN, + S_SGUNDOWN, + S_SGUNUP, + S_SGUN1, + S_SGUN2, + S_SGUN3, + S_SGUN4, + S_SGUN5, + S_SGUN6, + S_SGUN7, + S_SGUN8, + S_SGUN9, + S_SGUNFLASH1, + S_SGUNFLASH2, + S_DSGUN, + S_DSGUNDOWN, + S_DSGUNUP, + S_DSGUN1, + S_DSGUN2, + S_DSGUN3, + S_DSGUN4, + S_DSGUN5, + S_DSGUN6, + S_DSGUN7, + S_DSGUN8, + S_DSGUN9, + S_DSGUN10, + S_DSNR1, + S_DSNR2, + S_DSGUNFLASH1, + S_DSGUNFLASH2, + S_CHAIN, + S_CHAINDOWN, + S_CHAINUP, + S_CHAIN1, + S_CHAIN2, + S_CHAIN3, + S_CHAINFLASH1, + S_CHAINFLASH2, + S_MISSILE, + S_MISSILEDOWN, + S_MISSILEUP, + S_MISSILE1, + S_MISSILE2, + S_MISSILE3, + S_MISSILEFLASH1, + S_MISSILEFLASH2, + S_MISSILEFLASH3, + S_MISSILEFLASH4, + S_SAW, + S_SAWB, + S_SAWDOWN, + S_SAWUP, + S_SAW1, + S_SAW2, + S_SAW3, + S_PLASMA, + S_PLASMADOWN, + S_PLASMAUP, + S_PLASMA1, + S_PLASMA2, + S_PLASMAFLASH1, + S_PLASMAFLASH2, + S_BFG, + S_BFGDOWN, + S_BFGUP, + S_BFG1, + S_BFG2, + S_BFG3, + S_BFG4, + S_BFGFLASH1, + S_BFGFLASH2, + S_BLOOD1, + S_BLOOD2, + S_BLOOD3, + S_PUFF1, + S_PUFF2, + S_PUFF3, + S_PUFF4, + S_TBALL1, + S_TBALL2, + S_TBALLX1, + S_TBALLX2, + S_TBALLX3, + S_RBALL1, + S_RBALL2, + S_RBALLX1, + S_RBALLX2, + S_RBALLX3, + S_PLASBALL, + S_PLASBALL2, + S_PLASEXP, + S_PLASEXP2, + S_PLASEXP3, + S_PLASEXP4, + S_PLASEXP5, + S_ROCKET, + S_BFGSHOT, + S_BFGSHOT2, + S_BFGLAND, + S_BFGLAND2, + S_BFGLAND3, + S_BFGLAND4, + S_BFGLAND5, + S_BFGLAND6, + S_BFGEXP, + S_BFGEXP2, + S_BFGEXP3, + S_BFGEXP4, + S_EXPLODE1, + S_EXPLODE2, + S_EXPLODE3, + S_TFOG, + S_TFOG01, + S_TFOG02, + S_TFOG2, + S_TFOG3, + S_TFOG4, + S_TFOG5, + S_TFOG6, + S_TFOG7, + S_TFOG8, + S_TFOG9, + S_TFOG10, + S_IFOG, + S_IFOG01, + S_IFOG02, + S_IFOG2, + S_IFOG3, + S_IFOG4, + S_IFOG5, + S_PLAY, + S_PLAY_RUN1, + S_PLAY_RUN2, + S_PLAY_RUN3, + S_PLAY_RUN4, + S_PLAY_ATK1, + S_PLAY_ATK2, + S_PLAY_PAIN, + S_PLAY_PAIN2, + S_PLAY_DIE1, + S_PLAY_DIE2, + S_PLAY_DIE3, + S_PLAY_DIE4, + S_PLAY_DIE5, + S_PLAY_DIE6, + S_PLAY_DIE7, + S_PLAY_XDIE1, + S_PLAY_XDIE2, + S_PLAY_XDIE3, + S_PLAY_XDIE4, + S_PLAY_XDIE5, + S_PLAY_XDIE6, + S_PLAY_XDIE7, + S_PLAY_XDIE8, + S_PLAY_XDIE9, + S_POSS_STND, + S_POSS_STND2, + S_POSS_RUN1, + S_POSS_RUN2, + S_POSS_RUN3, + S_POSS_RUN4, + S_POSS_RUN5, + S_POSS_RUN6, + S_POSS_RUN7, + S_POSS_RUN8, + S_POSS_ATK1, + S_POSS_ATK2, + S_POSS_ATK3, + S_POSS_PAIN, + S_POSS_PAIN2, + S_POSS_DIE1, + S_POSS_DIE2, + S_POSS_DIE3, + S_POSS_DIE4, + S_POSS_DIE5, + S_POSS_XDIE1, + S_POSS_XDIE2, + S_POSS_XDIE3, + S_POSS_XDIE4, + S_POSS_XDIE5, + S_POSS_XDIE6, + S_POSS_XDIE7, + S_POSS_XDIE8, + S_POSS_XDIE9, + S_POSS_RAISE1, + S_POSS_RAISE2, + S_POSS_RAISE3, + S_POSS_RAISE4, + S_SPOS_STND, + S_SPOS_STND2, + S_SPOS_RUN1, + S_SPOS_RUN2, + S_SPOS_RUN3, + S_SPOS_RUN4, + S_SPOS_RUN5, + S_SPOS_RUN6, + S_SPOS_RUN7, + S_SPOS_RUN8, + S_SPOS_ATK1, + S_SPOS_ATK2, + S_SPOS_ATK3, + S_SPOS_PAIN, + S_SPOS_PAIN2, + S_SPOS_DIE1, + S_SPOS_DIE2, + S_SPOS_DIE3, + S_SPOS_DIE4, + S_SPOS_DIE5, + S_SPOS_XDIE1, + S_SPOS_XDIE2, + S_SPOS_XDIE3, + S_SPOS_XDIE4, + S_SPOS_XDIE5, + S_SPOS_XDIE6, + S_SPOS_XDIE7, + S_SPOS_XDIE8, + S_SPOS_XDIE9, + S_SPOS_RAISE1, + S_SPOS_RAISE2, + S_SPOS_RAISE3, + S_SPOS_RAISE4, + S_SPOS_RAISE5, + S_VILE_STND, + S_VILE_STND2, + S_VILE_RUN1, + S_VILE_RUN2, + S_VILE_RUN3, + S_VILE_RUN4, + S_VILE_RUN5, + S_VILE_RUN6, + S_VILE_RUN7, + S_VILE_RUN8, + S_VILE_RUN9, + S_VILE_RUN10, + S_VILE_RUN11, + S_VILE_RUN12, + S_VILE_ATK1, + S_VILE_ATK2, + S_VILE_ATK3, + S_VILE_ATK4, + S_VILE_ATK5, + S_VILE_ATK6, + S_VILE_ATK7, + S_VILE_ATK8, + S_VILE_ATK9, + S_VILE_ATK10, + S_VILE_ATK11, + S_VILE_HEAL1, + S_VILE_HEAL2, + S_VILE_HEAL3, + S_VILE_PAIN, + S_VILE_PAIN2, + S_VILE_DIE1, + S_VILE_DIE2, + S_VILE_DIE3, + S_VILE_DIE4, + S_VILE_DIE5, + S_VILE_DIE6, + S_VILE_DIE7, + S_VILE_DIE8, + S_VILE_DIE9, + S_VILE_DIE10, + S_FIRE1, + S_FIRE2, + S_FIRE3, + S_FIRE4, + S_FIRE5, + S_FIRE6, + S_FIRE7, + S_FIRE8, + S_FIRE9, + S_FIRE10, + S_FIRE11, + S_FIRE12, + S_FIRE13, + S_FIRE14, + S_FIRE15, + S_FIRE16, + S_FIRE17, + S_FIRE18, + S_FIRE19, + S_FIRE20, + S_FIRE21, + S_FIRE22, + S_FIRE23, + S_FIRE24, + S_FIRE25, + S_FIRE26, + S_FIRE27, + S_FIRE28, + S_FIRE29, + S_FIRE30, + S_SMOKE1, + S_SMOKE2, + S_SMOKE3, + S_SMOKE4, + S_SMOKE5, + S_TRACER, + S_TRACER2, + S_TRACEEXP1, + S_TRACEEXP2, + S_TRACEEXP3, + S_SKEL_STND, + S_SKEL_STND2, + S_SKEL_RUN1, + S_SKEL_RUN2, + S_SKEL_RUN3, + S_SKEL_RUN4, + S_SKEL_RUN5, + S_SKEL_RUN6, + S_SKEL_RUN7, + S_SKEL_RUN8, + S_SKEL_RUN9, + S_SKEL_RUN10, + S_SKEL_RUN11, + S_SKEL_RUN12, + S_SKEL_FIST1, + S_SKEL_FIST2, + S_SKEL_FIST3, + S_SKEL_FIST4, + S_SKEL_MISS1, + S_SKEL_MISS2, + S_SKEL_MISS3, + S_SKEL_MISS4, + S_SKEL_PAIN, + S_SKEL_PAIN2, + S_SKEL_DIE1, + S_SKEL_DIE2, + S_SKEL_DIE3, + S_SKEL_DIE4, + S_SKEL_DIE5, + S_SKEL_DIE6, + S_SKEL_RAISE1, + S_SKEL_RAISE2, + S_SKEL_RAISE3, + S_SKEL_RAISE4, + S_SKEL_RAISE5, + S_SKEL_RAISE6, + S_FATSHOT1, + S_FATSHOT2, + S_FATSHOTX1, + S_FATSHOTX2, + S_FATSHOTX3, + S_FATT_STND, + S_FATT_STND2, + S_FATT_RUN1, + S_FATT_RUN2, + S_FATT_RUN3, + S_FATT_RUN4, + S_FATT_RUN5, + S_FATT_RUN6, + S_FATT_RUN7, + S_FATT_RUN8, + S_FATT_RUN9, + S_FATT_RUN10, + S_FATT_RUN11, + S_FATT_RUN12, + S_FATT_ATK1, + S_FATT_ATK2, + S_FATT_ATK3, + S_FATT_ATK4, + S_FATT_ATK5, + S_FATT_ATK6, + S_FATT_ATK7, + S_FATT_ATK8, + S_FATT_ATK9, + S_FATT_ATK10, + S_FATT_PAIN, + S_FATT_PAIN2, + S_FATT_DIE1, + S_FATT_DIE2, + S_FATT_DIE3, + S_FATT_DIE4, + S_FATT_DIE5, + S_FATT_DIE6, + S_FATT_DIE7, + S_FATT_DIE8, + S_FATT_DIE9, + S_FATT_DIE10, + S_FATT_RAISE1, + S_FATT_RAISE2, + S_FATT_RAISE3, + S_FATT_RAISE4, + S_FATT_RAISE5, + S_FATT_RAISE6, + S_FATT_RAISE7, + S_FATT_RAISE8, + S_CPOS_STND, + S_CPOS_STND2, + S_CPOS_RUN1, + S_CPOS_RUN2, + S_CPOS_RUN3, + S_CPOS_RUN4, + S_CPOS_RUN5, + S_CPOS_RUN6, + S_CPOS_RUN7, + S_CPOS_RUN8, + S_CPOS_ATK1, + S_CPOS_ATK2, + S_CPOS_ATK3, + S_CPOS_ATK4, + S_CPOS_PAIN, + S_CPOS_PAIN2, + S_CPOS_DIE1, + S_CPOS_DIE2, + S_CPOS_DIE3, + S_CPOS_DIE4, + S_CPOS_DIE5, + S_CPOS_DIE6, + S_CPOS_DIE7, + S_CPOS_XDIE1, + S_CPOS_XDIE2, + S_CPOS_XDIE3, + S_CPOS_XDIE4, + S_CPOS_XDIE5, + S_CPOS_XDIE6, + S_CPOS_RAISE1, + S_CPOS_RAISE2, + S_CPOS_RAISE3, + S_CPOS_RAISE4, + S_CPOS_RAISE5, + S_CPOS_RAISE6, + S_CPOS_RAISE7, + S_TROO_STND, + S_TROO_STND2, + S_TROO_RUN1, + S_TROO_RUN2, + S_TROO_RUN3, + S_TROO_RUN4, + S_TROO_RUN5, + S_TROO_RUN6, + S_TROO_RUN7, + S_TROO_RUN8, + S_TROO_ATK1, + S_TROO_ATK2, + S_TROO_ATK3, + S_TROO_PAIN, + S_TROO_PAIN2, + S_TROO_DIE1, + S_TROO_DIE2, + S_TROO_DIE3, + S_TROO_DIE4, + S_TROO_DIE5, + S_TROO_XDIE1, + S_TROO_XDIE2, + S_TROO_XDIE3, + S_TROO_XDIE4, + S_TROO_XDIE5, + S_TROO_XDIE6, + S_TROO_XDIE7, + S_TROO_XDIE8, + S_TROO_RAISE1, + S_TROO_RAISE2, + S_TROO_RAISE3, + S_TROO_RAISE4, + S_TROO_RAISE5, + S_SARG_STND, + S_SARG_STND2, + S_SARG_RUN1, + S_SARG_RUN2, + S_SARG_RUN3, + S_SARG_RUN4, + S_SARG_RUN5, + S_SARG_RUN6, + S_SARG_RUN7, + S_SARG_RUN8, + S_SARG_ATK1, + S_SARG_ATK2, + S_SARG_ATK3, + S_SARG_PAIN, + S_SARG_PAIN2, + S_SARG_DIE1, + S_SARG_DIE2, + S_SARG_DIE3, + S_SARG_DIE4, + S_SARG_DIE5, + S_SARG_DIE6, + S_SARG_RAISE1, + S_SARG_RAISE2, + S_SARG_RAISE3, + S_SARG_RAISE4, + S_SARG_RAISE5, + S_SARG_RAISE6, + S_HEAD_STND, + S_HEAD_RUN1, + S_HEAD_ATK1, + S_HEAD_ATK2, + S_HEAD_ATK3, + S_HEAD_PAIN, + S_HEAD_PAIN2, + S_HEAD_PAIN3, + S_HEAD_DIE1, + S_HEAD_DIE2, + S_HEAD_DIE3, + S_HEAD_DIE4, + S_HEAD_DIE5, + S_HEAD_DIE6, + S_HEAD_RAISE1, + S_HEAD_RAISE2, + S_HEAD_RAISE3, + S_HEAD_RAISE4, + S_HEAD_RAISE5, + S_HEAD_RAISE6, + S_BRBALL1, + S_BRBALL2, + S_BRBALLX1, + S_BRBALLX2, + S_BRBALLX3, + S_BOSS_STND, + S_BOSS_STND2, + S_BOSS_RUN1, + S_BOSS_RUN2, + S_BOSS_RUN3, + S_BOSS_RUN4, + S_BOSS_RUN5, + S_BOSS_RUN6, + S_BOSS_RUN7, + S_BOSS_RUN8, + S_BOSS_ATK1, + S_BOSS_ATK2, + S_BOSS_ATK3, + S_BOSS_PAIN, + S_BOSS_PAIN2, + S_BOSS_DIE1, + S_BOSS_DIE2, + S_BOSS_DIE3, + S_BOSS_DIE4, + S_BOSS_DIE5, + S_BOSS_DIE6, + S_BOSS_DIE7, + S_BOSS_RAISE1, + S_BOSS_RAISE2, + S_BOSS_RAISE3, + S_BOSS_RAISE4, + S_BOSS_RAISE5, + S_BOSS_RAISE6, + S_BOSS_RAISE7, + S_BOS2_STND, + S_BOS2_STND2, + S_BOS2_RUN1, + S_BOS2_RUN2, + S_BOS2_RUN3, + S_BOS2_RUN4, + S_BOS2_RUN5, + S_BOS2_RUN6, + S_BOS2_RUN7, + S_BOS2_RUN8, + S_BOS2_ATK1, + S_BOS2_ATK2, + S_BOS2_ATK3, + S_BOS2_PAIN, + S_BOS2_PAIN2, + S_BOS2_DIE1, + S_BOS2_DIE2, + S_BOS2_DIE3, + S_BOS2_DIE4, + S_BOS2_DIE5, + S_BOS2_DIE6, + S_BOS2_DIE7, + S_BOS2_RAISE1, + S_BOS2_RAISE2, + S_BOS2_RAISE3, + S_BOS2_RAISE4, + S_BOS2_RAISE5, + S_BOS2_RAISE6, + S_BOS2_RAISE7, + S_SKULL_STND, + S_SKULL_STND2, + S_SKULL_RUN1, + S_SKULL_RUN2, + S_SKULL_ATK1, + S_SKULL_ATK2, + S_SKULL_ATK3, + S_SKULL_ATK4, + S_SKULL_PAIN, + S_SKULL_PAIN2, + S_SKULL_DIE1, + S_SKULL_DIE2, + S_SKULL_DIE3, + S_SKULL_DIE4, + S_SKULL_DIE5, + S_SKULL_DIE6, + S_SPID_STND, + S_SPID_STND2, + S_SPID_RUN1, + S_SPID_RUN2, + S_SPID_RUN3, + S_SPID_RUN4, + S_SPID_RUN5, + S_SPID_RUN6, + S_SPID_RUN7, + S_SPID_RUN8, + S_SPID_RUN9, + S_SPID_RUN10, + S_SPID_RUN11, + S_SPID_RUN12, + S_SPID_ATK1, + S_SPID_ATK2, + S_SPID_ATK3, + S_SPID_ATK4, + S_SPID_PAIN, + S_SPID_PAIN2, + S_SPID_DIE1, + S_SPID_DIE2, + S_SPID_DIE3, + S_SPID_DIE4, + S_SPID_DIE5, + S_SPID_DIE6, + S_SPID_DIE7, + S_SPID_DIE8, + S_SPID_DIE9, + S_SPID_DIE10, + S_SPID_DIE11, + S_BSPI_STND, + S_BSPI_STND2, + S_BSPI_SIGHT, + S_BSPI_RUN1, + S_BSPI_RUN2, + S_BSPI_RUN3, + S_BSPI_RUN4, + S_BSPI_RUN5, + S_BSPI_RUN6, + S_BSPI_RUN7, + S_BSPI_RUN8, + S_BSPI_RUN9, + S_BSPI_RUN10, + S_BSPI_RUN11, + S_BSPI_RUN12, + S_BSPI_ATK1, + S_BSPI_ATK2, + S_BSPI_ATK3, + S_BSPI_ATK4, + S_BSPI_PAIN, + S_BSPI_PAIN2, + S_BSPI_DIE1, + S_BSPI_DIE2, + S_BSPI_DIE3, + S_BSPI_DIE4, + S_BSPI_DIE5, + S_BSPI_DIE6, + S_BSPI_DIE7, + S_BSPI_RAISE1, + S_BSPI_RAISE2, + S_BSPI_RAISE3, + S_BSPI_RAISE4, + S_BSPI_RAISE5, + S_BSPI_RAISE6, + S_BSPI_RAISE7, + S_ARACH_PLAZ, + S_ARACH_PLAZ2, + S_ARACH_PLEX, + S_ARACH_PLEX2, + S_ARACH_PLEX3, + S_ARACH_PLEX4, + S_ARACH_PLEX5, + S_CYBER_STND, + S_CYBER_STND2, + S_CYBER_RUN1, + S_CYBER_RUN2, + S_CYBER_RUN3, + S_CYBER_RUN4, + S_CYBER_RUN5, + S_CYBER_RUN6, + S_CYBER_RUN7, + S_CYBER_RUN8, + S_CYBER_ATK1, + S_CYBER_ATK2, + S_CYBER_ATK3, + S_CYBER_ATK4, + S_CYBER_ATK5, + S_CYBER_ATK6, + S_CYBER_PAIN, + S_CYBER_DIE1, + S_CYBER_DIE2, + S_CYBER_DIE3, + S_CYBER_DIE4, + S_CYBER_DIE5, + S_CYBER_DIE6, + S_CYBER_DIE7, + S_CYBER_DIE8, + S_CYBER_DIE9, + S_CYBER_DIE10, + S_PAIN_STND, + S_PAIN_RUN1, + S_PAIN_RUN2, + S_PAIN_RUN3, + S_PAIN_RUN4, + S_PAIN_RUN5, + S_PAIN_RUN6, + S_PAIN_ATK1, + S_PAIN_ATK2, + S_PAIN_ATK3, + S_PAIN_ATK4, + S_PAIN_PAIN, + S_PAIN_PAIN2, + S_PAIN_DIE1, + S_PAIN_DIE2, + S_PAIN_DIE3, + S_PAIN_DIE4, + S_PAIN_DIE5, + S_PAIN_DIE6, + S_PAIN_RAISE1, + S_PAIN_RAISE2, + S_PAIN_RAISE3, + S_PAIN_RAISE4, + S_PAIN_RAISE5, + S_PAIN_RAISE6, + S_SSWV_STND, + S_SSWV_STND2, + S_SSWV_RUN1, + S_SSWV_RUN2, + S_SSWV_RUN3, + S_SSWV_RUN4, + S_SSWV_RUN5, + S_SSWV_RUN6, + S_SSWV_RUN7, + S_SSWV_RUN8, + S_SSWV_ATK1, + S_SSWV_ATK2, + S_SSWV_ATK3, + S_SSWV_ATK4, + S_SSWV_ATK5, + S_SSWV_ATK6, + S_SSWV_PAIN, + S_SSWV_PAIN2, + S_SSWV_DIE1, + S_SSWV_DIE2, + S_SSWV_DIE3, + S_SSWV_DIE4, + S_SSWV_DIE5, + S_SSWV_XDIE1, + S_SSWV_XDIE2, + S_SSWV_XDIE3, + S_SSWV_XDIE4, + S_SSWV_XDIE5, + S_SSWV_XDIE6, + S_SSWV_XDIE7, + S_SSWV_XDIE8, + S_SSWV_XDIE9, + S_SSWV_RAISE1, + S_SSWV_RAISE2, + S_SSWV_RAISE3, + S_SSWV_RAISE4, + S_SSWV_RAISE5, + S_KEENSTND, + S_COMMKEEN, + S_COMMKEEN2, + S_COMMKEEN3, + S_COMMKEEN4, + S_COMMKEEN5, + S_COMMKEEN6, + S_COMMKEEN7, + S_COMMKEEN8, + S_COMMKEEN9, + S_COMMKEEN10, + S_COMMKEEN11, + S_COMMKEEN12, + S_KEENPAIN, + S_KEENPAIN2, + S_BRAIN, + S_BRAIN_PAIN, + S_BRAIN_DIE1, + S_BRAIN_DIE2, + S_BRAIN_DIE3, + S_BRAIN_DIE4, + S_BRAINEYE, + S_BRAINEYESEE, + S_BRAINEYE1, + S_SPAWN1, + S_SPAWN2, + S_SPAWN3, + S_SPAWN4, + S_SPAWNFIRE1, + S_SPAWNFIRE2, + S_SPAWNFIRE3, + S_SPAWNFIRE4, + S_SPAWNFIRE5, + S_SPAWNFIRE6, + S_SPAWNFIRE7, + S_SPAWNFIRE8, + S_BRAINEXPLODE1, + S_BRAINEXPLODE2, + S_BRAINEXPLODE3, + S_ARM1, + S_ARM1A, + S_ARM2, + S_ARM2A, + S_BAR1, + S_BAR2, + S_BEXP, + S_BEXP2, + S_BEXP3, + S_BEXP4, + S_BEXP5, + S_BBAR1, + S_BBAR2, + S_BBAR3, + S_BON1, + S_BON1A, + S_BON1B, + S_BON1C, + S_BON1D, + S_BON1E, + S_BON2, + S_BON2A, + S_BON2B, + S_BON2C, + S_BON2D, + S_BON2E, + S_BKEY, + S_BKEY2, + S_RKEY, + S_RKEY2, + S_YKEY, + S_YKEY2, + S_BSKULL, + S_BSKULL2, + S_RSKULL, + S_RSKULL2, + S_YSKULL, + S_YSKULL2, + S_STIM, + S_MEDI, + S_SOUL, + S_SOUL2, + S_SOUL3, + S_SOUL4, + S_SOUL5, + S_SOUL6, + S_PINV, + S_PINV2, + S_PINV3, + S_PINV4, + S_PSTR, + S_PINS, + S_PINS2, + S_PINS3, + S_PINS4, + S_MEGA, + S_MEGA2, + S_MEGA3, + S_MEGA4, + S_SUIT, + S_PMAP, + S_PMAP2, + S_PMAP3, + S_PMAP4, + S_PMAP5, + S_PMAP6, + S_PVIS, + S_PVIS2, + S_CLIP, + S_AMMO, + S_ROCK, + S_BROK, + S_CELL, + S_CELP, + S_SHEL, + S_SBOX, + S_BPAK, + S_BFUG, + S_MGUN, + S_CSAW, + S_LAUN, + S_PLAS, + S_SHOT, + S_SHOT2, + S_COLU, + S_STALAG, + S_BLOODYTWITCH, + S_BLOODYTWITCH2, + S_BLOODYTWITCH3, + S_BLOODYTWITCH4, + S_DEADTORSO, + S_DEADBOTTOM, + S_HEADSONSTICK, + S_GIBS, + S_HEADONASTICK, + S_HEADCANDLES, + S_HEADCANDLES2, + S_DEADSTICK, + S_LIVESTICK, + S_LIVESTICK2, + S_MEAT2, + S_MEAT3, + S_MEAT4, + S_MEAT5, + S_STALAGTITE, + S_TALLGRNCOL, + S_SHRTGRNCOL, + S_TALLREDCOL, + S_SHRTREDCOL, + S_CANDLESTIK, + S_CANDELABRA, + S_SKULLCOL, + S_TORCHTREE, + S_BIGTREE, + S_TECHPILLAR, + S_EVILEYE, + S_EVILEYE2, + S_EVILEYE3, + S_EVILEYE4, + S_FLOATSKULL, + S_FLOATSKULL2, + S_FLOATSKULL3, + S_HEARTCOL, + S_HEARTCOL2, + S_BLUETORCH, + S_BLUETORCH2, + S_BLUETORCH3, + S_BLUETORCH4, + S_GREENTORCH, + S_GREENTORCH2, + S_GREENTORCH3, + S_GREENTORCH4, + S_REDTORCH, + S_REDTORCH2, + S_REDTORCH3, + S_REDTORCH4, + S_BTORCHSHRT, + S_BTORCHSHRT2, + S_BTORCHSHRT3, + S_BTORCHSHRT4, + S_GTORCHSHRT, + S_GTORCHSHRT2, + S_GTORCHSHRT3, + S_GTORCHSHRT4, + S_RTORCHSHRT, + S_RTORCHSHRT2, + S_RTORCHSHRT3, + S_RTORCHSHRT4, + S_HANGNOGUTS, + S_HANGBNOBRAIN, + S_HANGTLOOKDN, + S_HANGTSKULL, + S_HANGTLOOKUP, + S_HANGTNOBRAIN, + S_COLONGIBS, + S_SMALLPOOL, + S_BRAINSTEM, + S_TECHLAMP, + S_TECHLAMP2, + S_TECHLAMP3, + S_TECHLAMP4, + S_TECH2LAMP, + S_TECH2LAMP2, + S_TECH2LAMP3, + S_TECH2LAMP4, + NUMSTATES +} statenum_t; + + +typedef struct +{ + spritenum_t sprite; + long frame; + long tics; + actionf_t action; + statenum_t nextstate; + long misc1, misc2; +} state_t; + + +extern state_t states[NUMSTATES]; +extern char* sprnames[NUMSPRITES + 1]; + + +typedef enum +{ + MT_PLAYER, + MT_POSSESSED, + MT_SHOTGUY, + MT_VILE, + MT_FIRE, + MT_UNDEAD, + MT_TRACER, + MT_SMOKE, + MT_FATSO, + MT_FATSHOT, + MT_CHAINGUY, + MT_TROOP, + MT_SERGEANT, + MT_SHADOWS, + MT_HEAD, + MT_BRUISER, + MT_BRUISERSHOT, + MT_KNIGHT, + MT_SKULL, + MT_SPIDER, + MT_BABY, + MT_CYBORG, + MT_PAIN, + MT_WOLFSS, + MT_KEEN, + MT_BOSSBRAIN, + MT_BOSSSPIT, + MT_BOSSTARGET, + MT_SPAWNSHOT, + MT_SPAWNFIRE, + MT_BARREL, + MT_TROOPSHOT, + MT_HEADSHOT, + MT_ROCKET, + MT_PLASMA, + MT_BFG, + MT_ARACHPLAZ, + MT_PUFF, + MT_BLOOD, + MT_TFOG, + MT_IFOG, + MT_TELEPORTMAN, + MT_EXTRABFG, + MT_MISC0, + MT_MISC1, + MT_MISC2, + MT_MISC3, + MT_MISC4, + MT_MISC5, + MT_MISC6, + MT_MISC7, + MT_MISC8, + MT_MISC9, + MT_MISC10, + MT_MISC11, + MT_MISC12, + MT_INV, + MT_MISC13, + MT_INS, + MT_MISC14, + MT_MISC15, + MT_MISC16, + MT_MEGA, + MT_CLIP, + MT_MISC17, + MT_MISC18, + MT_MISC19, + MT_MISC20, + MT_MISC21, + MT_MISC22, + MT_MISC23, + MT_MISC24, + MT_MISC25, + MT_CHAINGUN, + MT_MISC26, + MT_MISC27, + MT_MISC28, + MT_SHOTGUN, + MT_SUPERSHOTGUN, + MT_MISC29, + MT_MISC30, + MT_MISC31, + MT_MISC32, + MT_MISC33, + MT_MISC34, + MT_MISC35, + MT_MISC36, + MT_MISC37, + MT_MISC38, + MT_MISC39, + MT_MISC40, + MT_MISC41, + MT_MISC42, + MT_MISC43, + MT_MISC44, + MT_MISC45, + MT_MISC46, + MT_MISC47, + MT_MISC48, + MT_MISC49, + MT_MISC50, + MT_MISC51, + MT_MISC52, + MT_MISC53, + MT_MISC54, + MT_MISC55, + MT_MISC56, + MT_MISC57, + MT_MISC58, + MT_MISC59, + MT_MISC60, + MT_MISC61, + MT_MISC62, + MT_MISC63, + MT_MISC64, + MT_MISC65, + MT_MISC66, + MT_MISC67, + MT_MISC68, + MT_MISC69, + MT_MISC70, + MT_MISC71, + MT_MISC72, + MT_MISC73, + MT_MISC74, + MT_MISC75, + MT_MISC76, + MT_MISC77, + MT_MISC78, + MT_MISC79, + MT_MISC80, + MT_MISC81, + MT_MISC82, + MT_MISC83, + MT_MISC84, + MT_MISC85, + MT_MISC86, + NUMMOBJTYPES +} mobjtype_t; + + +typedef struct +{ + int doomednum; + int spawnstate; + int spawnhealth; + int seestate; + int seesound; + int reactiontime; + int attacksound; + int painstate; + int painchance; + int painsound; + int meleestate; + int missilestate; + int deathstate; + int xdeathstate; + int deathsound; + int speed; + int radius; + int height; + int mass; + int damage; + int activesound; + int flags; + int raisestate; +} mobjinfo_t; + + +extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; + + +#endif + + +#ifndef __M_ARGV__ +#define __M_ARGV__ + + +// +// MISC +// +extern int myargc; +extern char** myargv; + +// Returns the position of the given parameter +// in the arg list (0 if not found). +int M_CheckParm(char* check); + + +#endif + + +#ifndef __M_CHEAT__ +#define __M_CHEAT__ + + +// +// CHEAT SEQUENCE PACKAGE +// + +#define SCRAMBLE(a) \ +((((a)&1)<<7) + (((a)&2)<<5) + ((a)&4) + (((a)&8)<<1) \ + + (((a)&16)>>1) + ((a)&32) + (((a)&64)>>5) + (((a)&128)>>7)) + + +typedef struct +{ + unsigned char* sequence; + unsigned char* p; +} cheatseq_t; + + +int cht_CheckCheat(cheatseq_t* cht, char key); +void cht_GetParam(cheatseq_t* cht, char* buffer); + + +#endif + + +#ifndef __M_FIXED__ +#define __M_FIXED__ + + +// +// Fixed point, 32bit as 16.16. +// +#define FRACBITS 16 +#define FRACUNIT (1< +// using from sounds.h +// +void S_StartSound(void* origin, int sound_id); + +// Will start a sound at a given volume. +void S_StartSoundAtVolume(void* origin, int sound_id, int volume); + +// Stop sound for thing at +void S_StopSound(void* origin); + +// Start music using from sounds.h +void S_StartMusic(int music_id); + +// Start music using from sounds.h, +// and set whether looping +void S_ChangeMusic(int music_id, int looping); + +// Stops the music fer sure. +void S_StopMusic(void); + +// Stop and resume music, during game PAUSE. +void S_PauseSound(void); +void S_ResumeSound(void); + +// +// Updates music & sounds +// +void S_UpdateSounds(void* listener); + +void S_SetMusicVolume(int volume); +void S_SetSfxVolume(int volume); + + +#endif + + +#ifndef __SOUNDS__ +#define __SOUNDS__ + + +// +// SoundFX struct. +// +typedef struct sfxinfo_struct sfxinfo_t; + +struct sfxinfo_struct +{ + // up to 6-character name + char* name; + + // Sfx singularity (only one at a time) + int singularity; + + // Sfx priority + int priority; + + // referenced sound if a link + sfxinfo_t* link; + + // pitch if a link + int pitch; + + // volume if a link + int volume; + + // sound data + void* data; + + // this is checked every second to see if sound + // can be thrown out (if 0, then decrement, if -1, + // then throw out, if > 0, then it is in use) + int usefulness; + + // lump number of sfx + int lumpnum; +}; + + +// +// MusicInfo struct. +// +typedef struct +{ + // up to 6-character name + char* name; + + // lump number of music + int lumpnum; + + // music data + void* data; + + // music handle once registered + int handle; +} musicinfo_t; + + +// the complete set of sound effects +extern sfxinfo_t S_sfx[]; + +// the complete set of music +extern musicinfo_t S_music[]; + +// +// Identifiers for all music in game. +// +typedef enum +{ + mus_None, + mus_e1m1, + mus_e1m2, + mus_e1m3, + mus_e1m4, + mus_e1m5, + mus_e1m6, + mus_e1m7, + mus_e1m8, + mus_e1m9, + mus_e2m1, + mus_e2m2, + mus_e2m3, + mus_e2m4, + mus_e2m5, + mus_e2m6, + mus_e2m7, + mus_e2m8, + mus_e2m9, + mus_e3m1, + mus_e3m2, + mus_e3m3, + mus_e3m4, + mus_e3m5, + mus_e3m6, + mus_e3m7, + mus_e3m8, + mus_e3m9, + mus_inter, + mus_intro, + mus_bunny, + mus_victor, + mus_introa, + mus_runnin, + mus_stalks, + mus_countd, + mus_betwee, + mus_doom, + mus_the_da, + mus_shawn, + mus_ddtblu, + mus_in_cit, + mus_dead, + mus_stlks2, + mus_theda2, + mus_doom2, + mus_ddtbl2, + mus_runni2, + mus_dead2, + mus_stlks3, + mus_romero, + mus_shawn2, + mus_messag, + mus_count2, + mus_ddtbl3, + mus_ampie, + mus_theda3, + mus_adrian, + mus_messg2, + mus_romer2, + mus_tense, + mus_shawn3, + mus_openin, + mus_evil, + mus_ultima, + mus_read_m, + mus_dm2ttl, + mus_dm2int, + NUMMUSIC +} musicenum_t; + + +// +// Identifiers for all sfx in game. +// +typedef enum +{ + sfx_None, + sfx_pistol, + sfx_shotgn, + sfx_sgcock, + sfx_dshtgn, + sfx_dbopn, + sfx_dbcls, + sfx_dbload, + sfx_plasma, + sfx_bfg, + sfx_sawup, + sfx_sawidl, + sfx_sawful, + sfx_sawhit, + sfx_rlaunc, + sfx_rxplod, + sfx_firsht, + sfx_firxpl, + sfx_pstart, + sfx_pstop, + sfx_doropn, + sfx_dorcls, + sfx_stnmov, + sfx_swtchn, + sfx_swtchx, + sfx_plpain, + sfx_dmpain, + sfx_popain, + sfx_vipain, + sfx_mnpain, + sfx_pepain, + sfx_slop, + sfx_itemup, + sfx_wpnup, + sfx_oof, + sfx_telept, + sfx_posit1, + sfx_posit2, + sfx_posit3, + sfx_bgsit1, + sfx_bgsit2, + sfx_sgtsit, + sfx_cacsit, + sfx_brssit, + sfx_cybsit, + sfx_spisit, + sfx_bspsit, + sfx_kntsit, + sfx_vilsit, + sfx_mansit, + sfx_pesit, + sfx_sklatk, + sfx_sgtatk, + sfx_skepch, + sfx_vilatk, + sfx_claw, + sfx_skeswg, + sfx_pldeth, + sfx_pdiehi, + sfx_podth1, + sfx_podth2, + sfx_podth3, + sfx_bgdth1, + sfx_bgdth2, + sfx_sgtdth, + sfx_cacdth, + sfx_skldth, + sfx_brsdth, + sfx_cybdth, + sfx_spidth, + sfx_bspdth, + sfx_vildth, + sfx_kntdth, + sfx_pedth, + sfx_skedth, + sfx_posact, + sfx_bgact, + sfx_dmact, + sfx_bspact, + sfx_bspwlk, + sfx_vilact, + sfx_noway, + sfx_barexp, + sfx_punch, + sfx_hoof, + sfx_metal, + sfx_chgun, + sfx_tink, + sfx_bdopn, + sfx_bdcls, + sfx_itmbk, + sfx_flame, + sfx_flamst, + sfx_getpow, + sfx_bospit, + sfx_boscub, + sfx_bossit, + sfx_bospn, + sfx_bosdth, + sfx_manatk, + sfx_mandth, + sfx_sssit, + sfx_ssdth, + sfx_keenpn, + sfx_keendt, + sfx_skeact, + sfx_skesit, + sfx_skeatk, + sfx_radio, + NUMSFX +} sfxenum_t; + +#endif + + +#ifndef __STSTUFF_H__ +#define __STSTUFF_H__ + +//#include "doomtype.h" +//#include "d_event.h" + +// Size of statusbar. +// Now sensitive for scaling. +#define ST_HEIGHT (32 * SCREEN_MUL) +#define ST_WIDTH SCREENWIDTH +#define ST_Y (SCREENHEIGHT - ST_HEIGHT) + + +// +// STATUS BAR +// + +// Called by main loop. +doom_boolean ST_Responder(event_t* ev); + +// Called by main loop. +void ST_Ticker(void); + +// Called by main loop. +void ST_Drawer(doom_boolean fullscreen, doom_boolean refresh); + +// Called when the console player is spawned on each level. +void ST_Start(void); + +// Called by startup code. +void ST_Init(void); + + +// States for status bar code. +typedef enum +{ + AutomapState, + FirstPersonState +} st_stateenum_t; + + +// States for the chat code. +typedef enum +{ + StartChatState, + WaitDestState, + GetChatState +} st_chatstateenum_t; + + +doom_boolean ST_Responder(event_t* ev); + +#endif + + +#ifndef __TABLES__ +#define __TABLES__ + + +#ifdef LINUX + +#else +#define PI 3.141592657 +#endif + +//#include "m_fixed.h" + +#define FINEANGLES 8192 +#define FINEMASK (FINEANGLES-1) + +// 0x100000000 to 0x2000 +#define ANGLETOFINESHIFT 19 + +// Effective size is 10240. +extern fixed_t finesine[5 * FINEANGLES / 4]; + +// Re-use data, is just PI/2 pahse shift. +extern fixed_t* finecosine; + + +// Effective size is 4096. +extern fixed_t finetangent[FINEANGLES / 2]; + +// Binary Angle Measument, BAM. +#define ANG45 0x20000000 +#define ANG90 0x40000000 +#define ANG180 0x80000000 +#define ANG270 0xc0000000 + +#define SLOPERANGE 2048 +#define SLOPEBITS 11 +#define DBITS (FRACBITS - SLOPEBITS) + +typedef unsigned angle_t; + +// Effective size is 2049; +// The +1 size is to handle the case when x==y +// without additional checking. +extern angle_t tantoangle[SLOPERANGE + 1]; + + +// Utility function, +// called by R_PointToAngle. +int SlopeDiv(unsigned num, unsigned den); + + +#endif + + +#ifndef __P_MOBJ__ +#define __P_MOBJ__ + + +// Basics. +//#include "tables.h" +//#include "m_fixed.h" + +// We need the thinker_t stuff. +//#include "d_think.h" + +// We need the WAD data structure for Map things, +// from the THINGS lump. +//#include "doomdata.h" + +// States are tied to finite states are +// tied to animation frames. +// Needs precompiled tables/data structures. +//#include "info.h" + + +// +// 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? +// + +// +// Misc. mobj flags +// +typedef enum +{ + // Call P_SpecialThing when touched. + MF_SPECIAL = 1, + // Blocks. + MF_SOLID = 2, + // Can be hit. + MF_SHOOTABLE = 4, + // Don't use the sector links (invisible but touchable). + MF_NOSECTOR = 8, + // Don't use the blocklinks (inert but displayable) + MF_NOBLOCKMAP = 16, + + // Not to be activated by sound, deaf monster. + MF_AMBUSH = 32, + // Will try to attack right back. + MF_JUSTHIT = 64, + // Will take at least one step before attacking. + MF_JUSTATTACKED = 128, + // On level spawning (initial position), + // hang from ceiling instead of stand on floor. + MF_SPAWNCEILING = 256, + // Don't apply gravity (every tic), + // that is, object will float, keeping current height + // or changing it actively. + MF_NOGRAVITY = 512, + + // Movement flags. + // This allows jumps from high places. + MF_DROPOFF = 0x400, + // For players, will pick up items. + MF_PICKUP = 0x800, + // Player cheat. ??? + MF_NOCLIP = 0x1000, + // Player: keep info about sliding along walls. + MF_SLIDE = 0x2000, + // Allow moves to any height, no gravity. + // For active floaters, e.g. cacodemons, pain elementals. + MF_FLOAT = 0x4000, + // Don't cross lines + // ??? or look at heights on teleport. + MF_TELEPORT = 0x8000, + // Don't hit same species, explode on block. + // Player missiles as well as fireballs of various kinds. + MF_MISSILE = 0x10000, + // Dropped by a demon, not level spawned. + // E.g. ammo clips dropped by dying former humans. + MF_DROPPED = 0x20000, + // Use fuzzy draw (shadow demons or spectres), + // temporary player invisibility powerup. + MF_SHADOW = 0x40000, + // Flag: don't bleed when shot (use puff), + // barrels and shootable furniture shall not bleed. + MF_NOBLOOD = 0x80000, + // Don't stop moving halfway off a step, + // that is, have dead bodies slide down all the way. + MF_CORPSE = 0x100000, + // Floating to a height for a move, ??? + // don't auto float to target's height. + MF_INFLOAT = 0x200000, + + // On kill, count this enemy object + // towards intermission kill total. + // Happy gathering. + MF_COUNTKILL = 0x400000, + + // On picking up, count this item object + // towards intermission item total. + MF_COUNTITEM = 0x800000, + + // Special handling: skull in flight. + // Neither a cacodemon nor a missile. + MF_SKULLFLY = 0x1000000, + + // Don't spawn this object + // in death match mode (e.g. key cards). + MF_NOTDMATCH = 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 + MF_TRANSLATION = 0xc000000, + // Hmm ???. + MF_TRANSSHIFT = 26 +} mobjflag_t; + + +// Map Object definition. +typedef struct mobj_s +{ + // List: thinker links. + thinker_t thinker; + + // Info for drawing: position. + fixed_t x; + fixed_t y; + fixed_t z; + + // More list: links in sector (if needed) + struct mobj_s* snext; + struct mobj_s* sprev; + + //More drawing info: to determine current sprite. + angle_t angle; // orientation + spritenum_t sprite; // used to find patch_t and flip value + int frame; // might be ORed with FF_FULLBRIGHT + + // Interaction info, by BLOCKMAP. + // Links in blocks (if needed). + struct mobj_s* bnext; + struct mobj_s* bprev; + + struct subsector_s* subsector; + + // The closest interval over all contacted Sectors. + fixed_t floorz; + fixed_t ceilingz; + + // For movement checking. + fixed_t radius; + fixed_t height; + + // Momentums, used to update position. + fixed_t momx; + fixed_t momy; + fixed_t momz; + + // If == validcount, already checked. + int validcount; + + mobjtype_t type; + mobjinfo_t* info; // &mobjinfo[mobj->type] + + int tics; // state tic counter + state_t* state; + int flags; + int health; + + // Movement direction, movement generation (zig-zagging). + int movedir; // 0-7 + int movecount; // when 0, select a new dir + + // Thing being chased/attacked (or 0), + // also the originator for missiles. + struct mobj_s* target; + + // Reaction time: if non 0, don't attack yet. + // Used by player to freeze a bit after teleporting. + int reactiontime; + + // If >0, the target will be chased + // no matter what (even if shot) + int threshold; + + // Additional info record for player avatars only. + // Only valid if type == MT_PLAYER + struct player_s* player; + + // Player number last looked for. + int lastlook; + + // For nightmare respawn. + mapthing_t spawnpoint; + + // Thing being chased/attacked for tracers. + struct mobj_s* tracer; +} mobj_t; + + +#endif + + +#ifndef __P_PSPR__ +#define __P_PSPR__ + +// Basic data types. +// Needs fixed point, and BAM angles. +//#include "m_fixed.h" +//#include "tables.h" + +// Needs to include the precompiled +// sprite animation tables. +// Header generated by multigen utility. +// This includes all the data for thing animation, +// i.e. the Thing Atrributes table +// and the Frame Sequence table. +//#include "info.h" + +// +// Frame flags: +// handles maximum brightness (torches, muzzle flare, light sources) +// +#define FF_FULLBRIGHT 0x8000 // flag in thing->frame +#define FF_FRAMEMASK 0x7fff + + +// +// Overlay psprites are scaled shapes +// drawn directly on the view screen, +// coordinates are given for a 320*200 view screen. +// +typedef enum +{ + ps_weapon, + ps_flash, + NUMPSPRITES +} psprnum_t; + + +typedef struct +{ + state_t* state; // a 0 state means not active + int tics; + fixed_t sx; + fixed_t sy; +} pspdef_t; + + +#endif + + +#ifndef __D_PLAYER__ +#define __D_PLAYER__ + + +// The player data structure depends on a number +// of other structs: items (internal inventory), +// animation states (closely tied to the sprites +// used to represent them, unfortunately). +//#include "d_items.h" +//#include "p_pspr.h" + +// In addition, the player is just a special +// case of the generic moving object/actor. +//#include "p_mobj.h" + +// Finally, for odd reasons, the player input +// is buffered within the player data struct, +// as commands per game tick. +//#include "d_ticcmd.h" + + +// +// Player states. +// +typedef enum +{ + // Playing or camping. + PST_LIVE, + // Dead on the ground, view follows killer. + PST_DEAD, + // Ready to restart/respawn??? + PST_REBORN +} playerstate_t; + + +// +// Player internal flags, for cheats and debug. +// +typedef enum +{ + // No clipping, walk through barriers. + CF_NOCLIP = 1, + // No damage, no health loss. + CF_GODMODE = 2, + // Not really a cheat, just a debug aid. + CF_NOMOMENTUM = 4 +} cheat_t; + + +// +// Extended player object info: player_t +// +typedef struct player_s +{ + mobj_t* mo; + playerstate_t playerstate; + ticcmd_t cmd; + + // Determine POV, + // including viewpoint bobbing during movement. + // Focal origin above r.z + fixed_t viewz; + // Base height above floor for viewz. + fixed_t viewheight; + // Bob/squat speed. + fixed_t deltaviewheight; + // bounded/scaled total momentum. + fixed_t bob; + + // This is only used between levels, + // mo->health is used during levels. + int health; + int armorpoints; + // Armor type is 0-2. + int armortype; + + // Power ups. invinc and invis are tic counters. + int powers[NUMPOWERS]; + doom_boolean cards[NUMCARDS]; + doom_boolean backpack; + + // Frags, kills of other players. + int frags[MAXPLAYERS]; + weapontype_t readyweapon; + + // Is wp_nochange if not changing. + weapontype_t pendingweapon; + + doom_boolean weaponowned[NUMWEAPONS]; + int ammo[NUMAMMO]; + int maxammo[NUMAMMO]; + + // True if button down last tic. + int attackdown; + int usedown; + + // Bit flags, for cheats and debug. + // See cheat_t, above. + int cheats; + + // Refired shots are less accurate. + int refire; + + // For intermission stats. + int killcount; + int itemcount; + int secretcount; + + // Hint messages. + char* message; + + // For screen flashing (red or bright). + int damagecount; + int bonuscount; + + // Who did damage (0 for floors/ceilings). + mobj_t* attacker; + + // So gun flashes light up areas. + int extralight; + + // Current PLAYPAL, ??? + // can be set to REDCOLORMAP for pain, etc. + int fixedcolormap; + + // Player skin colorshift, + // 0-3 for which color to draw player. + int colormap; + + // Overlay view sprites (gun, etc). + pspdef_t psprites[NUMPSPRITES]; + + // True if secret level has been done. + doom_boolean didsecret; +} player_t; + + +// +// INTERMISSION +// Structure passed e.g. to WI_Start(wb) +// +typedef struct +{ + doom_boolean in; // whether the player is in game + + // Player stats, kills, collected items etc. + int skills; + int sitems; + int ssecret; + int stime; + int frags[4]; + int score; // current score on entry, modified on return +} wbplayerstruct_t; + + +typedef struct +{ + int epsd; // episode # (0-2) + + // if true, splash the secret level + doom_boolean didsecret; + + // previous and next levels, origin 0 + int last; + int next; + + int maxkills; + int maxitems; + int maxsecret; + int maxfrags; + + // the par time + int partime; + + // index of this player in game + int pnum; + + wbplayerstruct_t plyr[MAXPLAYERS]; +} wbstartstruct_t; + + +#endif + + +#ifndef __D_NET__ +#define __D_NET__ + + +//#include "d_player.h" + + +// +// Network play related stuff. +// There is a data struct that stores network +// communication related stuff, and another +// one that defines the actual packets to +// be transmitted. +// + +#define DOOMCOM_ID 0x12345678l + +// Max computers/players in a game. +#define MAXNETNODES 8 + +// Networking and tick handling related. +#define BACKUPTICS 12 + + +typedef enum +{ + CMD_SEND = 1, + CMD_GET = 2 +} command_t; + + +// +// Network packet data. +// +typedef struct +{ + // High bit is retransmit request. + unsigned checksum; + // Only valid if NCMD_RETRANSMIT. + byte retransmitfrom; + + byte starttic; + byte player; + byte numtics; + ticcmd_t cmds[BACKUPTICS]; +} doomdata_t; + + +typedef struct +{ + // Supposed to be DOOMCOM_ID? + long id; + + // DOOM executes an int to execute commands. + short intnum; + // Communication between DOOM and the driver. + // Is CMD_SEND or CMD_GET. + short command; + // Is dest for send, set by get (-1 = no packet). + short remotenode; + + // Number of bytes in doomdata to be sent + short datalength; + + // Info common to all nodes. + // Console is allways node 0. + short numnodes; + // Flag: 1 = no duplication, 2-5 = dup for slow nets. + short ticdup; + // Flag: 1 = send a backup tic in every packet. + short extratics; + // Flag: 1 = deathmatch. + short deathmatch; + // Flag: -1 = new game, 0-5 = load savegame + short savegame; + short episode; // 1-3 + short map; // 1-9 + short skill; // 1-5 + + // Info specific to this node. + short consoleplayer; + short numplayers; + + // These are related to the 3-display mode, + // in which two drones looking left and right + // were used to render two additional views + // on two additional computers. + // Probably not operational anymore. + // 1 = left, 0 = center, -1 = right + short angleoffset; + // 1 = drone + short drone; + + // The packet data to be sent. + doomdata_t data; +} doomcom_t; + + +// Create any new ticcmds and broadcast to other players. +void NetUpdate(void); + +// Broadcasts special packets to other players +// to notify of game exit +void D_QuitNetGame(void); + +//? how many ticks to run? +void TryRunTics(void); + + +#endif + + +#ifndef __D_STATE__ +#define __D_STATE__ + + +// We need globally shared data structures, +// for defining the global state variables. +//#include "doomdata.h" +//#include "d_net.h" + +// We need the playr data structure as well. +//#include "d_player.h" + + +// ------------------------ +// Command line parameters. +// +extern doom_boolean nomonsters; // checkparm of -nomonsters +extern doom_boolean respawnparm; // checkparm of -respawn +extern doom_boolean fastparm; // checkparm of -fast +extern doom_boolean devparm; // DEBUG: launched with -devparm + + +// ----------------------------------------------------- +// Game Mode - identify IWAD as shareware, retail etc. +// +extern GameMode_t gamemode; +extern GameMission_t gamemission; + +// Set if homebrew PWAD stuff has been added. +extern doom_boolean modifiedgame; + + +// ------------------------------------------- +// Language. +extern Language_t language; + + +// ------------------------------------------- +// Selected skill type, map etc. +// + +// Defaults for menu, methinks. +extern skill_t startskill; +extern int startepisode; +extern int startmap; + +extern doom_boolean autostart; + +// Selected by user. +extern skill_t gameskill; +extern int gameepisode; +extern int gamemap; + +// Nightmare mode flag, single player. +extern doom_boolean respawnmonsters; + +// Netgame? Only true if >1 player. +extern doom_boolean netgame; + +// Flag: true only if started as net deathmatch. +// An enum might handle altdeath/cooperative better. +extern doom_boolean deathmatch; + +// ------------------------- +// Internal parameters for sound rendering. +// These have been taken from the DOS version, +// but are not (yet) supported with Linux +// (e.g. no sound volume adjustment with menu. + +// These are not used, but should be (menu). +// From m_menu.c: +// Sound FX volume has default, 0 - 15 +// Music volume has default, 0 - 15 +// These are multiplied by 8. +extern int snd_SfxVolume; // maximum volume for sound +extern int snd_MusicVolume; // maximum volume for music + +// Current music/sfx card - index useless +// w/o a reference LUT in a sound module. +// Ideally, this would use indices found +// in: /usr/include/linux/soundcard.h +extern int snd_MusicDevice; +extern int snd_SfxDevice; + +// Config file? Same disclaimer as above. +extern int snd_DesiredMusicDevice; +extern int snd_DesiredSfxDevice; + + +// ------------------------- +// Status flags for refresh. +// + +// Depending on view size - no status bar? +// Note that there is no way to disable the +// status bar explicitely. +extern doom_boolean statusbaractive; + +extern doom_boolean automapactive; // In AutoMap mode? +extern doom_boolean menuactive; // Menu overlayed? +extern doom_boolean paused; // Game Pause? + +extern doom_boolean viewactive; + +extern doom_boolean nodrawers; +extern doom_boolean noblit; + +extern int viewwindowx; +extern int viewwindowy; +extern int viewheight; +extern int viewwidth; +extern int scaledviewwidth; + + +// This one is related to the 3-screen display mode. +// ANG90 = left side, ANG270 = right +extern int viewangleoffset; + +// Player taking events, and displaying. +extern int consoleplayer; +extern int displayplayer; + + +// ------------------------------------- +// Scores, rating. +// Statistics on a given map, for intermission. +// +extern int totalkills; +extern int totalitems; +extern int totalsecret; + +// Timer, for scores. +extern int levelstarttic; // gametic at level start +extern int leveltime; // tics in game play for par + + +// -------------------------------------- +// DEMO playback/recording related stuff. +// No demo, there is a human player in charge? +// Disable save/end game? +extern doom_boolean usergame; + +//? +extern doom_boolean demoplayback; +extern doom_boolean demorecording; + +// Quit after playing a demo from cmdline. +extern doom_boolean singledemo; + +//? +extern gamestate_t gamestate; + + +//----------------------------- +// Internal parameters, fixed. +// These are set by the engine, and not changed +// according to user inputs. Partly load from +// WAD, partly set at startup time. + +extern int gametic; + + +// Bookkeeping on players - state. +extern player_t players[MAXPLAYERS]; + +// Alive? Disconnected? +extern doom_boolean playeringame[MAXPLAYERS]; + + +// Player spawn spots for deathmatch. +#define MAX_DM_STARTS 10 +extern mapthing_t deathmatchstarts[MAX_DM_STARTS]; +extern mapthing_t* deathmatch_p; + +// Player spawn spots. +extern mapthing_t playerstarts[MAXPLAYERS]; + +// Intermission stats. +// Parameters for world map / intermission. +extern wbstartstruct_t wminfo; + + +// LUT of ammunition limits for each kind. +// This doubles with BackPack powerup item. +extern int maxammo[NUMAMMO]; + + +//----------------------------------------- +// Internal parameters, used for engine. +// + +// File handling stuff. +extern char basedefault[1024]; +extern void* debugfile; + +// if true, load all graphics at level load +extern doom_boolean precache; + +// wipegamestate can be set to -1 +// to force a wipe on the next draw +extern gamestate_t wipegamestate; + +extern int mouseSensitivity; +//? +// debug flag to cancel adaptiveness +extern doom_boolean singletics; + +extern int bodyqueslot; + + +// Needed to store the number of the dummy sky flat. +// Used for rendering, +// as well as tracking projectiles etc. +extern int skyflatnum; + + +// Netgame stuff (buffers and pointers, i.e. indices). + +// This is ??? +extern doomcom_t* doomcom; + +// This points inside doomcom. +extern doomdata_t* netbuffer; + + +extern ticcmd_t localcmds[BACKUPTICS]; +extern int rndindex; + +extern int maketic; +extern int nettics[MAXNETNODES]; + +extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; +extern int ticdup; + + +#endif + + +#ifndef __I_SOUND__ +#define __I_SOUND__ + + +//#include "doomdef.h" + + + +//#include "doomstat.h" +//#include "sounds.h" + + +// Init at program start... +void I_InitSound(); + +// ... update sound buffer and audio device at runtime... +void I_UpdateSound(void); +void I_SubmitSound(void); + +// ... shut down and relase at program termination. +void I_ShutdownSound(void); + + +// +// SFX I/O +// + +// Initialize channels? +void I_SetChannels(); + +// Get raw data lump index for sound descriptor. +int I_GetSfxLumpNum(sfxinfo_t* sfxinfo); + +// Starts a sound in a particular sound channel. +int I_StartSound(int id, int vol, int sep, int pitch, int priority); + +// Stops a sound channel. +void I_StopSound(int handle); + +// Called by S_*() functions +// to see if a channel is still playing. +// Returns 0 if no longer playing, 1 if playing. +int I_SoundIsPlaying(int handle); + +// Updates the volume, separation, +// and pitch of a sound channel. +void I_UpdateSoundParams(int handle, int vol, int sep, int pitch); + + +// +// MUSIC I/O +// +void I_InitMusic(void); +void I_ShutdownMusic(void); + +// Volume. +void I_SetMusicVolume(int volume); + +// PAUSE game handling. +void I_PauseSong(int handle); +void I_ResumeSong(int handle); + +// Registers a song handle to song data. +int I_RegisterSong(void* data); + +// Called by anything that wishes to start music. +// plays a song, and when the song is done, +// starts playing it again in an endless loop. +// Horrible thing to do, considering. +void I_PlaySong(int handle, int looping); + +// Stops a song over 3 seconds. +void I_StopSong(int handle); + +// See above (register), then think backwards +void I_UnRegisterSong(int handle); + +// Get next MIDI message +unsigned long I_TickSong(); + + +#endif + + +#ifndef __P_INTER__ +#define __P_INTER__ + + +//#include "d_player.h" + + +doom_boolean P_GivePower(player_t*, int); + + +#endif + + +#ifndef __R_DEFS__ +#define __R_DEFS__ + + +// Screenwidth. +//#include "doomdef.h" + +// Some more or less basic data types +// we depend on. +//#include "m_fixed.h" + +// We rely on the thinker data struct +// to handle sound origins in sectors. +//#include "d_think.h" +// SECTORS do store MObjs anyway. +//#include "p_mobj.h" + + +// Silhouette, needed for clipping Segs (mainly) +// and sprites representing things. +#define SIL_NONE 0 +#define SIL_BOTTOM 1 +#define SIL_TOP 2 +#define SIL_BOTH 3 + +#define MAXDRAWSEGS 256 + + +// +// INTERNAL MAP TYPES +// used by play and refresh +// + +// +// Your plain vanilla vertex. +// Note: transformed values not buffered locally, +// like some DOOM-alikes ("wt", "WebView") did. +// +typedef struct +{ + fixed_t x; + fixed_t y; +} vertex_t; + + +// Forward of LineDefs, for Sectors. +struct line_s; + +// Each sector has a degenmobj_t in its center +// for sound origin purposes. +// I suppose this does not handle sound from +// moving objects (doppler), because +// position is prolly just buffered, not +// updated. +typedef struct +{ + thinker_t thinker; // not used for anything + fixed_t x; + fixed_t y; + fixed_t z; +} degenmobj_t; + +// +// The SECTORS record, at runtime. +// Stores things/mobjs. +// +typedef struct +{ + fixed_t floorheight; + fixed_t ceilingheight; + short floorpic; + short ceilingpic; + short lightlevel; + short special; + short tag; + + // 0 = untraversed, 1,2 = sndlines -1 + int soundtraversed; + + // thing that made a sound (or null) + mobj_t* soundtarget; + + // mapblock bounding box for height changes + int blockbox[4]; + + // origin for any sounds played by the sector + degenmobj_t soundorg; + + // if == validcount, already checked + int validcount; + + // list of mobjs in sector + mobj_t* thinglist; + + // thinker_t for reversable actions + void* specialdata; + + int linecount; + struct line_s** lines; // [linecount] size +} sector_t; + + +// +// The SideDef. +// +typedef struct +{ + // add this to the calculated texture column + fixed_t textureoffset; + + // add this to the calculated texture top + fixed_t rowoffset; + + // Texture indices. + // We do not maintain names here. + short toptexture; + short bottomtexture; + short midtexture; + + // Sector the SideDef is facing. + sector_t* sector; +} side_t; + + +// +// Move clipping aid for LineDefs. +// +typedef enum +{ + ST_HORIZONTAL, + ST_VERTICAL, + ST_POSITIVE, + ST_NEGATIVE +} slopetype_t; + + +typedef struct line_s +{ + // Vertices, from v1 to v2. + vertex_t* v1; + vertex_t* v2; + + // Precalculated v2 - v1 for side checking. + fixed_t dx; + fixed_t dy; + + // Animation related. + short flags; + short special; + short tag; + + // Visual appearance: SideDefs. + // sidenum[1] will be -1 if one sided + short sidenum[2]; + + // Neat. Another bounding box, for the extent + // of the LineDef. + fixed_t bbox[4]; + + // To aid move clipping. + slopetype_t slopetype; + + // Front and back sector. + // Note: redundant? Can be retrieved from SideDefs. + sector_t* frontsector; + sector_t* backsector; + + // if == validcount, already checked + int validcount; + + // thinker_t for reversable actions + void* specialdata; +} line_t; + + +// +// A SubSector. +// References a Sector. +// Basically, this is a list of LineSegs, +// indicating the visible walls that define +// (all or some) sides of a convex BSP leaf. +// +typedef struct subsector_s +{ + sector_t* sector; + short numlines; + short firstline; +} subsector_t; + + +// +// The LineSeg. +// +typedef struct +{ + vertex_t* v1; + vertex_t* v2; + + fixed_t offset; + + angle_t angle; + + side_t* sidedef; + line_t* linedef; + + // Sector references. + // Could be retrieved from linedef, too. + // backsector is 0 for one sided lines + sector_t* frontsector; + sector_t* backsector; +} seg_t; + + +// +// BSP node. +// +typedef struct +{ + // Partition line. + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; + + // Bounding box for each child. + fixed_t bbox[2][4]; + + // If NF_SUBSECTOR its a subsector. + unsigned short children[2]; +} node_t; + + +// posts are runs of non masked source pixels +typedef struct +{ + byte topdelta; // -1 is the last post in a column + byte length; // length data bytes follows +} post_t; + +// column_t is a list of 0 or more post_t, (byte)-1 terminated +typedef post_t column_t; + + +// +// OTHER TYPES +// + +// This could be wider for >8 bit display. +// Indeed, true color support is posibble +// precalculating 24bpp lightmap/colormap LUT. +// from darkening PLAYPAL to all black. +// Could even us emore than 32 levels. +typedef byte lighttable_t; + + +// +// ? +// +typedef struct drawseg_s +{ + seg_t* curline; + int x1; + int x2; + + fixed_t scale1; + fixed_t scale2; + fixed_t scalestep; + + // 0=none, 1=bottom, 2=top, 3=both + int silhouette; + + // do not clip sprites above this + fixed_t bsilheight; + + // do not clip sprites below this + fixed_t tsilheight; + + // Pointers to lists for sprite clipping, + // all three adjusted so [x1] is first value. + short* sprtopclip; + short* sprbottomclip; + short* maskedtexturecol; +} drawseg_t; + + +// Patches. +// A patch holds one or more columns. +// Patches are used for sprites and all masked pictures, +// and we compose textures from the TEXTURE1/2 lists +// of patches. +typedef struct +{ + short width; // bounding box size + short height; + short leftoffset; // pixels to the left of origin + short topoffset; // pixels below the origin + int columnofs[8]; // only [width] used + // the [0] is &columnofs[width] +} patch_t; + + +// A vissprite_t is a thing +// that will be drawn during a refresh. +// I.e. a sprite object that is partly visible. +typedef struct vissprite_s +{ + // Doubly linked list. + struct vissprite_s* prev; + struct vissprite_s* next; + + int x1; + int x2; + + // for line side calculation + fixed_t gx; + fixed_t gy; + + // global bottom / top for silhouette clipping + fixed_t gz; + fixed_t gzt; + + // horizontal position of x1 + fixed_t startfrac; + + fixed_t scale; + + // negative if flipped + fixed_t xiscale; + + fixed_t texturemid; + int patch; + + // for color translation and shadow draw, + // maxbright frames as well + lighttable_t* colormap; + + int mobjflags; +} vissprite_t; + + +// +// Sprites are patches with a special naming convention +// so they can be recognized by R_InitSprites. +// The base name is NNNNFx or NNNNFxFx, with +// x indicating the rotation, x = 0, 1-7. +// The sprite and frame specified by a thing_t +// is range checked at run time. +// A sprite is a patch_t that is assumed to represent +// a three dimensional object and may have multiple +// rotations pre drawn. +// Horizontal flipping is used to save space, +// thus NNNNF2F5 defines a mirrored patch. +// Some sprites will only have one picture used +// for all views: NNNNF0 +// +typedef struct +{ + // If false use 0 for any position. + // Note: as eight entries are available, + // we might as well insert the same name eight times. + doom_boolean rotate; + + // Lump to use for view angles 0-7. + short lump[8]; + + // Flip bit (1 = flip) to use for view angles 0-7. + byte flip[8]; +} spriteframe_t; + + +// +// A sprite definition: +// a number of animation frames. +// +typedef struct +{ + int numframes; + spriteframe_t* spriteframes; +} spritedef_t; + + +// +// Now what is a visplane, anyway? +// +typedef struct +{ + fixed_t height; + int picnum; + int lightlevel; + int minx; + int maxx; + + // leave pads for [minx-1]/[maxx+1] + + byte pad1; + // Here lies the rub for all + // dynamic resize/change of resolution. + byte top[SCREENWIDTH]; + byte pad2; + byte pad3; + // See above. + byte bottom[SCREENWIDTH]; + byte pad4; +} visplane_t; + + +#endif + + +#ifndef __HULIB__ +#define __HULIB__ + + +// We are referring to patches. +//#include "r_defs.h" + + +// background and foreground screen numbers +// different from other modules. +#define BG 1 +#define FG 0 + +// font stuff +#define HU_CHARERASE KEY_BACKSPACE + +#define HU_MAXLINES 4 +#define HU_MAXLINELENGTH 80 + + +// +// Typedefs of widgets +// + +// Text Line widget +// (parent of Scrolling Text and Input Text widgets) +typedef struct +{ + // left-justified position of scrolling text window + int x; + int y; + + patch_t** f; // font + int sc; // start character + char l[HU_MAXLINELENGTH + 1]; // line of text + int len; // current line length + + // whether this line needs to be udpated + int needsupdate; +} hu_textline_t; + + +// Scrolling Text window widget +// (child of Text Line widget) +typedef struct +{ + hu_textline_t l[HU_MAXLINES]; // text lines to draw + int h; // height in lines + int cl; // current line number + + // pointer to doom_boolean stating whether to update window + doom_boolean* on; + doom_boolean laston; // last value of *->on. +} hu_stext_t; + + +// Input Text Line widget +// (child of Text Line widget) +typedef struct +{ + hu_textline_t l; // text line to input on + + // left margin past which I am not to delete characters + int lm; + + // pointer to doom_boolean stating whether to update window + doom_boolean* on; + doom_boolean laston; // last value of *->on; +} hu_itext_t; + + +// +// Widget creation, access, and update routines +// + +// initializes heads-up widget library +void HUlib_init(void); + +// +// textline code +// + +// clear a line of text +void HUlib_clearTextLine(hu_textline_t* t); + +void HUlib_initTextLine(hu_textline_t* t, int x, int y, patch_t** f, int sc); + +// returns success +doom_boolean HUlib_addCharToTextLine(hu_textline_t* t, char ch); + +// returns success +doom_boolean HUlib_delCharFromTextLine(hu_textline_t* t); + +// draws tline +void HUlib_drawTextLine(hu_textline_t* l, doom_boolean drawcursor); + +// erases text line +void HUlib_eraseTextLine(hu_textline_t* l); + + +// +// Scrolling Text window widget routines +// + +// ? +void HUlib_initSText(hu_stext_t* s, + int x, + int y, + int h, + patch_t** font, + int startchar, + doom_boolean* on); + +// add a new line +void HUlib_addLineToSText(hu_stext_t* s); + +// ? +void HUlib_addMessageToSText(hu_stext_t* s, char* prefix, char* msg); + +// draws stext +void HUlib_drawSText(hu_stext_t* s); + +// erases all stext lines +void HUlib_eraseSText(hu_stext_t* s); + +// Input Text Line widget routines +void HUlib_initIText(hu_itext_t* it, + int x, + int y, + patch_t** font, + int startchar, + doom_boolean* on); + +// enforces left margin +void HUlib_delCharFromIText(hu_itext_t* it); + +// enforces left margin +void HUlib_eraseLineFromIText(hu_itext_t* it); + +// resets line and left margin +void HUlib_resetIText(hu_itext_t* it); + +// left of left-margin +void HUlib_addPrefixToIText(hu_itext_t* it, char* str); + +// whether eaten +doom_boolean HUlib_keyInIText(hu_itext_t* it, unsigned char ch); + +void HUlib_drawIText(hu_itext_t* it); + +// erases all itext lines +void HUlib_eraseIText(hu_itext_t* it); + + +#endif + + +#ifndef __P_SPEC__ +#define __P_SPEC__ + + +//#include "p_mobj.h" +//#include "r_defs.h" + + +// +// End-level timer (-TIMER option) +// +extern doom_boolean levelTimer; +extern int levelTimeCount; + + +// Define values for map objects +#define MO_TELEPORTMAN 14 + + +// at game start +void P_InitPicAnims(void); + +// at map load +void P_SpawnSpecials(void); + +// every tic +void P_UpdateSpecials(void); + +// when needed +doom_boolean P_UseSpecialLine(mobj_t* thing, line_t* line, int side); + +void P_ShootSpecialLine(mobj_t* thing, line_t* line); +void P_CrossSpecialLine(int linenum, int side, mobj_t* thing); +void P_PlayerInSpecialSector(player_t* player); +int twoSided(int sector, int line); +sector_t* getSector(int currentSector, int line, int side); +side_t* getSide(int currentSector, int line, int side); +fixed_t P_FindLowestFloorSurrounding(sector_t* sec); +fixed_t P_FindHighestFloorSurrounding(sector_t* sec); +fixed_t P_FindNextHighestFloor(sector_t* sec, int currentheight); +fixed_t P_FindLowestCeilingSurrounding(sector_t* sec); +fixed_t P_FindHighestCeilingSurrounding(sector_t* sec); +int P_FindSectorFromLineTag(line_t* line, int start); +int P_FindMinSurroundingLight(sector_t* sector, int max); +sector_t* getNextSector(line_t* line, sector_t* sec); + +// +// SPECIAL +// +int EV_DoDonut(line_t* line); + + +// +// P_LIGHTS +// +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int maxlight; + int minlight; +} fireflicker_t; + + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int maxlight; + int minlight; + int maxtime; + int mintime; +} lightflash_t; + + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int minlight; + int maxlight; + int darktime; + int brighttime; +} strobe_t; + + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int minlight; + int maxlight; + int direction; +} glow_t; + + +#define GLOWSPEED 8 +#define STROBEBRIGHT 5 +#define FASTDARK 15 +#define SLOWDARK 35 + +void P_SpawnFireFlicker(sector_t* sector); +void T_LightFlash(lightflash_t* flash); +void P_SpawnLightFlash(sector_t* sector); +void T_StrobeFlash(strobe_t* flash); + +void P_SpawnStrobeFlash(sector_t* sector, int fastOrSlow, int inSync); +void EV_StartLightStrobing(line_t* line); +void EV_TurnTagLightsOff(line_t* line); + +void EV_LightTurnOn(line_t* line, int bright); + +void T_Glow(glow_t* g); +void P_SpawnGlowingLight(sector_t* sector); + + +// +// P_SWITCH +// +typedef struct +{ + char name1[9]; + char name2[9]; + short episode; +} switchlist_t; + + +typedef enum +{ + top, + middle, + bottom +} bwhere_e; + + +typedef struct +{ + line_t* line; + bwhere_e where; + int btexture; + int btimer; + mobj_t* soundorg; +} button_t; + + +// max # of wall switches in a level +#define MAXSWITCHES 50 + +// 4 players, 4 buttons each at once, max. +#define MAXBUTTONS 16 + +// 1 second, in ticks. +#define BUTTONTIME 35 + + +extern button_t buttonlist[MAXBUTTONS]; + + +void P_ChangeSwitchTexture(line_t* line, int useAgain); +void P_InitSwitchList(void); + + +// +// P_PLATS +// +typedef enum +{ + up, + down, + waiting, + in_stasis +} plat_e; + + +typedef enum +{ + perpetualRaise, + downWaitUpStay, + raiseAndChange, + raiseToNearestAndChange, + blazeDWUS +} plattype_e; + + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + fixed_t speed; + fixed_t low; + fixed_t high; + int wait; + int count; + plat_e status; + plat_e oldstatus; + doom_boolean crush; + int tag; + plattype_e type; +} plat_t; + + +#define PLATWAIT 3 +#define PLATSPEED FRACUNIT +#define MAXPLATS 30 + + +extern plat_t* activeplats[MAXPLATS]; + + +void T_PlatRaise(plat_t* plat); +int EV_DoPlat(line_t* line, plattype_e type, int amount); +void P_AddActivePlat(plat_t* plat); +void P_RemoveActivePlat(plat_t* plat); +void EV_StopPlat(line_t* line); +void P_ActivateInStasis(int tag); + + +// +// P_DOORS +// +typedef enum +{ + door_normal, + close30ThenOpen, + door_close, + door_open, + raiseIn5Mins, + blazeRaise, + blazeOpen, + blazeClose +} vldoor_e; + + +typedef struct +{ + thinker_t thinker; + vldoor_e type; + sector_t* sector; + fixed_t topheight; + fixed_t speed; + + // 1 = up, 0 = waiting at top, -1 = down + int direction; + + // tics to wait at the top + int topwait; + + // (keep in case a door going down is reset) + // when it reaches 0, start going down + int topcountdown; +} vldoor_t; + + +#define VDOORSPEED FRACUNIT*2 +#define VDOORWAIT 150 + + +void EV_VerticalDoor(line_t* line, mobj_t* thing); +int EV_DoDoor(line_t* line, vldoor_e type); +int EV_DoLockedDoor(line_t* line, vldoor_e type, mobj_t* thing); +void T_VerticalDoor(vldoor_t* door); +void P_SpawnDoorCloseIn30(sector_t* sec); +void P_SpawnDoorRaiseIn5Mins(sector_t* sec, int secnum); + + +// +// P_CEILNG +// +typedef enum +{ + lowerToFloor, + raiseToHighest, + lowerAndCrush, + crushAndRaise, + fastCrushAndRaise, + silentCrushAndRaise +} ceiling_e; + + +typedef struct +{ + thinker_t thinker; + ceiling_e type; + sector_t* sector; + fixed_t bottomheight; + fixed_t topheight; + fixed_t speed; + doom_boolean crush; + + // 1 = up, 0 = waiting, -1 = down + int direction; + + // ID + int tag; + int olddirection; +} ceiling_t; + + +#define CEILSPEED FRACUNIT +#define CEILWAIT 150 +#define MAXCEILINGS 30 + + +extern ceiling_t* activeceilings[MAXCEILINGS]; + + +int EV_DoCeiling(line_t* line, ceiling_e type); +void T_MoveCeiling(ceiling_t* ceiling); +void P_AddActiveCeiling(ceiling_t* c); +void P_RemoveActiveCeiling(ceiling_t* c); +int EV_CeilingCrushStop(line_t* line); +void P_ActivateInStasisCeiling(line_t* line); + + +// +// P_FLOOR +// +typedef enum +{ + // 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 floorpic + lowerAndChange, + + raiseFloor24, + raiseFloor24AndChange, + raiseFloorCrush, + + // raise to next highest floor, turbo-speed + raiseFloorTurbo, + donutRaise, + raiseFloor512 +} floor_e; + + +typedef enum +{ + build8, // slowly build by 8 + turbo16 // quickly build by 16 +} stair_e; + + +typedef struct +{ + thinker_t thinker; + floor_e type; + doom_boolean crush; + sector_t* sector; + int direction; + int newspecial; + short texture; + fixed_t floordestheight; + fixed_t speed; +} floormove_t; + + +#define FLOORSPEED FRACUNIT + + +typedef enum +{ + ok, + crushed, + pastdest +} result_e; + + +result_e T_MovePlane(sector_t* sector, fixed_t speed, fixed_t dest, doom_boolean crush, int floorOrCeiling, int direction); +int EV_BuildStairs(line_t* line, stair_e type); +int EV_DoFloor(line_t* line, floor_e floortype); +void T_MoveFloor(floormove_t* floor); + +// +// P_TELEPT +// +int EV_Teleport(line_t* line, int side, mobj_t* thing); + + +#endif + + +#ifndef __R_BSP__ +#define __R_BSP__ + + +//#include "r_defs.h" + + +extern seg_t* curline; +extern side_t* sidedef; +extern line_t* linedef; +extern sector_t* frontsector; +extern sector_t* backsector; + +extern int rw_x; +extern int rw_stopx; + +extern doom_boolean segtextured; + +// false if the back side is the same plane +extern doom_boolean markfloor; +extern doom_boolean markceiling; + +extern doom_boolean skymap; + +extern drawseg_t drawsegs[MAXDRAWSEGS]; +extern drawseg_t* ds_p; + +extern lighttable_t** hscalelight; +extern lighttable_t** vscalelight; +extern lighttable_t** dscalelight; + + +typedef void (*drawfunc_t) (int start, int stop); + + +// BSP? +void R_ClearClipSegs(void); +void R_ClearDrawSegs(void); +void R_RenderBSPNode(int bspnum); + + +#endif + + +#ifndef __R_DRAW__ +#define __R_DRAW__ + + +//#include "r_defs.h" + + +extern lighttable_t* dc_colormap; +extern int dc_x; +extern int dc_yl; +extern int dc_yh; +extern fixed_t dc_iscale; +extern fixed_t dc_texturemid; + +// first pixel in a column +extern byte* dc_source; + + +// The span blitting interface. +// Hook in assembler or system specific BLT +// here. +void R_DrawColumn(void); +void R_DrawColumnLow(void); + +// The Spectre/Invisibility effect. +void R_DrawFuzzColumn(void); +void R_DrawFuzzColumnLow(void); + +// Draw with color translation tables, +// for player sprite rendering, +// Green/Red/Blue/Indigo shirts. +void R_DrawTranslatedColumn(void); +void R_DrawTranslatedColumnLow(void); + +void R_VideoErase(unsigned ofs, int count); + +extern int ds_y; +extern int ds_x1; +extern int ds_x2; + +extern lighttable_t* ds_colormap; + +extern fixed_t ds_xfrac; +extern fixed_t ds_yfrac; +extern fixed_t ds_xstep; +extern fixed_t ds_ystep; + +// start of a 64*64 tile image +extern byte* ds_source; + +extern byte* translationtables; +extern byte* dc_translation; + + +// Span blitting for rows, floor/ceiling. +// No Sepctre effect needed. +void R_DrawSpan(void); + +// Low resolution mode, 160x200? +void R_DrawSpanLow(void); + +void R_InitBuffer(int width, int height); + + +// Initialize color translation tables, +// for player rendering etc. +void R_InitTranslationTables(void); + +// Rendering function. +void R_FillBackScreen(void); + +// If the view size is not full screen, draws a border around it. +void R_DrawViewBorder(void); + + +#endif + + +#ifndef __R_SEGS__ +#define __R_SEGS__ + + +//#include "r_defs.h" + + +void R_RenderMaskedSegRange(drawseg_t* ds, int x1, int x2); + + +#endif + + +#ifndef __R_STATE__ +#define __R_STATE__ + + +//#include "d_player.h" // Need data structure definitions. +//#include "r_defs.h" + + +// +// Refresh internal data structures, +// for rendering. +// + +// needed for texture pegging +extern fixed_t* textureheight; + +// needed for pre rendering (fracs) +extern fixed_t* spritewidth; + +extern fixed_t* spriteoffset; +extern fixed_t* spritetopoffset; + +extern lighttable_t* colormaps; + +extern int viewwidth; +extern int scaledviewwidth; +extern int viewheight; + +extern int firstflat; + +// for global animation +extern int* flattranslation; +extern int* texturetranslation; + +// Sprite.... +extern int firstspritelump; +extern int lastspritelump; +extern int numspritelumps; + +// +// Lookup tables for map data. +// +extern int numsprites; +extern spritedef_t* sprites; + +extern int numvertexes; +extern vertex_t* vertexes; + +extern int numsegs; +extern seg_t* segs; + +extern int numsectors; +extern sector_t* sectors; + +extern int numsubsectors; +extern subsector_t* subsectors; + +extern int numnodes; +extern node_t* nodes; + +extern int numlines; +extern line_t* lines; + +extern int numsides; +extern side_t* sides; + +// +// POV data. +// +extern fixed_t viewx; +extern fixed_t viewy; +extern fixed_t viewz; + +extern angle_t viewangle; +extern player_t* viewplayer; + +// ? +extern angle_t clipangle; + +extern int viewangletox[FINEANGLES / 2]; +extern angle_t xtoviewangle[SCREENWIDTH + 1]; + +extern fixed_t rw_distance; +extern angle_t rw_normalangle; + +// angle to line origin +extern int rw_angle1; + +// Segs count? +extern int sscount; + +extern visplane_t* floorplane; +extern visplane_t* ceilingplane; + + +#endif + + +#ifndef __R_DATA__ +#define __R_DATA__ + + +//#include "r_defs.h" +//#include "r_state.h" + + +// Retrieve column data for span blitting. +byte* R_GetColumn(int tex, int col); + +// I/O, setting up the stuff. +void R_InitData(void); +void R_PrecacheLevel(void); + +// Retrieval. +// Floor/ceiling opaque texture tiles, +// lookup by name. For animation? +int R_FlatNumForName(char* name); + +// Called by P_Ticker for switches and animations, +// returns the texture number for the texture name. +int R_TextureNumForName(char* name); +int R_CheckTextureNumForName(char* name); + +#endif + + +#ifndef __R_MAIN__ +#define __R_MAIN__ + +//#include "d_player.h" +//#include "r_data.h" + + +// +// POV related. +// +extern fixed_t viewcos; +extern fixed_t viewsin; + +extern int viewwidth; +extern int viewheight; +extern int viewwindowx; +extern int viewwindowy; + +extern int centerx; +extern int centery; + +extern fixed_t centerxfrac; +extern fixed_t centeryfrac; +extern fixed_t projection; + +extern int validcount; + +extern int linecount; +extern int loopcount; + + +// +// Lighting LUT. +// Used for z-depth cuing per column/row, +// and other lighting effects (sector ambient, flash). +// + +// Lighting constants. +// Now why not 32 levels here? +#define LIGHTLEVELS 16 +#define LIGHTSEGSHIFT 4 + +#define MAXLIGHTSCALE 48 +#define LIGHTSCALESHIFT 12 +#define MAXLIGHTZ 128 +#define LIGHTZSHIFT 20 + +extern lighttable_t* scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; +extern lighttable_t* scalelightfixed[MAXLIGHTSCALE]; +extern lighttable_t* zlight[LIGHTLEVELS][MAXLIGHTZ]; + +extern int extralight; +extern lighttable_t* fixedcolormap; + + +// Number of diminishing brightness levels. +// There a 0-31, i.e. 32 LUT in the COLORMAP lump. +#define NUMCOLORMAPS 32 + + +// Blocky/low detail mode. +//B remove this? +// 0 = high, 1 = low +extern int detailshift; + + +// +// Function pointers to switch refresh/drawing functions. +// Used to select shadow mode etc. +// +extern void (*colfunc) (void); +extern void (*basecolfunc) (void); +extern void (*fuzzcolfunc) (void); +// No shadow effects on floors. +extern void (*spanfunc) (void); + + +// +// Utility functions. +int R_PointOnSide(fixed_t x, fixed_t y, node_t* node); +int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t* line); +angle_t R_PointToAngle(fixed_t x, fixed_t y); +angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); +fixed_t R_PointToDist(fixed_t x, fixed_t y); +fixed_t R_ScaleFromGlobalAngle(angle_t visangle); +subsector_t* R_PointInSubsector(fixed_t x, fixed_t y); +void R_AddPointToBox(int x, int y, fixed_t* box); + + +// +// REFRESH - the actual rendering functions. +// + +// Called by G_Drawer. +void R_RenderPlayerView(player_t* player); + +// Called by startup code. +void R_Init(void); + +// Called by M_Responder. +void R_SetViewSize(int blocks, int detail); + +#endif + + +#ifndef __R_PLANE__ +#define __R_PLANE__ + + +//#include "r_data.h" + + +// Visplane related. +extern short* lastopening; + +typedef void (*planefunction_t) (int top, int bottom); + +extern planefunction_t floorfunc; +extern planefunction_t ceilingfunc_t; + +extern short floorclip[SCREENWIDTH]; +extern short ceilingclip[SCREENWIDTH]; + +extern fixed_t yslope[SCREENHEIGHT]; +extern fixed_t distscale[SCREENWIDTH]; + +void R_InitPlanes(void); +void R_ClearPlanes(void); +void R_MapPlane(int y, int x1, int x2); +void R_MakeSpans(int x, int t1, int b1, int t2, int b2); +void R_DrawPlanes(void); +visplane_t* R_FindPlane(fixed_t height, int picnum, int lightlevel); +visplane_t* R_CheckPlane(visplane_t* pl, int start, int stop); + + +#endif + + +#ifndef __R_THINGS__ +#define __R_THINGS__ + + +//#include "r_defs.h" + + +#define MAXVISSPRITES 128 + + +extern vissprite_t vissprites[MAXVISSPRITES]; +extern vissprite_t* vissprite_p; +extern vissprite_t vsprsortedhead; + +// Constant arrays used for psprite clipping +// and initializing clipping. +extern short negonearray[SCREENWIDTH]; +extern short screenheightarray[SCREENWIDTH]; + +// vars for R_DrawMaskedColumn +extern short* mfloorclip; +extern short* mceilingclip; +extern fixed_t spryscale; +extern fixed_t sprtopscreen; + +extern fixed_t pspritescale; +extern fixed_t pspriteiscale; + + +void R_DrawMaskedColumn(column_t* column); +void R_SortVisSprites(void); +void R_AddSprites(sector_t* sec); +void R_InitSprites(char** namelist); +void R_ClearSprites(void); +void R_DrawMasked(void); + + +#endif + + +#ifndef __R_LOCAL__ +#define __R_LOCAL__ + +// Binary Angles, sine/cosine/atan lookups. +//#include "tables.h" + +// Screen size related parameters. +//#include "doomdef.h" + +// Include the refresh/render data structs. +//#include "r_data.h" + +// +// Separate header file for each module. +// +//#include "r_main.h" +//#include "r_bsp.h" +//#include "r_segs.h" +//#include "r_plane.h" +//#include "r_data.h" +//#include "r_things.h" +//#include "r_draw.h" + +#endif // __R_LOCAL__ + + +#ifndef __P_LOCAL__ +#define __P_LOCAL__ + + +#ifndef __R_LOCAL__ +//#include "r_local.h" +#endif + + +#define FLOATSPEED (FRACUNIT*4) + +#define MAXHEALTH 100 +#define VIEWHEIGHT (41*FRACUNIT) + +// mapblocks are used to check movement +// against lines and things +#define MAPBLOCKUNITS 128 +#define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) +#define MAPBLOCKSHIFT (FRACBITS+7) +#define MAPBMASK (MAPBLOCKSIZE-1) +#define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) + + +// player radius for movement checking +#define PLAYERRADIUS 16*FRACUNIT + +// MAXRADIUS is for precalculated sector block boxes +// the spider demon is larger, +// but we do not have any moving sectors nearby +#define MAXRADIUS 32*FRACUNIT + +#define GRAVITY FRACUNIT +#define MAXMOVE (30*FRACUNIT) + +#define USERANGE (64*FRACUNIT) +#define MELEERANGE (64*FRACUNIT) +#define MISSILERANGE (32*64*FRACUNIT) + +// follow a player exlusively for 3 seconds +#define BASETHRESHOLD 100 + + +// +// P_TICK +// + +// both the head and tail of the thinker list +extern thinker_t thinkercap; + + +void P_InitThinkers(void); +void P_AddThinker(thinker_t* thinker); +void P_RemoveThinker(thinker_t* thinker); + + +// +// P_PSPR +// +void P_SetupPsprites(player_t* curplayer); +void P_MovePsprites(player_t* curplayer); +void P_DropWeapon(player_t* player); + + +// +// P_USER +// +void P_PlayerThink(player_t* player); + + +// +// P_MOBJ +// +#define ONFLOORZ DOOM_MININT +#define ONCEILINGZ DOOM_MAXINT + +// Time interval for item respawning. +#define ITEMQUESIZE 128 + + +extern mapthing_t itemrespawnque[ITEMQUESIZE]; +extern int itemrespawntime[ITEMQUESIZE]; +extern int iquehead; +extern int iquetail; + + +void P_RespawnSpecials(void); +mobj_t* P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); +void P_RemoveMobj(mobj_t* th); +doom_boolean P_SetMobjState(mobj_t* mobj, statenum_t state); +void P_MobjThinker(mobj_t* mobj); +void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z); +void P_SpawnBlood(fixed_t x, fixed_t y, fixed_t z, int damage); +mobj_t* P_SpawnMissile(mobj_t* source, mobj_t* dest, mobjtype_t type); +void P_SpawnPlayerMissile(mobj_t* source, mobjtype_t type); + + +// +// P_ENEMY +// +void P_NoiseAlert(mobj_t* target, mobj_t* emmiter); + + +// +// P_MAPUTL +// +typedef struct +{ + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; +} divline_t; + + +typedef struct +{ + fixed_t frac; // along trace line + doom_boolean isaline; + union + { + mobj_t* thing; + line_t* line; + } d; +} intercept_t; + + +#define MAXINTERCEPTS 128 +extern intercept_t intercepts[MAXINTERCEPTS]; +extern intercept_t* intercept_p; + + +typedef doom_boolean(*traverser_t) (intercept_t* in); + + +fixed_t P_AproxDistance(fixed_t dx, fixed_t dy); +int P_PointOnLineSide(fixed_t x, fixed_t y, line_t* line); +int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t* line); +void P_MakeDivline(line_t* li, divline_t* dl); +fixed_t P_InterceptVector(divline_t* v2, divline_t* v1); +int P_BoxOnLineSide(fixed_t* tmbox, line_t* ld); + + +extern fixed_t opentop; +extern fixed_t openbottom; +extern fixed_t openrange; +extern fixed_t lowfloor; + + +void P_LineOpening(line_t* linedef); + + +doom_boolean P_BlockLinesIterator(int x, int y, doom_boolean(*func)(line_t*)); +doom_boolean P_BlockThingsIterator(int x, int y, doom_boolean(*func)(mobj_t*)); + + +#define PT_ADDLINES 1 +#define PT_ADDTHINGS 2 +#define PT_EARLYOUT 4 + + +extern divline_t trace; + + +doom_boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, doom_boolean(*trav) (intercept_t*)); +void P_UnsetThingPosition(mobj_t* thing); +void P_SetThingPosition(mobj_t* thing); + + +// +// P_MAP +// + +// If "floatok" true, move would be ok +// if within "tmfloorz - tmceilingz". +extern doom_boolean floatok; +extern fixed_t tmfloorz; +extern fixed_t tmceilingz; + +extern line_t* ceilingline; + + +doom_boolean P_CheckPosition(mobj_t* thing, fixed_t x, fixed_t y); +doom_boolean P_TryMove(mobj_t* thing, fixed_t x, fixed_t y); +doom_boolean P_TeleportMove(mobj_t* thing, fixed_t x, fixed_t y); +void P_SlideMove(mobj_t* mo); +doom_boolean P_CheckSight(mobj_t* t1, mobj_t* t2); +void P_UseLines(player_t* player); +doom_boolean P_ChangeSector(sector_t* sector, doom_boolean crunch); + + +extern mobj_t* linetarget; // who got hit (or 0) + + +fixed_t P_AimLineAttack(mobj_t* t1, angle_t angle, fixed_t distance); +void P_LineAttack(mobj_t* t1, angle_t angle, fixed_t distance, fixed_t slope, int damage); +void P_RadiusAttack(mobj_t* spot, mobj_t* source, int damage); + + +// +// P_SETUP +// +extern byte* rejectmatrix; // for fast sight rejection +extern short* blockmaplump; // offsets in blockmap are from here +extern short* blockmap; +extern int bmapwidth; +extern int bmapheight; // in mapblocks +extern fixed_t bmaporgx; +extern fixed_t bmaporgy; // origin of block map +extern mobj_t** blocklinks; // for thing chains + + +// +// P_INTER +// +extern int maxammo[NUMAMMO]; +extern int clipammo[NUMAMMO]; + + +void P_TouchSpecialThing(mobj_t* special, mobj_t* toucher); +void P_DamageMobj(mobj_t* target, mobj_t* inflictor, mobj_t* source, int damage); + + +// +// P_SPEC +// +//#include "p_spec.h" + + +#endif // __P_LOCAL__ + + +#ifndef __STLIB__ +#define __STLIB__ + + +// We are referring to patches. +//#include "r_defs.h" + + +// +// Background and foreground screen numbers +// +#define STLIB_BG 4 +#define STLIB_FG 0 + + +// +// Typedefs of widgets +// + +// Number widget +typedef struct +{ + // upper right-hand corner + // of the number (right-justified) + int x; + int y; + + // max # of digits in number + int width; + + // last number value + int oldnum; + + // pointer to current value + int* num; + + // pointer to doom_boolean stating + // whether to update number + doom_boolean* on; + + // list of patches for 0-9 + patch_t** p; + + // user data + int data; +} st_number_t; + + +// Percent widget ("child" of number widget, +// or, more precisely, contains a number widget.) +typedef struct +{ + // number information + st_number_t n; + + // percent sign graphic + patch_t* p; +} st_percent_t; + + +// Multiple Icon widget +typedef struct +{ + // center-justified location of icons + int x; + int y; + + // last icon number + int oldinum; + + // pointer to current icon + int* inum; + + // pointer to doom_boolean stating + // whether to update icon + doom_boolean* on; + + // list of icons + patch_t** p; + + // user data + int data; +} st_multicon_t; + + +// Binary Icon widget +typedef struct +{ + // center-justified location of icon + int x; + int y; + + // last icon value + int oldval; + + // pointer to current icon status + doom_boolean* val; + + // pointer to doom_boolean + // stating whether to update icon + doom_boolean* on; + + + patch_t* p; // icon + int data; // user data + +} st_binicon_t; + + +// +// Widget creation, access, and update routines +// + +// Initializes widget library. +// More precisely, initialize STMINUS, +// everything else is done somewhere else. +// +void STlib_init(void); + +// Number widget routines +void STlib_initNum(st_number_t* n, + int x, + int y, + patch_t** pl, + int* num, + doom_boolean* on, + int width); + +void STlib_updateNum(st_number_t* n, doom_boolean refresh); + +// Percent widget routines +void STlib_initPercent(st_percent_t* p, + int x, + int y, + patch_t** pl, + int* num, + doom_boolean* on, + patch_t* percent); + +void STlib_updatePercent(st_percent_t* per, int refresh); + +// Multiple Icon widget routines +void STlib_initMultIcon(st_multicon_t* mi, + int x, + int y, + patch_t** il, + int* inum, + doom_boolean* on); + +void STlib_updateMultIcon(st_multicon_t* mi, doom_boolean refresh); + +// Binary Icon widget routines +void STlib_initBinIcon(st_binicon_t* b, + int x, + int y, + patch_t* i, + doom_boolean* val, + doom_boolean* on); + +void STlib_updateBinIcon(st_binicon_t* bi, doom_boolean refresh); + +#endif + + +#ifndef __V_VIDEO__ +#define __V_VIDEO__ + +//#include "doomtype.h" +//#include "doomdef.h" +//#include "r_data.h" // Needed because we are refering to patches. + +// +// VIDEO +// + +#define CENTERY (SCREENHEIGHT / 2) + + +// Screen 0 is the screen updated by I_Update screen. +// Screen 1 is an extra buffer. +extern byte* screens[5]; +extern int dirtybox[4]; +extern byte gammatable[5][256]; +extern int usegamma; + + +// Allocates buffer screens, call before R_Init. +void V_Init(void); + +void V_CopyRect(int srcx, + int srcy, + int srcscrn, + int width, + int height, + int destx, + int desty, + int destscrn); + +void V_DrawPatch(int x, + int y, + int scrn, + patch_t* patch); + +void V_DrawPatchDirect(int x, + int y, + int scrn, + patch_t* patch); + +void V_DrawPatchRectDirect(int x, int y, int scrn, patch_t* patch, int src_x, int src_w); + +// Draw a linear block of pixels into the view buffer. +void V_DrawBlock(int x, + int y, + int scrn, + int width, + int height, + byte* src); + +// Reads a linear block of pixels into the view buffer. +void V_GetBlock(int x, + int y, + int scrn, + int width, + int height, + byte* dest); + +void V_MarkRect(int x, + int y, + int width, + int height); + +#endif + + +#ifndef __W_WAD__ +#define __W_WAD__ + + +// +// TYPES +// +typedef struct +{ + // Should be "IWAD" or "PWAD". + char identification[4]; + int numlumps; + int infotableofs; +} wadinfo_t; + + +typedef struct +{ + int filepos; + int size; + char name[8]; +} filelump_t; + + +// +// WADFILE I/O related stuff. +// +typedef struct +{ + char name[8]; + void* handle; + int position; + int size; +} lumpinfo_t; + + +extern void** lumpcache; +extern lumpinfo_t* lumpinfo; +extern int numlumps; + +void W_InitMultipleFiles(char** filenames); +void W_Reload(void); + +int W_CheckNumForName(char* name); +int W_GetNumForName(char* name); + +int W_LumpLength(int lump); +void W_ReadLump(int lump, void* dest); + +void* W_CacheLumpNum(int lump, int tag); +void* W_CacheLumpName(char* name, int tag); + + +#endif + + +#ifndef __WI_STUFF__ +#define __WI_STUFF__ + +//#include "doomdef.h" + +// States for the intermission +typedef enum +{ + NoState = -1, + StatCount, + ShowNextLoc +} stateenum_t; + +// Called by main loop, animate the intermission. +void WI_Ticker(void); + +// Called by main loop, +// draws the intermission directly into the screen buffer. +void WI_Drawer(void); + +// Setup for an intermission screen. +void WI_Start(wbstartstruct_t* wbstartstruct); + +#endif + + +#ifndef __Z_ZONE__ +#define __Z_ZONE__ + + + + +// +// ZONE MEMORY +// PU - purge tags. +// Tags < 100 are not overwritten until freed. +#define PU_STATIC 1 // static entire execution time +#define PU_SOUND 2 // static while playing +#define PU_MUSIC 3 // static while playing +#define PU_DAVE 4 // anything else Dave wants static +#define PU_LEVEL 50 // static until level exited +#define PU_LEVSPEC 51 // a special thinker in a level +// Tags >= 100 are purgable whenever needed. +#define PU_PURGELEVEL 100 +#define PU_CACHE 101 + + +void Z_Init(void); +void* Z_Malloc(int size, int tag, void* ptr); +void Z_Free(void* ptr); +void Z_FreeTags(int lowtag, int hightag); +void Z_DumpHeap(int lowtag, int hightag); +void Z_FileDumpHeap(void* f); +void Z_CheckHeap(void); +void Z_ChangeTag2(void* ptr, int tag); +int Z_FreeMemory(void); + + +typedef struct memblock_s +{ + int size; // including the header and possibly tiny fragments + void** user; // 0 if a free block + int tag; // purgelevel + int id; // should be ZONEID + struct memblock_s* next; + struct memblock_s* prev; +} memblock_t; + +// +// This is used to get the local FILE:LINE info from CPP +// prior to really call the function in question. +// +#define Z_ChangeTag(p,t) \ +{ \ + if (( (memblock_t *)( (byte *)(p) - sizeof(memblock_t)))->id!=0x1d4a11) \ + { \ + /*I_Error("Error: Z_CT at "__FILE__":%i",__LINE__);*/ \ + char buf[260]; \ + doom_strcpy(buf, "Error: Z_CT at " __FILE__ ":"); \ + doom_concat(buf, doom_itoa(__LINE__, 10)); \ + I_Error(buf); \ + } \ + Z_ChangeTag2(p,t); \ +}; + + +#endif + + + +#if defined(DOOM_IMPLEMENTATION) +#if defined(WIN32) +#define _CRT_SECURE_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + +//#include "DOOM.h" + +//#include "d_main.h" +//#include "doomdef.h" +//#include "doomtype.h" +//#include "i_system.h" +//#include "m_argv.h" +//#include "m_misc.h" + + +extern byte* screens[5]; +extern unsigned char screen_palette[256 * 3]; +extern doom_boolean is_wiping_screen; +extern default_t defaults[]; +extern int numdefaults; +extern signed short mixbuffer[2048]; + + +static unsigned char* screen_buffer = 0; +static unsigned char* final_screen_buffer = 0; +static int last_update_time = 0; +static int button_states[3] = { 0 }; +static char itoa_buf[20]; + + +char error_buf[260]; +int doom_flags = 0; +doom_print_fn doom_print = 0; +doom_malloc_fn doom_malloc = 0; +doom_free_fn doom_free = 0; +doom_open_fn doom_open = 0; +doom_close_fn doom_close = 0; +doom_read_fn doom_read = 0; +doom_write_fn doom_write = 0; +doom_seek_fn doom_seek = 0; +doom_tell_fn doom_tell = 0; +doom_eof_fn doom_eof = 0; +doom_gettime_fn doom_gettime = 0; +doom_exit_fn doom_exit = 0; +doom_getenv_fn doom_getenv = 0; + + +void D_DoomLoop(void); +void D_UpdateWipe(void); +void I_UpdateSound(); +unsigned long I_TickSong(); + + +#if defined(DOOM_IMPLEMENT_PRINT) +#include +static void doom_print_impl(const char* str) +{ + printf("%s", str); +} +#else +static void doom_print_impl(const char* str) {} +#endif + +#if defined(DOOM_IMPLEMENT_MALLOC) +#include +static void* doom_malloc_impl(int size) +{ + return malloc((size_t)size); +} +static void doom_free_impl(void* ptr) +{ + free(ptr); +} +#else +static void* doom_malloc_impl(int size) { return 0; } +static void doom_free_impl(void* ptr) {} +#endif + + +#if defined(DOOM_IMPLEMENT_FILE_IO) +#include +void* doom_open_impl(const char* filename, const char* mode) +{ + return fopen(filename, mode); +} +void doom_close_impl(void* handle) +{ + fclose(handle); +} +int doom_read_impl(void* handle, void *buf, int count) +{ + return (int)fread(buf, 1, count, handle); +} +int doom_write_impl(void* handle, const void *buf, int count) +{ + return (int)fwrite(buf, 1, count, handle); +} +int doom_seek_impl(void* handle, int offset, doom_seek_t origin) +{ + return fseek(handle, offset, origin); +} +int doom_tell_impl(void* handle) +{ + return (int)ftell(handle); +} +int doom_eof_impl(void* handle) +{ + return feof(handle); +} +#else +void* doom_open_impl(const char* filename, const char* mode) +{ + return 0; +} +void doom_close_impl(void* handle) {} +int doom_read_impl(void* handle, void *buf, int count) +{ + return -1; +} +int doom_write_impl(void* handle, const void *buf, int count) +{ + return -1; +} +int doom_seek_impl(void* handle, int offset, doom_seek_t origin) +{ + return -1; +} +int doom_tell_impl(void* handle) +{ + return -1; +} +int doom_eof_impl(void* handle) +{ + return 1; +} +#endif + + +#if defined(DOOM_IMPLEMENT_GETTIME) +#if defined(WIN32) +#include +#else +#include +#endif +void doom_gettime_impl(int* sec, int* usec) +{ +#if defined(WIN32) + static const unsigned long long EPOCH = ((unsigned long long)116444736000000000ULL); + SYSTEMTIME system_time; + FILETIME file_time; + unsigned long long time; + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((unsigned long long)file_time.dwLowDateTime); + time += ((unsigned long long)file_time.dwHighDateTime) << 32; + *sec = (int)((time - EPOCH) / 10000000L); + *usec = (int)(system_time.wMilliseconds * 1000); +#else + struct timeval tp; + struct timezone tzp; + gettimeofday(&tp, &tzp); + *sec = tp.tv_sec; + *usec = tp.tv_usec; +#endif +} +#else +void doom_gettime_impl(int* sec, int* usec) +{ + *sec = 0; + *usec = 0; +} +#endif + + +#if defined(DOOM_IMPLEMENT_EXIT) +#include +void doom_exit_impl(int code) +{ + exit(code); +} +#else +void doom_exit_impl(int code) {} +#endif + + +#if defined(DOOM_IMPLEMENT_GETENV) +#include +char* doom_getenv_impl(const char* var) +{ + return getenv(var); +} +#else +char* doom_getenv_impl(const char* var) { return 0; } +#endif + + +void doom_memset(void* ptr, int value, int num) +{ + unsigned char* p = ptr; + for (int i = 0; i < num; ++i, ++p) + { + *p = (unsigned char)value; + } +} + + +void* doom_memcpy(void* destination, const void* source, int num) +{ + unsigned char* dst = destination; + const unsigned char* src = source; + + for (int i = 0; i < num; ++i, ++dst, ++src) + { + *dst = *src; + } + + return destination; +} + + +int doom_strlen(const char* str) +{ + int len = 0; + while (*str++) ++len; + return len; +} + + +char* doom_concat(char* dst, const char* src) +{ + char* ret = dst; + dst += doom_strlen(dst); + + while (*src) *dst++ = *src++; + *dst = *src; // \0 + + return ret; +} + + +char* doom_strcpy(char* dst, const char* src) +{ + char* ret = dst; + + while (*src) *dst++ = *src++; + *dst = *src; // \0 + + return ret; +} + + +char* doom_strncpy(char* dst, const char* src, int num) +{ + int i = 0; + + for (; i < num; ++i) + { + if (!src[i]) break; + dst[i] = src[i]; + } + + while (i < num) dst[i++] = '\0'; + + return dst; +} + + +int doom_strcmp(const char* str1, const char* str2) +{ + int ret = 0; + + while (!(ret = *(unsigned char*)str1 - *(unsigned char*) str2) && *str1) + ++str1, ++str2; + + if (ret < 0) + ret = -1; + else if (ret > 0) + ret = 1; + + return (ret); +} + + +int doom_strncmp(const char* str1, const char* str2, int n) +{ + int ret = 0; + int count = 1; + + while (!(ret = *(unsigned char*)str1 - *(unsigned char*) str2) && *str1 && count++ < n) + ++str1, ++str2; + + if (ret < 0) + ret = -1; + else if (ret > 0) + ret = 1; + + return (ret); +} + + +int doom_toupper(int c) +{ + if (c >= 'a' && c <= 'z') return c - 'a' + 'A'; + return c; +} + + +int doom_strcasecmp(const char* str1, const char* str2) +{ + int ret = 0; + + while (!(ret = doom_toupper(*(unsigned char*)str1) - doom_toupper(*(unsigned char*)str2)) && *str1) + ++str1, ++str2; + + if (ret < 0) + ret = -1; + else if (ret > 0) + ret = 1; + + return (ret); +} + + +int doom_strncasecmp(const char* str1, const char* str2, int n) +{ + int ret = 0; + int count = 1; + + while (!(ret = doom_toupper(*(unsigned char*)str1) - doom_toupper(*(unsigned char*)str2)) && *str1 && count++ < n) + ++str1, ++str2; + + if (ret < 0) + ret = -1; + else if (ret > 0) + ret = 1; + + return (ret); +} + + +int doom_atoi(const char* str) +{ + int i = 0; + int c; + + while ((c = *str++) != 0) + { + i *= 10; + i += c - '0'; + } + + return i; +} + + +int doom_atox(const char* str) +{ + int i = 0; + int c; + + while ((c = *str++) != 0) + { + i *= 16; + if (c >= '0' && c <= '9') + i += c - '0'; + else + i += c - 'A' + 10; + } + + return i; +} + + +const char* doom_itoa(int k, int radix) +{ + int i = k < 0 ? -k : k; + if (i == 0) + { + itoa_buf[0] = '0'; + itoa_buf[1] = '\0'; + return itoa_buf; + } + + int idx = k < 0 ? 1 : 0; + int j = i; + while (j) + { + j /= radix; + idx++; + } + itoa_buf[idx] = '\0'; + + if (radix == 10) + { + while (i) + { + itoa_buf[--idx] = '0' + (i % 10); + i /= 10; + } + } + else + { + while (i) + { + int k = (i & 0xF); + if (k >= 10) + itoa_buf[--idx] = 'A' + ((i & 0xF) - 10); + else + itoa_buf[--idx] = '0' + (i & 0xF); + i >>= 4; + } + } + + if (k < 0) itoa_buf[0] = '-'; + + return itoa_buf; +} + + +const char* doom_ctoa(char c) +{ + itoa_buf[0] = c; + itoa_buf[1] = '\0'; + return itoa_buf; +} + + +const char* doom_ptoa(void* p) +{ + int idx = 0; + unsigned long long i = (unsigned long long)p; + + itoa_buf[idx++] = '0'; + itoa_buf[idx++] = 'x'; + + while (i) + { + int k = (i & 0xF); + if (k >= 10) + itoa_buf[idx++] = 'A' + ((i & 0xF) - 10); + else + itoa_buf[idx++] = '0' + (i & 0xF); + i >>= 4; + } + + itoa_buf[idx] = '\0'; + return itoa_buf; +} + + +int doom_fprint(void* handle, const char* str) +{ + return doom_write(handle, str, doom_strlen(str)); +} + + +static default_t* get_default(const char* name) +{ + for (int i = 0; i < numdefaults; ++i) + { + if (doom_strcmp(defaults[i].name, name) == 0) return &defaults[i]; + } + return 0; +} + + +void doom_set_resolution(int width, int height) +{ + if (width <= 0 || height <= 0) return; + // SCREENWIDTH = width; + // SCREENHEIGHT = height; +} + + +void doom_set_default_int(const char* name, int value) +{ + default_t* def = get_default(name); + if (!def) return; + def->defaultvalue = value; +} + + +void doom_set_default_string(const char* name, const char* value) +{ + default_t* def = get_default(name); + if (!def) return; + def->default_text_value = (char*)value; +} + + +void doom_set_print(doom_print_fn print_fn) +{ + doom_print = print_fn; +} + + +void doom_set_malloc(doom_malloc_fn malloc_fn, doom_free_fn free_fn) +{ + doom_malloc = malloc_fn; + doom_free = free_fn; +} + + +void doom_set_file_io(doom_open_fn open_fn, + doom_close_fn close_fn, + doom_read_fn read_fn, + doom_write_fn write_fn, + doom_seek_fn seek_fn, + doom_tell_fn tell_fn, + doom_eof_fn eof_fn) +{ + doom_open = open_fn; + doom_close = close_fn; + doom_read = read_fn; + doom_write = write_fn; + doom_seek = seek_fn; + doom_tell = tell_fn; + doom_eof = eof_fn; +} + + +void doom_set_gettime(doom_gettime_fn gettime_fn) +{ + doom_gettime = gettime_fn; +} + + +void doom_set_exit(doom_exit_fn exit_fn) +{ + doom_exit = exit_fn; +} + + +void doom_set_getenv(doom_getenv_fn getenv_fn) +{ + doom_getenv = getenv_fn; +} + + +void doom_init(int argc, char** argv, int flags) +{ + if (!doom_print) doom_print = doom_print_impl; + if (!doom_malloc) doom_malloc = doom_malloc_impl; + if (!doom_free) doom_free = doom_free_impl; + if (!doom_open) doom_open = doom_open_impl; + if (!doom_close) doom_close = doom_close_impl; + if (!doom_read) doom_read = doom_read_impl; + if (!doom_write) doom_write = doom_write_impl; + if (!doom_seek) doom_seek = doom_seek_impl; + if (!doom_tell) doom_tell = doom_tell_impl; + if (!doom_eof) doom_eof = doom_eof_impl; + if (!doom_gettime) doom_gettime = doom_gettime_impl; + if (!doom_exit) doom_exit = doom_exit_impl; + if (!doom_getenv) doom_getenv = doom_getenv_impl; + + screen_buffer = doom_malloc(SCREENWIDTH * SCREENHEIGHT); + final_screen_buffer = doom_malloc(SCREENWIDTH * SCREENHEIGHT * 4); + last_update_time = I_GetTime(); + + myargc = argc; + myargv = argv; + doom_flags = flags; + + D_DoomMain(); +} + + +void doom_update() +{ + int now = I_GetTime(); + int delta_time = now - last_update_time; + + while (delta_time-- > 0) + { + if (is_wiping_screen) + D_UpdateWipe(); + else + D_DoomLoop(); + } + + last_update_time = now; +} + + +const unsigned char* doom_get_framebuffer(int channels) +{ + int i, len; + + doom_memcpy(screen_buffer, screens[0], SCREENWIDTH * SCREENHEIGHT); + + extern doom_boolean menuactive; + extern gamestate_t gamestate; + extern doom_boolean automapactive; + extern int crosshair; + + // Draw crosshair + if (crosshair && + !menuactive && + gamestate == GS_LEVEL && + !automapactive) + { + int y; + extern int setblocks; + if (setblocks == 11) y = SCREENHEIGHT / 2 + 8; + else y = SCREENHEIGHT / 2 - 8; + for (i = 0; i < 2; ++i) + { + screen_buffer[SCREENWIDTH / 2 - 2 - i + y * SCREENWIDTH] = 4; + screen_buffer[SCREENWIDTH / 2 + 2 + i + y * SCREENWIDTH] = 4; + } + for (i = 0; i < 2; ++i) + { + screen_buffer[SCREENWIDTH / 2 + (y - 2 - i) * SCREENWIDTH] = 4; + screen_buffer[SCREENWIDTH / 2 + (y + 2 + i) * SCREENWIDTH] = 4; + } + } + + if (channels == 1) + { + return screen_buffer; + } + else if (channels == 3) + { + for (i = 0, len = SCREENWIDTH * SCREENHEIGHT; i < len; ++i) + { + int k = i * 3; + int kpal = screen_buffer[i] * 3; + final_screen_buffer[k + 0] = screen_palette[kpal + 0]; + final_screen_buffer[k + 1] = screen_palette[kpal + 1]; + final_screen_buffer[k + 2] = screen_palette[kpal + 2]; + } + return final_screen_buffer; + } + else if (channels == 4) + { + for (i = 0, len = SCREENWIDTH * SCREENHEIGHT; i < len; ++i) + { + int k = i * 4; + int kpal = screen_buffer[i] * 3; + final_screen_buffer[k + 0] = screen_palette[kpal + 0]; + final_screen_buffer[k + 1] = screen_palette[kpal + 1]; + final_screen_buffer[k + 2] = screen_palette[kpal + 2]; + final_screen_buffer[k + 3] = 255; + } + return final_screen_buffer; + } + else + { + return 0; + } +} + + +unsigned long doom_tick_midi() +{ + return I_TickSong(); +} + + +short* doom_get_sound_buffer() +{ + I_UpdateSound(); + return mixbuffer; +} + + +void doom_key_down(doom_key_t key) +{ + event_t event; + event.type = ev_keydown; + event.data1 = (int)key; + D_PostEvent(&event); +} + + +void doom_key_up(doom_key_t key) +{ + event_t event; + event.type = ev_keyup; + event.data1 = (int)key; + D_PostEvent(&event); +} + + +void doom_button_down(doom_button_t button) +{ + button_states[button] = 1; + + event_t event; + event.type = ev_mouse; + event.data1 = + (button_states[0]) | + (button_states[1] ? 2 : 0) | + (button_states[2] ? 4 : 0); + event.data2 = event.data3 = 0; + D_PostEvent(&event); +} + + +void doom_button_up(doom_button_t button) +{ + button_states[button] = 0; + + event_t event; + event.type = ev_mouse; + event.data1 = + (button_states[0]) | + (button_states[1] ? 2 : 0) | + (button_states[2] ? 4 : 0); + + event.data1 = + event.data1 + ^ (button_states[0] ? 1 : 0) + ^ (button_states[1] ? 2 : 0) + ^ (button_states[2] ? 4 : 0); + + event.data2 = event.data3 = 0; + D_PostEvent(&event); +} + + +void doom_mouse_move(int delta_x, int delta_y) +{ + event_t event; + + event.type = ev_mouse; + event.data1 = + (button_states[0]) | + (button_states[1] ? 2 : 0) | + (button_states[2] ? 4 : 0); + event.data2 = delta_x; + event.data3 = -delta_y; + + if (event.data2 || event.data3) + { + D_PostEvent(&event); + } +} +#define REDS (256-5*16) +#define REDRANGE 16 +#define BLUES (256-4*16+8) +#define BLUERANGE 8 +#define GREENS (7*16) +#define GREENRANGE 16 +#define GRAYS (6*16) +#define GRAYSRANGE 16 +#define BROWNS (4*16) +#define BROWNRANGE 16 +#define YELLOWS (256-32+7) +#define YELLOWRANGE 1 +#define BLACK 0 +#define WHITE (256-47) + +// Automap colors +#define BACKGROUND BLACK +#define YOURCOLORS WHITE +#define YOURRANGE 0 +#define WALLCOLORS REDS +#define WALLRANGE REDRANGE +#define TSWALLCOLORS GRAYS +#define TSWALLRANGE GRAYSRANGE +#define FDWALLCOLORS BROWNS +#define FDWALLRANGE BROWNRANGE +#define CDWALLCOLORS YELLOWS +#define CDWALLRANGE YELLOWRANGE +#define THINGCOLORS GREENS +#define THINGRANGE GREENRANGE +#define SECRETWALLCOLORS WALLCOLORS +#define SECRETWALLRANGE WALLRANGE +#define GRIDCOLORS (GRAYS + GRAYSRANGE/2) +#define GRIDRANGE 0 +#define XHAIRCOLORS GRAYS + +// drawing stuff +#define FB 0 + +#define AM_PANDOWNKEY KEY_DOWNARROW +#define AM_PANUPKEY KEY_UPARROW +#define AM_PANRIGHTKEY KEY_RIGHTARROW +#define AM_PANLEFTKEY KEY_LEFTARROW +#define AM_ZOOMINKEY '=' +#define AM_ZOOMOUTKEY '-' +#define AM_STARTKEY KEY_TAB +#define AM_ENDKEY KEY_TAB +#define AM_GOBIGKEY '0' +#define AM_FOLLOWKEY 'f' +#define AM_GRIDKEY 'g' +#define AM_MARKKEY 'm' +#define AM_CLEARMARKKEY 'c' + +#define AM_NUMMARKPOINTS 10 + +// scale on entry +#define INITSCALEMTOF (.2*FRACUNIT) +// how much the automap moves window per tic in frame-buffer coordinates +// moves 140 pixels in 1 second +#define F_PANINC 4 +// how much zoom-in per tic +// goes to 2x in 1 second +#define M_ZOOMIN ((int) (1.02*FRACUNIT)) +// how much zoom-out per tic +// pulls out to 0.5x in 1 second +#define M_ZOOMOUT ((int) (FRACUNIT/1.02)) + +// translates between frame-buffer and map distances +#define FTOM(x) FixedMul(((x)<<16),scale_ftom) +#define MTOF(x) (FixedMul((x),scale_mtof)>>16) +// translates between frame-buffer and map coordinates +#define CXMTOF(x) (f_x + MTOF((x)-m_x)) +#define CYMTOF(y) (f_y + (f_h - MTOF((y)-m_y))) + +// the following is crap +#define LINE_NEVERSEE ML_DONTDRAW + + +typedef struct +{ + int x, y; +} fpoint_t; + + +typedef struct +{ + fpoint_t a, b; +} fline_t; + + +typedef struct +{ + fixed_t x, y; +} mpoint_t; + + +typedef struct +{ + mpoint_t a, b; +} mline_t; + + +typedef struct +{ + fixed_t slp, islp; +} islope_t; + + +// +// The vector graphics for the automap. +// A line drawing of the player pointing right, +// starting from the middle. +// +#define R ((8*PLAYERRADIUS)/7) +mline_t player_arrow[] = { + { { -R + R / 8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R - R / 2, R / 4 } }, // -----> + { { R, 0 }, { R - R / 2, -R / 4 } }, + { { -R + R / 8, 0 }, { -R - R / 8, R / 4 } }, // >----> + { { -R + R / 8, 0 }, { -R - R / 8, -R / 4 } }, + { { -R + 3 * R / 8, 0 }, { -R + R / 8, R / 4 } }, // >>---> + { { -R + 3 * R / 8, 0 }, { -R + R / 8, -R / 4 } } +}; +#undef R +#define NUMPLYRLINES (sizeof(player_arrow)/sizeof(mline_t)) + +#define R ((8*PLAYERRADIUS)/7) +mline_t cheat_player_arrow[] = { + { { -R + R / 8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R - R / 2, R / 6 } }, // -----> + { { R, 0 }, { R - R / 2, -R / 6 } }, + { { -R + R / 8, 0 }, { -R - R / 8, R / 6 } }, // >-----> + { { -R + R / 8, 0 }, { -R - R / 8, -R / 6 } }, + { { -R + 3 * R / 8, 0 }, { -R + R / 8, R / 6 } }, // >>-----> + { { -R + 3 * R / 8, 0 }, { -R + R / 8, -R / 6 } }, + { { -R / 2, 0 }, { -R / 2, -R / 6 } }, // >>-d---> + { { -R / 2, -R / 6 }, { -R / 2 + R / 6, -R / 6 } }, + { { -R / 2 + R / 6, -R / 6 }, { -R / 2 + R / 6, R / 4 } }, + { { -R / 6, 0 }, { -R / 6, -R / 6 } }, // >>-dd--> + { { -R / 6, -R / 6 }, { 0, -R / 6 } }, + { { 0, -R / 6 }, { 0, R / 4 } }, + { { R / 6, R / 4 }, { R / 6, -R / 7 } }, // >>-ddt-> + { { R / 6, -R / 7 }, { R / 6 + R / 32, -R / 7 - R / 32 } }, + { { R / 6 + R / 32, -R / 7 - R / 32 }, { R / 6 + R / 10, -R / 7 } } +}; +#undef R +#define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t)) + +#define R (FRACUNIT) +mline_t triangle_guy[] = { + { { (fixed_t)(-.867 * R), (fixed_t)(-.5 * R) }, { (fixed_t)(.867 * R), (fixed_t)(-.5 * R) } }, + { { (fixed_t)(.867 * R), (fixed_t)(-.5 * R) } , { 0, R } }, + { { 0, R }, { (fixed_t)(-.867 * R), (fixed_t)(-.5 * R) } } +}; +#undef R +#define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t)) + +#define R (FRACUNIT) +mline_t thintriangle_guy[] = { + { { (fixed_t)(-.5 * R), (fixed_t)(-.7 * R) }, { R, 0 } }, + { { R, 0 }, { (fixed_t)(-.5 * R), (fixed_t)(.7 * R) } }, + { { (fixed_t)(-.5 * R), (fixed_t)(.7 * R) }, { (fixed_t)(-.5 * R), (fixed_t)(-.7 * R) } } +}; +#undef R +#define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t)) + + +static int cheating = 0; +static int grid = 0; + +static int leveljuststarted = 1; // kluge until AM_LevelInit() is called + +static int finit_width = SCREENWIDTH; +static int finit_height = SCREENHEIGHT - 32; + +// location of window on screen +static int f_x; +static int f_y; + +// size of window on screen +static int f_w; +static int f_h; + +static int lightlev; // used for funky strobing effect +static byte* fb; // pseudo-frame buffer +static int amclock; + +static mpoint_t m_paninc; // how far the window pans each tic (map coords) +static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords) +static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords) + +static fixed_t m_x, m_y; // LL x,y where the window is on the map (map coords) +static fixed_t m_x2, m_y2; // UR x,y where the window is on the map (map coords) + +// +// width/height of window on map (map coords) +// +static fixed_t m_w; +static fixed_t m_h; + +// based on level size +static fixed_t min_x; +static fixed_t min_y; +static fixed_t max_x; +static fixed_t max_y; + +static fixed_t max_w; // max_x-min_x, +static fixed_t max_h; // max_y-min_y + +// based on player size +static fixed_t min_w; +static fixed_t min_h; + +static fixed_t min_scale_mtof; // used to tell when to stop zooming out +static fixed_t max_scale_mtof; // used to tell when to stop zooming in + +// old stuff for recovery later +static fixed_t old_m_w, old_m_h; +static fixed_t old_m_x, old_m_y; + +// old location used by the Follower routine +static mpoint_t f_oldloc; + +// used by MTOF to scale from map-to-frame-buffer coords +static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF; +// used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) +static fixed_t scale_ftom; + +static player_t* plr; // the player represented by an arrow + +static patch_t* marknums[10]; // numbers used for marking by the automap +static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are +static int markpointnum = 0; // next point to be assigned + +static int followplayer = 1; // specifies whether to follow the player around + +static unsigned char cheat_amap_seq[] = { 0xb2, 0x26, 0x26, 0x2e, 0xff }; +static cheatseq_t cheat_amap = { cheat_amap_seq, 0 }; + +static doom_boolean stopped = true; + + +doom_boolean automapactive = false; + + +extern doom_boolean viewactive; + + +void V_MarkRect(int x, int y, int width, int height); + + +// Calculates the slope and slope according to the x-axis of a line +// segment in map coordinates (with the upright y-axis n' all) so +// that it can be used with the brain-dead drawing stuff. +void AM_getIslope(mline_t* ml, islope_t* is) +{ + int dx, dy; + + dy = ml->a.y - ml->b.y; + dx = ml->b.x - ml->a.x; + if (!dy) is->islp = (dx < 0 ? -DOOM_MAXINT : DOOM_MAXINT); + else is->islp = FixedDiv(dx, dy); + if (!dx) is->slp = (dy < 0 ? -DOOM_MAXINT : DOOM_MAXINT); + else is->slp = FixedDiv(dy, dx); +} + + +// +// +// +void AM_activateNewScale(void) +{ + m_x += m_w / 2; + m_y += m_h / 2; + m_w = FTOM(f_w); + m_h = FTOM(f_h); + m_x -= m_w / 2; + m_y -= m_h / 2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + + +// +// +// +void AM_saveScaleAndLoc(void) +{ + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; +} + + +// +// +// +void AM_restoreScaleAndLoc(void) +{ + m_w = old_m_w; + m_h = old_m_h; + if (!followplayer) + { + m_x = old_m_x; + m_y = old_m_y; + } + else + { + m_x = plr->mo->x - m_w / 2; + m_y = plr->mo->y - m_h / 2; + } + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + // Change the scaling multipliers + scale_mtof = FixedDiv(f_w << FRACBITS, m_w); + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); +} + + +// +// adds a marker at the current location +// +void AM_addMark(void) +{ + markpoints[markpointnum].x = m_x + m_w / 2; + markpoints[markpointnum].y = m_y + m_h / 2; + markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS; +} + + +// +// Determines bounding box of all vertices, +// sets global variables controlling zoom range. +// +void AM_findMinMaxBoundaries(void) +{ + int i; + fixed_t a; + fixed_t b; + + min_x = min_y = DOOM_MAXINT; + max_x = max_y = -DOOM_MAXINT; + + for (i = 0; i < numvertexes; i++) + { + if (vertexes[i].x < min_x) + min_x = vertexes[i].x; + else if (vertexes[i].x > max_x) + max_x = vertexes[i].x; + + if (vertexes[i].y < min_y) + min_y = vertexes[i].y; + else if (vertexes[i].y > max_y) + max_y = vertexes[i].y; + } + + max_w = max_x - min_x; + max_h = max_y - min_y; + + min_w = 2 * PLAYERRADIUS; // const? never changed? + min_h = 2 * PLAYERRADIUS; + + a = FixedDiv(f_w << FRACBITS, max_w); + b = FixedDiv(f_h << FRACBITS, max_h); + + min_scale_mtof = a < b ? a : b; + max_scale_mtof = FixedDiv(f_h << FRACBITS, 2 * PLAYERRADIUS); +} + + +// +// +// +void AM_changeWindowLoc(void) +{ + if (m_paninc.x || m_paninc.y) + { + followplayer = 0; + f_oldloc.x = DOOM_MAXINT; + } + + m_x += m_paninc.x; + m_y += m_paninc.y; + + if (m_x + m_w / 2 > max_x) + m_x = max_x - m_w / 2; + else if (m_x + m_w / 2 < min_x) + m_x = min_x - m_w / 2; + + if (m_y + m_h / 2 > max_y) + m_y = max_y - m_h / 2; + else if (m_y + m_h / 2 < min_y) + m_y = min_y - m_h / 2; + + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + + +// +// +// +void AM_initVariables(void) +{ + int pnum; + static event_t st_notify = { ev_keyup, AM_MSGENTERED }; + + automapactive = true; + fb = screens[0]; + + f_oldloc.x = DOOM_MAXINT; + amclock = 0; + lightlev = 0; + + m_paninc.x = m_paninc.y = 0; + ftom_zoommul = FRACUNIT; + mtof_zoommul = FRACUNIT; + + m_w = FTOM(f_w); + m_h = FTOM(f_h); + + // find player to center on initially + if (!playeringame[pnum = consoleplayer]) + for (pnum = 0; pnum < MAXPLAYERS; pnum++) + if (playeringame[pnum]) + break; + + plr = &players[pnum]; + m_x = plr->mo->x - m_w / 2; + m_y = plr->mo->y - m_h / 2; + AM_changeWindowLoc(); + + // for saving & restoring + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; + + // inform the status bar of the change + ST_Responder(&st_notify); +} + + +// +// +// +void AM_loadPics(void) +{ + int i; + char namebuf[9]; + + for (i = 0; i < 10; i++) + { + doom_concat(doom_strcpy(namebuf, "AMMNUM"), doom_itoa(i, 10)); + marknums[i] = W_CacheLumpName(namebuf, PU_STATIC); + } +} + + +void AM_unloadPics(void) +{ + int i; + + for (i = 0; i < 10; i++) + Z_ChangeTag(marknums[i], PU_CACHE); +} + + +void AM_clearMarks(void) +{ + int i; + + for (i = 0; i < AM_NUMMARKPOINTS; i++) + markpoints[i].x = -1; // means empty + markpointnum = 0; +} + + +// +// should be called at the start of every level +// right now, i figure it out myself +// +void AM_LevelInit(void) +{ + leveljuststarted = 0; + + f_x = f_y = 0; + f_w = finit_width; + f_h = finit_height; + + AM_clearMarks(); + + AM_findMinMaxBoundaries(); + scale_mtof = FixedDiv(min_scale_mtof, (int)(0.7 * FRACUNIT)); + if (scale_mtof > max_scale_mtof) + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); +} + + +// +// +// +void AM_Stop(void) +{ + static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED }; + + AM_unloadPics(); + automapactive = false; + ST_Responder(&st_notify); + stopped = true; +} + + +// +// +// +void AM_Start(void) +{ + static int lastlevel = -1, lastepisode = -1; + + if (!stopped) AM_Stop(); + stopped = false; + if (lastlevel != gamemap || lastepisode != gameepisode) + { + AM_LevelInit(); + lastlevel = gamemap; + lastepisode = gameepisode; + } + AM_initVariables(); + AM_loadPics(); +} + + +// +// set the window scale to the maximum size +// +void AM_minOutWindowScale(void) +{ + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + + +// +// set the window scale to the minimum size +// +void AM_maxOutWindowScale(void) +{ + scale_mtof = max_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + + +// +// Handle events (user inputs) in automap mode +// +doom_boolean AM_Responder(event_t* ev) +{ + int rc; + static int cheatstate = 0; + static int bigstate = 0; + static char buffer[20]; + + rc = false; + + if (!automapactive) + { + if (ev->type == ev_keydown && ev->data1 == AM_STARTKEY) + { + AM_Start(); + viewactive = false; + rc = true; + } + } + + else if (ev->type == ev_keydown) + { + + rc = true; + switch (ev->data1) + { + case AM_PANRIGHTKEY: // pan right + if (!followplayer) m_paninc.x = FTOM(F_PANINC); + else rc = false; + break; + case AM_PANLEFTKEY: // pan left + if (!followplayer) m_paninc.x = -FTOM(F_PANINC); + else rc = false; + break; + case AM_PANUPKEY: // pan up + if (!followplayer) m_paninc.y = FTOM(F_PANINC); + else rc = false; + break; + case AM_PANDOWNKEY: // pan down + if (!followplayer) m_paninc.y = -FTOM(F_PANINC); + else rc = false; + break; + case AM_ZOOMOUTKEY: // zoom out + mtof_zoommul = M_ZOOMOUT; + ftom_zoommul = M_ZOOMIN; + break; + case AM_ZOOMINKEY: // zoom in + mtof_zoommul = M_ZOOMIN; + ftom_zoommul = M_ZOOMOUT; + break; + case AM_ENDKEY: + bigstate = 0; + viewactive = true; + AM_Stop(); + break; + case AM_GOBIGKEY: + bigstate = !bigstate; + if (bigstate) + { + AM_saveScaleAndLoc(); + AM_minOutWindowScale(); + } + else AM_restoreScaleAndLoc(); + break; + case AM_FOLLOWKEY: + followplayer = !followplayer; + f_oldloc.x = DOOM_MAXINT; + plr->message = followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF; + break; + case AM_GRIDKEY: + grid = !grid; + plr->message = grid ? AMSTR_GRIDON : AMSTR_GRIDOFF; + break; + case AM_MARKKEY: + doom_strcpy(buffer, AMSTR_MARKEDSPOT); + doom_concat(buffer, " "); + doom_concat(buffer, doom_itoa(markpointnum, 10)); + //doom_sprintf(buffer, "%s %d", AMSTR_MARKEDSPOT, markpointnum); + plr->message = buffer; + AM_addMark(); + break; + case AM_CLEARMARKKEY: + AM_clearMarks(); + plr->message = AMSTR_MARKSCLEARED; + break; + default: + cheatstate = 0; + rc = false; + } + if (!deathmatch && cht_CheckCheat(&cheat_amap, ev->data1)) + { + rc = false; + cheating = (cheating + 1) % 3; + } + } + + else if (ev->type == ev_keyup) + { + rc = false; + switch (ev->data1) + { + case AM_PANRIGHTKEY: + if (!followplayer) m_paninc.x = 0; + break; + case AM_PANLEFTKEY: + if (!followplayer) m_paninc.x = 0; + break; + case AM_PANUPKEY: + if (!followplayer) m_paninc.y = 0; + break; + case AM_PANDOWNKEY: + if (!followplayer) m_paninc.y = 0; + break; + case AM_ZOOMOUTKEY: + case AM_ZOOMINKEY: + mtof_zoommul = FRACUNIT; + ftom_zoommul = FRACUNIT; + break; + } + } + + return rc; +} + + +// +// Zooming +// +void AM_changeWindowScale(void) +{ + + // Change the scaling multipliers + scale_mtof = FixedMul(scale_mtof, mtof_zoommul); + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + + if (scale_mtof < min_scale_mtof) + AM_minOutWindowScale(); + else if (scale_mtof > max_scale_mtof) + AM_maxOutWindowScale(); + else + AM_activateNewScale(); +} + + +// +// +// +void AM_doFollowPlayer(void) +{ + if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) + { + m_x = FTOM(MTOF(plr->mo->x)) - m_w / 2; + m_y = FTOM(MTOF(plr->mo->y)) - m_h / 2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + f_oldloc.x = plr->mo->x; + f_oldloc.y = plr->mo->y; + } +} + + +// +// +// +void AM_updateLightLev(void) +{ + static int nexttic = 0; + //static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 }; + static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 }; + static int litelevelscnt = 0; + + // Change light level + if (amclock > nexttic) + { + lightlev = litelevels[litelevelscnt++]; + if (litelevelscnt == sizeof(litelevels) / sizeof(int)) litelevelscnt = 0; + nexttic = amclock + 6 - (amclock % 6); + } +} + + +// +// Updates on Game Tick +// +void AM_Ticker(void) +{ + if (!automapactive) + return; + + amclock++; + + if (followplayer) + AM_doFollowPlayer(); + + // Change the zoom if necessary + if (ftom_zoommul != FRACUNIT) + AM_changeWindowScale(); + + // Change x,y location + if (m_paninc.x || m_paninc.y) + AM_changeWindowLoc(); + + // Update light level + // AM_updateLightLev(); +} + + +// +// Clear automap frame buffer. +// +void AM_clearFB(int color) +{ + doom_memset(fb, color, f_w * f_h); +} + + +// +// Automap clipping of lines. +// +// Based on Cohen-Sutherland clipping algorithm but with a slightly +// faster reject and precalculated slopes. If the speed is needed, +// use a hash algorithm to handle the common cases. +// +doom_boolean AM_clipMline(mline_t* ml, fline_t* fl) +{ + enum + { + LEFT = 1, + RIGHT = 2, + BOTTOM = 4, + TOP = 8 + }; + + register int outcode1 = 0; + register int outcode2 = 0; + register int outside; + + fpoint_t tmp; + int dx; + int dy; + + +#define DOOUTCODE(oc, mx, my) \ + (oc) = 0; \ + if ((my) < 0) (oc) |= TOP; \ + else if ((my) >= f_h) (oc) |= BOTTOM; \ + if ((mx) < 0) (oc) |= LEFT; \ + else if ((mx) >= f_w) (oc) |= RIGHT; + + + // do trivial rejects and outcodes + if (ml->a.y > m_y2) + outcode1 = TOP; + else if (ml->a.y < m_y) + outcode1 = BOTTOM; + + if (ml->b.y > m_y2) + outcode2 = TOP; + else if (ml->b.y < m_y) + outcode2 = BOTTOM; + + if (outcode1 & outcode2) + return false; // trivially outside + + if (ml->a.x < m_x) + outcode1 |= LEFT; + else if (ml->a.x > m_x2) + outcode1 |= RIGHT; + + if (ml->b.x < m_x) + outcode2 |= LEFT; + else if (ml->b.x > m_x2) + outcode2 |= RIGHT; + + if (outcode1 & outcode2) + return false; // trivially outside + + // transform to frame-buffer coordinates. + fl->a.x = CXMTOF(ml->a.x); + fl->a.y = CYMTOF(ml->a.y); + fl->b.x = CXMTOF(ml->b.x); + fl->b.y = CYMTOF(ml->b.y); + + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + + if (outcode1 & outcode2) + return false; + + while (outcode1 | outcode2) + { + // may be partially inside box + // find an outside point + if (outcode1) + outside = outcode1; + else + outside = outcode2; + + // clip to each side + if (outside & TOP) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx * (fl->a.y)) / dy; + tmp.y = 0; + } + else if (outside & BOTTOM) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx * (fl->a.y - f_h)) / dy; + tmp.y = f_h - 1; + } + else if (outside & RIGHT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy * (f_w - 1 - fl->a.x)) / dx; + tmp.x = f_w - 1; + } + else if (outside & LEFT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy * (-fl->a.x)) / dx; + tmp.x = 0; + } + + if (outside == outcode1) + { + fl->a = tmp; + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + } + else + { + fl->b = tmp; + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + } + + if (outcode1 & outcode2) + return false; // trivially outside + } + + return true; +} +#undef DOOUTCODE + + +// +// Classic Bresenham w/ whatever optimizations needed for speed +// +void AM_drawFline(fline_t* fl, int color) +{ + register int x; + register int y; + register int dx; + register int dy; + register int sx; + register int sy; + register int ax; + register int ay; + register int d; + + static int fuck = 0; + + // For debugging only +#if 0 // [pd] Don't waste CPU cycles testing this then + if (fl->a.x < 0 || fl->a.x >= f_w + || fl->a.y < 0 || fl->a.y >= f_h + || fl->b.x < 0 || fl->b.x >= f_w + || fl->b.y < 0 || fl->b.y >= f_h) + { + doom_print("fuck "); + doom_print(doom_itoa(fuck++, 10)); + doom_print("\r"); + return; + } +#endif + +#define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc) + + dx = fl->b.x - fl->a.x; + ax = 2 * (dx < 0 ? -dx : dx); + sx = dx < 0 ? -1 : 1; + + dy = fl->b.y - fl->a.y; + ay = 2 * (dy < 0 ? -dy : dy); + sy = dy < 0 ? -1 : 1; + + x = fl->a.x; + y = fl->a.y; + + if (ax > ay) + { + d = ay - ax / 2; + while (1) + { + PUTDOT(x, y, color); + if (x == fl->b.x) return; + if (d >= 0) + { + y += sy; + d -= ax; + } + x += sx; + d += ay; + } + } + else + { + d = ax - ay / 2; + while (1) + { + PUTDOT(x, y, color); + if (y == fl->b.y) return; + if (d >= 0) + { + x += sx; + d -= ay; + } + y += sy; + d += ax; + } + } +} + + +// +// Clip lines, draw visible part sof lines. +// +void AM_drawMline(mline_t* ml, int color) +{ + static fline_t fl; + + if (AM_clipMline(ml, &fl)) + AM_drawFline(&fl, color); // draws it on frame buffer using fb coords +} + + +// +// Draws flat (floor/ceiling tile) aligned grid lines. +// +void AM_drawGrid(int color) +{ + fixed_t x, y; + fixed_t start, end; + mline_t ml; + + // Figure out start of vertical gridlines + start = m_x; + if ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS)) + start += (MAPBLOCKUNITS << FRACBITS) + - ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS)); + end = m_x + m_w; + + // draw vertical gridlines + ml.a.y = m_y; + ml.b.y = m_y + m_h; + for (x = start; x < end; x += (MAPBLOCKUNITS << FRACBITS)) + { + ml.a.x = x; + ml.b.x = x; + AM_drawMline(&ml, color); + } + + // Figure out start of horizontal gridlines + start = m_y; + if ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS)) + start += (MAPBLOCKUNITS << FRACBITS) + - ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS)); + end = m_y + m_h; + + // draw horizontal gridlines + ml.a.x = m_x; + ml.b.x = m_x + m_w; + for (y = start; y < end; y += (MAPBLOCKUNITS << FRACBITS)) + { + ml.a.y = y; + ml.b.y = y; + AM_drawMline(&ml, color); + } +} + + +// +// Determines visible lines, draws them. +// This is LineDef based, not LineSeg based. +// +void AM_drawWalls(void) +{ + int i; + static mline_t l; + + for (i = 0; i < numlines; i++) + { + l.a.x = lines[i].v1->x; + l.a.y = lines[i].v1->y; + l.b.x = lines[i].v2->x; + l.b.y = lines[i].v2->y; + if (cheating || (lines[i].flags & ML_MAPPED)) + { + if ((lines[i].flags & LINE_NEVERSEE) && !cheating) + continue; + if (!lines[i].backsector) + { + AM_drawMline(&l, WALLCOLORS + lightlev); + } + else + { + if (lines[i].special == 39) + { // teleporters + AM_drawMline(&l, WALLCOLORS + WALLRANGE / 2); + } + else if (lines[i].flags & ML_SECRET) // secret door + { + if (cheating) AM_drawMline(&l, SECRETWALLCOLORS + lightlev); + else AM_drawMline(&l, WALLCOLORS + lightlev); + } + else if (lines[i].backsector->floorheight + != lines[i].frontsector->floorheight) + { + AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change + } + else if (lines[i].backsector->ceilingheight + != lines[i].frontsector->ceilingheight) + { + AM_drawMline(&l, CDWALLCOLORS + lightlev); // ceiling level change + } + else if (cheating) + { + AM_drawMline(&l, TSWALLCOLORS + lightlev); + } + } + } + else if (plr->powers[pw_allmap]) + { + if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS + 3); + } + } +} + + +// +// Rotation in 2D. +// Used to rotate player arrow line character. +// +void AM_rotate(fixed_t* x, fixed_t* y, angle_t a) +{ + fixed_t tmpx; + + tmpx = + FixedMul(*x, finecosine[a >> ANGLETOFINESHIFT]) + - FixedMul(*y, finesine[a >> ANGLETOFINESHIFT]); + + *y = + FixedMul(*x, finesine[a >> ANGLETOFINESHIFT]) + + FixedMul(*y, finecosine[a >> ANGLETOFINESHIFT]); + + *x = tmpx; +} + + +void AM_drawLineCharacter(mline_t* lineguy, + int lineguylines, + fixed_t scale, + angle_t angle, + int color, + fixed_t x, + fixed_t y) +{ + int i; + mline_t l; + + for (i = 0; i < lineguylines; i++) + { + l.a.x = lineguy[i].a.x; + l.a.y = lineguy[i].a.y; + + if (scale) + { + l.a.x = FixedMul(scale, l.a.x); + l.a.y = FixedMul(scale, l.a.y); + } + + if (angle) + AM_rotate(&l.a.x, &l.a.y, angle); + + l.a.x += x; + l.a.y += y; + + l.b.x = lineguy[i].b.x; + l.b.y = lineguy[i].b.y; + + if (scale) + { + l.b.x = FixedMul(scale, l.b.x); + l.b.y = FixedMul(scale, l.b.y); + } + + if (angle) + AM_rotate(&l.b.x, &l.b.y, angle); + + l.b.x += x; + l.b.y += y; + + AM_drawMline(&l, color); + } +} + + +void AM_drawPlayers(void) +{ + int i; + player_t* p; + static int their_colors[] = { GREENS, GRAYS, BROWNS, REDS }; + int their_color = -1; + int color; + + if (!netgame) + { + if (cheating) + AM_drawLineCharacter + (cheat_player_arrow, NUMCHEATPLYRLINES, 0, + plr->mo->angle, WHITE, plr->mo->x, plr->mo->y); + else + AM_drawLineCharacter + (player_arrow, NUMPLYRLINES, 0, plr->mo->angle, + WHITE, plr->mo->x, plr->mo->y); + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + their_color++; + p = &players[i]; + + if ((deathmatch && !singledemo) && p != plr) + continue; + + if (!playeringame[i]) + continue; + + if (p->powers[pw_invisibility]) + color = 246; // *close* to black + else + color = their_colors[their_color]; + + AM_drawLineCharacter + (player_arrow, NUMPLYRLINES, 0, p->mo->angle, + color, p->mo->x, p->mo->y); + } +} + + +void AM_drawThings(int colors, int colorrange) +{ + int i; + mobj_t* t; + + for (i = 0; i < numsectors; i++) + { + t = sectors[i].thinglist; + while (t) + { + AM_drawLineCharacter + (thintriangle_guy, NUMTHINTRIANGLEGUYLINES, + 16 << FRACBITS, t->angle, colors + lightlev, t->x, t->y); + t = t->snext; + } + } +} + + +void AM_drawMarks(void) +{ + int i, fx, fy, w, h; + + for (i = 0; i < AM_NUMMARKPOINTS; i++) + { + if (markpoints[i].x != -1) + { + // w = SHORT(marknums[i]->width); + // h = SHORT(marknums[i]->height); + w = 5; // because something's wrong with the wad, i guess + h = 6; // because something's wrong with the wad, i guess + fx = CXMTOF(markpoints[i].x); + fy = CYMTOF(markpoints[i].y); + if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h) + V_DrawPatch(fx, fy, FB, marknums[i]); + } + } +} + + +void AM_drawCrosshair(int color) +{ + fb[(f_w * (f_h + 1)) / 2] = color; // single point for now +} + + +void AM_Drawer(void) +{ + if (!automapactive) return; + + AM_clearFB(BACKGROUND); + if (grid) + AM_drawGrid(GRIDCOLORS); + AM_drawWalls(); + AM_drawPlayers(); + if (cheating == 2) + AM_drawThings(THINGCOLORS, THINGRANGE); + AM_drawCrosshair(XHAIRCOLORS); + + AM_drawMarks(); + + V_MarkRect(f_x, f_y, f_w, f_h); +} +weaponinfo_t weaponinfo[NUMWEAPONS] = +{ + { + // fist + am_noammo, + S_PUNCHUP, + S_PUNCHDOWN, + S_PUNCH, + S_PUNCH1, + S_NULL + }, + { + // pistol + am_clip, + S_PISTOLUP, + S_PISTOLDOWN, + S_PISTOL, + S_PISTOL1, + S_PISTOLFLASH + }, + { + // shotgun + am_shell, + S_SGUNUP, + S_SGUNDOWN, + S_SGUN, + S_SGUN1, + S_SGUNFLASH1 + }, + { + // chaingun + am_clip, + S_CHAINUP, + S_CHAINDOWN, + S_CHAIN, + S_CHAIN1, + S_CHAINFLASH1 + }, + { + // missile launcher + am_misl, + S_MISSILEUP, + S_MISSILEDOWN, + S_MISSILE, + S_MISSILE1, + S_MISSILEFLASH1 + }, + { + // plasma rifle + am_cell, + S_PLASMAUP, + S_PLASMADOWN, + S_PLASMA, + S_PLASMA1, + S_PLASMAFLASH1 + }, + { + // bfg 9000 + am_cell, + S_BFGUP, + S_BFGDOWN, + S_BFG, + S_BFG1, + S_BFGFLASH1 + }, + { + // chainsaw + am_noammo, + S_SAWUP, + S_SAWDOWN, + S_SAW, + S_SAW1, + S_NULL + }, + { + // super shotgun + am_shell, + S_DSGUNUP, + S_DSGUNDOWN, + S_DSGUN, + S_DSGUN1, + S_DSGUNFLASH1 + }, +}; +#if defined(DOOM_WIN32) + + +#define X_OK 0 +#define W_OK 2 +#define R_OK 4 +#define RW_OK 6 +#elif defined(DOOM_APPLE) + + +#endif + +//#include "doomdef.h" +//#include "doomstat.h" +//#include "dstrings.h" +//#include "sounds.h" +//#include "z_zone.h" +//#include "w_wad.h" +//#include "s_sound.h" +//#include "v_video.h" +//#include "f_finale.h" +//#include "f_wipe.h" +//#include "m_argv.h" +//#include "m_misc.h" +//#include "m_menu.h" +//#include "i_system.h" +//#include "i_sound.h" +//#include "i_video.h" +//#include "g_game.h" +//#include "hu_stuff.h" +//#include "wi_stuff.h" +//#include "st_stuff.h" +//#include "am_map.h" +//#include "p_setup.h" +//#include "r_local.h" +//#include "d_main.h" + + +#define MAXARGVS 100 + + +char* wadfiles[MAXWADFILES]; + +doom_boolean devparm; // started game with -devparm +doom_boolean nomonsters; // checkparm of -nomonsters +doom_boolean respawnparm; // checkparm of -respawn +doom_boolean fastparm; // checkparm of -fast + +doom_boolean drone; + +doom_boolean singletics = true; // debug flag to cancel adaptiveness + +doom_boolean is_wiping_screen = false; + +skill_t startskill; +int startepisode; +int startmap; +doom_boolean autostart; + +void* debugfile = 0; + +doom_boolean advancedemo; + +char wadfile[1024]; // primary wad file +char mapdir[1024]; // directory of development maps +char basedefault[1024]; // default file + +// +// EVENT HANDLING +// +// Events are asynchronous inputs generally generated by the game user. +// Events can be discarded if no responder claims them +// +event_t events[MAXEVENTS]; +int eventhead; +int eventtail; + +// wipegamestate can be set to -1 to force a wipe on the next draw +gamestate_t wipegamestate = GS_DEMOSCREEN; +void R_ExecuteSetViewSize(void); + +// print title for every printed line +char title[128]; + + +extern doom_boolean inhelpscreens; +extern doom_boolean setsizeneeded; +extern int showMessages; +extern doom_boolean demorecording; + + +void D_DoomLoop(void); +void D_CheckNetGame(void); +void D_ProcessEvents(void); +void G_BuildTiccmd(ticcmd_t* cmd); +void D_DoAdvanceDemo(void); + + +// +// D_PostEvent +// Called by the I/O functions when input is detected +// +void D_PostEvent(event_t* ev) +{ + events[eventhead] = *ev; + eventhead++; + eventhead = (eventhead) & (MAXEVENTS - 1); +} + + +// +// D_ProcessEvents +// Send all the events of the given timestamp down the responder chain +// +void D_ProcessEvents(void) +{ + event_t* ev; + + // IF STORE DEMO, DO NOT ACCEPT INPUT + if ((gamemode == commercial) + && (W_CheckNumForName("map01") < 0)) + return; + + for (; eventtail != eventhead; ) + { + ev = &events[eventtail]; + if (!M_Responder(ev)) + G_Responder(ev); + // else menu ate the event + eventtail++; + eventtail = (eventtail) & (MAXEVENTS - 1); + } +} + + +// +// D_Display +// draw current display, possibly wiping it from the previous +// +void D_Display(void) +{ + static doom_boolean viewactivestate = false; + static doom_boolean menuactivestate = false; + static doom_boolean inhelpscreensstate = false; + static doom_boolean fullscreen = false; + static gamestate_t oldgamestate = -1; + static int borderdrawcount; + int wipestart; + int y; + doom_boolean wipe; + doom_boolean redrawsbar; + + if (nodrawers) + return; // for comparative timing / profiling + + redrawsbar = false; + + // change the view size if needed + if (setsizeneeded) + { + R_ExecuteSetViewSize(); + oldgamestate = -1; // force background redraw + borderdrawcount = 3; + } + + // save the current screen if about to wipe + if (gamestate != wipegamestate) + { + wipe = true; + wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); + } + else + wipe = false; + + if (gamestate == GS_LEVEL && gametic) + HU_Erase(); + + // do buffered drawing + switch (gamestate) + { + case GS_LEVEL: + if (!gametic) + break; + if (automapactive) + AM_Drawer(); + if (wipe || (viewheight != 200 && fullscreen)) + redrawsbar = true; + if (inhelpscreensstate && !inhelpscreens) + redrawsbar = true; // just put away the help screen + ST_Drawer(viewheight == 200, redrawsbar); + fullscreen = viewheight == 200; + break; + + case GS_INTERMISSION: + WI_Drawer(); + break; + + case GS_FINALE: + F_Drawer(); + break; + + case GS_DEMOSCREEN: + D_PageDrawer(); + break; + } + + // draw buffered stuff to screen + I_UpdateNoBlit(); + + // draw the view directly + if (gamestate == GS_LEVEL && !automapactive && gametic) + R_RenderPlayerView(&players[displayplayer]); + + if (gamestate == GS_LEVEL && gametic) + HU_Drawer(); + + // clean up border stuff + if (gamestate != oldgamestate && gamestate != GS_LEVEL) + I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); + + // see if the border needs to be initially drawn + if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL) + { + viewactivestate = false; // view was not active + R_FillBackScreen(); // draw the pattern into the back screen + } + + // see if the border needs to be updated to the screen + if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != 320) + { + if (menuactive || menuactivestate || !viewactivestate) + borderdrawcount = 3; + if (borderdrawcount) + { + R_DrawViewBorder(); // erase old menu stuff + borderdrawcount--; + } + + } + + menuactivestate = menuactive; + viewactivestate = viewactive; + inhelpscreensstate = inhelpscreens; + oldgamestate = wipegamestate = gamestate; + + // draw pause pic + if (paused) + { + if (automapactive) + y = 4; + else + y = viewwindowy + 4; + V_DrawPatchDirect(viewwindowx + (scaledviewwidth - 68) / 2, + y, 0, W_CacheLumpName("M_PAUSE", PU_CACHE)); + } + + + // menus go directly to the screen + M_Drawer(); // menu is drawn even on top of everything + NetUpdate(); // send out any new accumulation + + + // normal update + is_wiping_screen = wipe; + if (!wipe) + { + I_FinishUpdate(); // page flip or blit buffer + return; + } + + // wipe update + wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); + + wipestart = I_GetTime() - 1; + +#if 0 // [pd] Moved to D_UpdateWipe + do + { + do + { + nowtime = I_GetTime(); + tics = nowtime - wipestart; + } while (!tics); + wipestart = nowtime; + done = wipe_ScreenWipe(wipe_Melt + , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics); + I_UpdateNoBlit(); + M_Drawer(); // menu is drawn even on top of wipes + I_FinishUpdate(); // page flip or blit buffer + } while (!done); +#endif +} + + +// +// D_DoomLoop +// +void D_UpdateWipe(void) +{ + if (wipe_ScreenWipe(wipe_Melt, 0, 0, SCREENWIDTH, SCREENHEIGHT, 1)) + is_wiping_screen = false; +} + + +void D_DoomLoop(void) +{ +#if 0 // [pd] Moved to D_DoomMain() + if (demorecording) + G_BeginRecording(); + + if (M_CheckParm("-debugfile")) + { + char filename[20]; + //doom_sprintf(filename, "debug%i.txt", consoleplayer); + doom_print("debug output to: %s\n", filename); + debugfile = doom_open(filename, "w"); + } + + I_InitGraphics(); +#endif + + // while (1) + { + // frame syncronous IO operations + I_StartFrame(); + + // process one or more tics + if (singletics) + { + I_StartTic(); + D_ProcessEvents(); + G_BuildTiccmd(&netcmds[consoleplayer][maketic % BACKUPTICS]); + if (advancedemo) + D_DoAdvanceDemo(); + M_Ticker(); + G_Ticker(); + gametic++; + maketic++; + } + else + { + TryRunTics(); // will run at least one tic + } + + S_UpdateSounds(players[consoleplayer].mo);// move positional sounds + + // Update display, next frame, with current state. + D_Display(); + +#if 0 // [pd] Sound is queried by the application's audio thread. + // Sound mixing for the buffer is snychronous. + I_UpdateSound(); + + // Synchronous sound output is explicitly called. + // Update sound output. + I_SubmitSound(); +#endif + } +} + + +// +// DEMO LOOP +// +int demosequence; +int pagetic; +char* pagename; + + +// +// D_PageTicker +// Handles timing for warped projection +// +void D_PageTicker(void) +{ + if (--pagetic < 0) + D_AdvanceDemo(); +} + + +// +// D_PageDrawer +// +void D_PageDrawer(void) +{ + V_DrawPatch(0, 0, 0, W_CacheLumpName(pagename, PU_CACHE)); +} + + +// +// D_AdvanceDemo +// Called after each demo or intro demosequence finishes +// +void D_AdvanceDemo(void) +{ + advancedemo = true; +} + + +// +// This cycles through the demo sequences. +// FIXME - version dependend demo numbers? +// +void D_DoAdvanceDemo(void) +{ + players[consoleplayer].playerstate = PST_LIVE; // not reborn + advancedemo = false; + usergame = false; // no save / end game here + paused = false; + gameaction = ga_nothing; + + if (gamemode == retail) + demosequence = (demosequence + 1) % 7; + else + demosequence = (demosequence + 1) % 6; + + switch (demosequence) + { + case 0: + if (gamemode == commercial) + pagetic = 35 * 11; + else + pagetic = 170; + gamestate = GS_DEMOSCREEN; + pagename = "TITLEPIC"; + if (gamemode == commercial) + S_StartMusic(mus_dm2ttl); + else + S_StartMusic(mus_intro); + break; + case 1: + G_DeferedPlayDemo("demo1"); + break; + case 2: + pagetic = 200; + gamestate = GS_DEMOSCREEN; + pagename = "CREDIT"; + break; + case 3: + G_DeferedPlayDemo("demo2"); + break; + case 4: + gamestate = GS_DEMOSCREEN; + if (gamemode == commercial) + { + pagetic = 35 * 11; + pagename = "TITLEPIC"; + S_StartMusic(mus_dm2ttl); + } + else + { + pagetic = 200; + + if (gamemode == retail) + pagename = "CREDIT"; + else + pagename = "HELP2"; + } + break; + case 5: + G_DeferedPlayDemo("demo3"); + break; + // THE DEFINITIVE DOOM Special Edition demo + case 6: + G_DeferedPlayDemo("demo4"); + break; + } +} + + +// +// D_StartTitle +// +void D_StartTitle(void) +{ + gameaction = ga_nothing; + demosequence = -1; + D_AdvanceDemo(); +} + + +// +// D_AddFile +// +void D_AddFile(char* file) +{ + int numwadfiles; + char* newfile; + + for (numwadfiles = 0; wadfiles[numwadfiles]; numwadfiles++) + ; + + newfile = doom_malloc(doom_strlen(file) + 1); + doom_strcpy(newfile, file); + + wadfiles[numwadfiles] = newfile; +} + + +// +// IdentifyVersion +// Checks availability of IWAD files by name, +// to determine whether registered/commercial features +// should be executed (notably loading PWAD's). +// +void IdentifyVersion(void) +{ + char* doom1wad; + char* doomwad; + char* doomuwad; + char* doom2wad; + + char* doom2fwad; + char* plutoniawad; + char* tntwad; + + char* home; + char* doomwaddir; + doomwaddir = doom_getenv("DOOMWADDIR"); + if (!doomwaddir) + doomwaddir = "."; + + // Commercial. + doom2wad = doom_malloc(doom_strlen(doomwaddir) + 1 + 9 + 1); + //doom_sprintf(doom2wad, "%s/doom2.wad", doomwaddir); + doom_strcpy(doom2wad, doomwaddir); + doom_concat(doom2wad, "/doom2.wad"); + + // Retail. + doomuwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 8 + 1); + //doom_sprintf(doomuwad, "%s/doomu.wad", doomwaddir); + doom_strcpy(doomuwad, doomwaddir); + doom_concat(doomuwad, "/doomu.wad"); + + // Registered. + doomwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 8 + 1); + //doom_sprintf(doomwad, "%s/doom.wad", doomwaddir); + doom_strcpy(doomwad, doomwaddir); + doom_concat(doomwad, "/doom.wad"); + + // Shareware. + doom1wad = doom_malloc(doom_strlen(doomwaddir) + 1 + 9 + 1); + //doom_sprintf(doom1wad, "%s/doom1.wad", doomwaddir); + doom_strcpy(doom1wad, doomwaddir); + doom_concat(doom1wad, "/doom1.wad"); + + // Bug, dear Shawn. + // Insufficient malloc, caused spurious realloc errors. + plutoniawad = doom_malloc(doom_strlen(doomwaddir) + 1 +/*9*/12 + 1); + //doom_sprintf(plutoniawad, "%s/plutonia.wad", doomwaddir); + doom_strcpy(plutoniawad, doomwaddir); + doom_concat(plutoniawad, "/plutonia.wad"); + + tntwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 9 + 1); + //doom_sprintf(tntwad, "%s/tnt.wad", doomwaddir); + doom_strcpy(tntwad, doomwaddir); + doom_concat(tntwad, "/tnt.wad"); + + // French stuff. + doom2fwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 10 + 1); + //doom_sprintf(doom2fwad, "%s/doom2f.wad", doomwaddir); + doom_strcpy(doom2fwad, doomwaddir); + doom_concat(doom2fwad, "/doom2f.wad"); + +#if !defined(DOOM_WIN32) + home = doom_getenv("HOME"); + if (!home) + I_Error("Error: Please set $HOME to your home directory"); +#else + home = "."; +#endif + //doom_sprintf(basedefault, "%s/.doomrc", home); + doom_strcpy(basedefault, home); + doom_concat(basedefault, "/.doomrc"); + + if (M_CheckParm("-shdev")) + { + gamemode = shareware; + devparm = true; + D_AddFile(DEVDATA"doom1.wad"); + D_AddFile(DEVMAPS"data_se/texture1.lmp"); + D_AddFile(DEVMAPS"data_se/pnames.lmp"); + doom_strcpy(basedefault, DEVDATA"default.cfg"); + return; + } + + if (M_CheckParm("-regdev")) + { + gamemode = registered; + devparm = true; + D_AddFile(DEVDATA"doom.wad"); + D_AddFile(DEVMAPS"data_se/texture1.lmp"); + D_AddFile(DEVMAPS"data_se/texture2.lmp"); + D_AddFile(DEVMAPS"data_se/pnames.lmp"); + doom_strcpy(basedefault, DEVDATA"default.cfg"); + return; + } + + if (M_CheckParm("-comdev")) + { + gamemode = commercial; + devparm = true; + /* I don't bother + if(plutonia) + D_AddFile (DEVDATA"plutonia.wad"); + else if(tnt) + D_AddFile (DEVDATA"tnt.wad"); + else*/ + D_AddFile(DEVDATA"doom2.wad"); + + D_AddFile(DEVMAPS"cdata/texture1.lmp"); + D_AddFile(DEVMAPS"cdata/pnames.lmp"); + doom_strcpy(basedefault, DEVDATA"default.cfg"); + return; + } + + void* f; + if (f = doom_open(doom2fwad, "rb")) + { + doom_close(f); + gamemode = commercial; + // C'est ridicule! + // Let's handle languages in config files, okay? + language = french; + doom_print("French version\n"); + D_AddFile(doom2fwad); + return; + } + + if (f = doom_open(doom2wad, "rb")) + { + doom_close(f); + gamemode = commercial; + D_AddFile(doom2wad); + return; + } + + if (f = doom_open(plutoniawad, "rb")) + { + doom_close(f); + gamemode = commercial; + D_AddFile(plutoniawad); + return; + } + + if (f = doom_open(tntwad, "rb")) + { + doom_close(f); + gamemode = commercial; + D_AddFile(tntwad); + return; + } + + if (f = doom_open(doomuwad, "rb")) + { + doom_close(f); + gamemode = retail; + D_AddFile(doomuwad); + return; + } + + if (f = doom_open(doomwad, "rb")) + { + doom_close(f); + gamemode = registered; + D_AddFile(doomwad); + return; + } + + if (f = doom_open(doom1wad, "rb")) + { + doom_close(f); + gamemode = shareware; + D_AddFile(doom1wad); + return; + } + + doom_print("Game mode indeterminate.\n"); + gamemode = indetermined; +} + + +// +// Find a Response File +// +void FindResponseFile(void) +{ + int i; + + for (i = 1; i < myargc; i++) + if (myargv[i][0] == '@') + { + void* handle = 0; + int size; + int k; + int index; + int indexinfile; + char* infile; + char* file; + char* moreargs[20]; + char* firstargv; + + // READ THE RESPONSE FILE INTO MEMORY + handle = doom_open(&myargv[i][1], "rb"); + if (!handle) + { + doom_print("\nNo such response file!"); + doom_exit(1); + } + doom_print("Found response file %s!\n"); + doom_print(&myargv[i][1]); + doom_print("!\n"); + doom_seek(handle, 0, DOOM_SEEK_END); + size = doom_tell(handle); + doom_seek(handle, 0, DOOM_SEEK_SET); + file = doom_malloc(size); + doom_read(handle, file, size * 1); + doom_close(handle); + + // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG + for (index = 0, k = i + 1; k < myargc; k++) + moreargs[index++] = myargv[k]; + + firstargv = myargv[0]; + myargv = doom_malloc(sizeof(char*) * MAXARGVS); + doom_memset(myargv, 0, sizeof(char*) * MAXARGVS); + myargv[0] = firstargv; + + infile = file; + indexinfile = k = 0; + indexinfile++; // SKIP PAST ARGV[0] (KEEP IT) + do + { + myargv[indexinfile++] = infile + k; + while (k < size && + ((*(infile + k) >= ' ' + 1) && (*(infile + k) <= 'z'))) + k++; + *(infile + k) = 0; + while (k < size && + ((*(infile + k) <= ' ') || (*(infile + k) > 'z'))) + k++; + } while (k < size); + + for (k = 0; k < index; k++) + myargv[indexinfile++] = moreargs[k]; + myargc = indexinfile; + + // DISPLAY ARGS + doom_print(doom_itoa(myargc, 10)); + doom_print(" command-line args:\n"); + for (k = 1; k < myargc; k++) + { + //doom_print("%s\n", myargv[k]); + doom_print(myargv[k]); + doom_print("\n"); + } + + break; + } +} + + +// +// D_DoomMain +// +void D_DoomMain(void) +{ + int p; + char file[256]; + + FindResponseFile(); + + IdentifyVersion(); + + modifiedgame = false; + + nomonsters = M_CheckParm("-nomonsters"); + respawnparm = M_CheckParm("-respawn"); + fastparm = M_CheckParm("-fast"); + devparm = M_CheckParm("-devparm"); + if (M_CheckParm("-altdeath")) + deathmatch = 2; + else if (M_CheckParm("-deathmatch")) + deathmatch = 1; + + switch (gamemode) + { + case retail: + //doom_sprintf(title, + // " " + // "The Ultimate DOOM Startup v%i.%i" + // " ", + // VERSION / 100, VERSION % 100); + doom_strcpy(title, " " "The Ultimate DOOM Startup v"); + doom_concat(title, doom_itoa(VERSION / 100, 10)); + doom_concat(title, "."); + doom_concat(title, doom_itoa(VERSION % 100, 10)); + doom_concat(title, " "); + break; + case shareware: + //doom_sprintf(title, + // " " + // "DOOM Shareware Startup v%i.%i" + // " ", + // VERSION / 100, VERSION % 100); + doom_strcpy(title, " " "DOOM Shareware Startup v"); + doom_concat(title, doom_itoa(VERSION / 100, 10)); + doom_concat(title, "."); + doom_concat(title, doom_itoa(VERSION % 100, 10)); + doom_concat(title, " "); + break; + case registered: + //doom_sprintf(title, + // " " + // "DOOM Registered Startup v%i.%i" + // " ", + // VERSION / 100, VERSION % 100); + doom_strcpy(title, " " "DOOM Registered Startup v"); + doom_concat(title, doom_itoa(VERSION / 100, 10)); + doom_concat(title, "."); + doom_concat(title, doom_itoa(VERSION % 100, 10)); + doom_concat(title, " "); + break; + case commercial: + //doom_sprintf(title, + // " " + // "DOOM 2: Hell on Earth v%i.%i" + // " ", + // VERSION / 100, VERSION % 100); + doom_strcpy(title, " " "DOOM 2: Hell on Earth v"); + doom_concat(title, doom_itoa(VERSION / 100, 10)); + doom_concat(title, "."); + doom_concat(title, doom_itoa(VERSION % 100, 10)); + doom_concat(title, " "); + break; + /*FIXME + case pack_plut: + sprintf (title, + " " + "DOOM 2: Plutonia Experiment v%i.%i" + " ", + VERSION/100,VERSION%100); + break; + case pack_tnt: + sprintf (title, + " " + "DOOM 2: TNT - Evilution v%i.%i" + " ", + VERSION/100,VERSION%100); + break; + */ + default: + //doom_sprintf(title, + // " " + // "Public DOOM - v%i.%i" + // " ", + // VERSION / 100, VERSION % 100); + doom_strcpy(title, " " "Public DOOM - v"); + doom_concat(title, doom_itoa(VERSION / 100, 10)); + doom_concat(title, "."); + doom_concat(title, doom_itoa(VERSION % 100, 10)); + doom_concat(title, " "); + break; + } + + //doom_print("%s\n", title); + doom_print(title); + doom_print("\n"); + + if (devparm) + doom_print(D_DEVSTR); + +#if 0 // [pd] Ignore cdrom + if (M_CheckParm("-cdrom")) + { + doom_print(D_CDROM); + mkdir("c:\\doomdata", 0); + doom_strcpy(basedefault, "c:/doomdata/default.cfg"); + } +#endif + + // turbo option + if ((p = M_CheckParm("-turbo"))) + { + int scale = 200; + extern int forwardmove[2]; + extern int sidemove[2]; + + if (p < myargc - 1) + scale = doom_atoi(myargv[p + 1]); + if (scale < 10) + scale = 10; + if (scale > 400) + scale = 400; + //doom_print("turbo scale: %i%%\n", scale); + doom_print("turbo scale: "); + doom_print(doom_itoa(scale, 10)); + doom_print("%%\n"); + forwardmove[0] = forwardmove[0] * scale / 100; + forwardmove[1] = forwardmove[1] * scale / 100; + sidemove[0] = sidemove[0] * scale / 100; + sidemove[1] = sidemove[1] * scale / 100; + } + + // add any files specified on the command line with -file wadfile + // to the wad list + // + // convenience hack to allow -wart e m to add a wad file + // prepend a tilde to the filename so wadfile will be reloadable + p = M_CheckParm("-wart"); + if (p) + { + myargv[p][4] = 'p'; // big hack, change to -warp + + // Map name handling. + switch (gamemode) + { + case shareware: + case retail: + case registered: + //doom_sprintf(file, "~"DEVMAPS"E%cM%c.wad", myargv[p + 1][0], myargv[p + 2][0]); + doom_strcpy(file, "~"DEVMAPS"E"); + doom_concat(file, doom_ctoa(myargv[p + 1][0])); + doom_concat(file, "M"); + doom_concat(file, doom_ctoa(myargv[p + 2][0])); + doom_concat(file, ".wad"); + + //doom_print("Warping to Episode %s, Map %s.\n", myargv[p + 1], myargv[p + 2]); + doom_print("Warping to Episode "); + doom_print(myargv[p + 1]); + doom_print(", Map "); + doom_print(myargv[p + 2]); + doom_print(".\n"); + break; + + case commercial: + default: + p = doom_atoi(myargv[p + 1]); + if (p < 10) + { + //doom_sprintf(file, "~"DEVMAPS"cdata/map0%i.wad", p); + doom_strcpy(file, "~"DEVMAPS"cdata/map0"); + doom_concat(file, doom_itoa(p, 10)); + doom_concat(file, ".wad"); + } + else + { + //doom_sprintf(file, "~"DEVMAPS"cdata/map%i.wad", p); + doom_strcpy(file, "~"DEVMAPS"cdata/map"); + doom_concat(file, doom_itoa(p, 10)); + doom_concat(file, ".wad"); + } + break; + } + D_AddFile(file); + } + + p = M_CheckParm("-file"); + if (p) + { + // the parms after p are wadfile/lump names, + // until end of parms or another - preceded parm + modifiedgame = true; // homebrew levels + while (++p != myargc && myargv[p][0] != '-') + D_AddFile(myargv[p]); + } + + p = M_CheckParm("-playdemo"); + + if (!p) + p = M_CheckParm("-timedemo"); + + if (p && p < myargc - 1) + { + //doom_sprintf(file, "%s.lmp", myargv[p + 1]); + doom_strcpy(file, myargv[p + 1]); + doom_concat(file, ".lmp"); + D_AddFile(file); + //doom_print("Playing demo %s.lmp.\n", myargv[p + 1]); + doom_print("Playing demo "); + doom_print(myargv[p + 1]); + doom_print(".lmp.\n"); + } + + // get skill / episode / map from parms + startskill = sk_medium; + startepisode = 1; + startmap = 1; + autostart = false; + + + p = M_CheckParm("-skill"); + if (p && p < myargc - 1) + { + startskill = myargv[p + 1][0] - '1'; + autostart = true; + } + + p = M_CheckParm("-episode"); + if (p && p < myargc - 1) + { + startepisode = myargv[p + 1][0] - '0'; + startmap = 1; + autostart = true; + } + + p = M_CheckParm("-timer"); + if (p && p < myargc - 1 && deathmatch) + { + int time; + time = doom_atoi(myargv[p + 1]); + //doom_print("Levels will end after %d minute", time); + doom_print("Levels will end after "); + doom_print(doom_itoa(time, 10)); + doom_print(" minute"); + if (time > 1) + doom_print("s"); + doom_print(".\n"); + } + + p = M_CheckParm("-avg"); + if (p && p < myargc - 1 && deathmatch) + doom_print("Austin Virtual Gaming: Levels will end after 20 minutes\n"); + + p = M_CheckParm("-warp"); + if (p && p < myargc - 1) + { + if (gamemode == commercial) + startmap = doom_atoi(myargv[p + 1]); + else + { + startepisode = myargv[p + 1][0] - '0'; + startmap = myargv[p + 2][0] - '0'; + } + autostart = true; + } + + // init subsystems + doom_print("V_Init: allocate screens.\n"); + V_Init(); + + doom_print("M_LoadDefaults: Load system defaults.\n"); + M_LoadDefaults(); // load before initing other systems + + doom_print("Z_Init: Init zone memory allocation daemon. \n"); + Z_Init(); + + doom_print("W_Init: Init WADfiles.\n"); + W_InitMultipleFiles(wadfiles); + + + // Check for -file in shareware + if (modifiedgame) + { + // These are the lumps that will be checked in IWAD, + // if any one is not present, execution will be aborted. + char name[23][8] = + { + "e2m1","e2m2","e2m3","e2m4","e2m5","e2m6","e2m7","e2m8","e2m9", + "e3m1","e3m3","e3m3","e3m4","e3m5","e3m6","e3m7","e3m8","e3m9", + "dphoof","bfgga0","heada1","cybra1","spida1d1" + }; + int i; + + if (gamemode == shareware) + I_Error("Error: \nYou cannot -file with the shareware " + "version. Register!"); + + // Check for fake IWAD with right name, + // but w/o all the lumps of the registered version. + if (gamemode == registered) + for (i = 0; i < 23; i++) + if (W_CheckNumForName(name[i]) < 0) + I_Error("Error: \nThis is not the registered version."); + } + + // Iff additonal PWAD files are used, print modified banner + if (modifiedgame) + { + /*m*/doom_print( + "===========================================================================\n" + "ATTENTION: This version of DOOM has been modified. If you would like to\n" + "get a copy of the original game, call 1-800-IDGAMES or see the readme file.\n" + " You will not receive technical support for modified games.\n" + //" press enter to continue\n" + "===========================================================================\n" + ); + //getchar(); + } + + + // Check and print which version is executed. + switch (gamemode) + { + case shareware: + case indetermined: + doom_print( + "===========================================================================\n" + " Shareware!\n" + "===========================================================================\n" + ); + break; + case registered: + case retail: + case commercial: + doom_print( + "===========================================================================\n" + " Commercial product - do not distribute!\n" + " Please report software piracy to the SPA: 1-800-388-PIR8\n" + "===========================================================================\n" + ); + break; + + default: + // Ouch. + break; + } + + doom_print("M_Init: Init miscellaneous info.\n"); + M_Init(); + + doom_print("R_Init: Init DOOM refresh daemon - "); + R_Init(); + + doom_print("\nP_Init: Init Playloop state.\n"); + P_Init(); + + doom_print("I_Init: Setting up machine state.\n"); + I_Init(); + + doom_print("D_CheckNetGame: Checking network game status.\n"); + D_CheckNetGame(); + + doom_print("S_Init: Setting up sound.\n"); + S_Init(snd_SfxVolume /* *8 */, snd_MusicVolume /* *8*/); + + doom_print("HU_Init: Setting up heads up display.\n"); + HU_Init(); + + doom_print("ST_Init: Init status bar.\n"); + ST_Init(); + + // check for a driver that wants intermission stats +#if 0 // [pd] Unsure how to test this + p = M_CheckParm("-statcopy"); + if (p && p < myargc - 1) + { + // for statistics driver + extern void* statcopy; + + statcopy = (void*)atoll(myargv[p + 1]); + doom_print("External statistics registered.\n"); + } +#endif + + // start the apropriate game based on parms + p = M_CheckParm("-record"); + + if (p && p < myargc - 1) + { + G_RecordDemo(myargv[p + 1]); + autostart = true; + } + + p = M_CheckParm("-playdemo"); + if (p && p < myargc - 1) + { + singledemo = true; // quit after one demo + G_DeferedPlayDemo(myargv[p + 1]); + D_DoomLoop(); // never returns + } + + p = M_CheckParm("-timedemo"); + if (p && p < myargc - 1) + { + G_TimeDemo(myargv[p + 1]); + D_DoomLoop(); // never returns + } + + p = M_CheckParm("-loadgame"); + if (p && p < myargc - 1) + { +#if 0 // [pd] We don't support the cdrom flag + if (M_CheckParm("-cdrom")) + { + //doom_sprintf(file, "c:\\doomdata\\"SAVEGAMENAME"%c.dsg", myargv[p + 1][0]); + } + else +#endif + { + //doom_sprintf(file, SAVEGAMENAME"%c.dsg", myargv[p + 1][0]); + doom_strcpy(file, SAVEGAMENAME); + doom_concat(file, doom_ctoa(myargv[p + 1][0])); + doom_concat(file, ".dsg"); + } + G_LoadGame(file); + } + + + if (gameaction != ga_loadgame) + { + if (autostart || netgame) + G_InitNew(startskill, startepisode, startmap); + else + D_StartTitle(); // start up intro loop + + } + + // D_DoomLoop (); // never returns [ddps] Called by app + + if (demorecording) + G_BeginRecording(); + + if (M_CheckParm("-debugfile")) + { + char filename[20]; + //doom_sprintf(filename, "debug%i.txt", consoleplayer); + doom_strcpy(filename, "debug"); + doom_concat(filename, doom_itoa(consoleplayer, 10)); + doom_concat(filename, ".txt"); + //doom_print("debug output to: %s\n", filename); + doom_print("debug output to: "); + doom_print(filename); + doom_print("\n"); + debugfile = doom_open(filename, "w"); + } + + I_InitGraphics(); +} +#define NCMD_EXIT 0x80000000 +#define NCMD_RETRANSMIT 0x40000000 +#define NCMD_SETUP 0x20000000 +#define NCMD_KILL 0x10000000 // kill game +#define NCMD_CHECKSUM 0x0fffffff + +#define RESENDCOUNT 10 +#define PL_DRONE 0x80 // bit flag in doomdata->player + + +doomcom_t* doomcom; +doomdata_t* netbuffer; // points inside doomcom + +ticcmd_t localcmds[BACKUPTICS]; + +ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; +int nettics[MAXNETNODES]; +doom_boolean nodeingame[MAXNETNODES]; // set false as nodes leave game +doom_boolean remoteresend[MAXNETNODES]; // set when local needs tics +int resendto[MAXNETNODES]; // set when remote needs tics +int resendcount[MAXNETNODES]; + +int nodeforplayer[MAXPLAYERS]; + +int maketic; +int lastnettic; +int skiptics; +int ticdup; +int maxsend; // BACKUPTICS/(2*ticdup)-1 + +doom_boolean reboundpacket; +doomdata_t reboundstore; + +char exitmsg[80]; +int gametime; +int frametics[4]; +int frameon; +int frameskip[4]; +int oldnettics; + + +extern int viewangleoffset; +extern doom_boolean advancedemo; + + +void D_ProcessEvents(void); +void G_BuildTiccmd(ticcmd_t* cmd); +void D_DoAdvanceDemo(void); + + +// +// +// +int NetbufferSize(void) +{ + return (int)(long long)&(((doomdata_t*)0)->cmds[netbuffer->numtics]); +} + + +// +// Checksum +// +unsigned NetbufferChecksum(void) +{ + unsigned c; + int i, l; + + c = 0x1234567; + + // FIXME -endianess? +// #ifdef NORMALUNIX + return 0; // byte order problems +// #endif + + l = (NetbufferSize() - (int)(long long)&(((doomdata_t*)0)->retransmitfrom)) / 4; + for (i = 0; i < l; i++) + c += ((unsigned*)&netbuffer->retransmitfrom)[i] * (i + 1); + + return c & NCMD_CHECKSUM; +} + + +// +// +// +int ExpandTics(int low) +{ + int delta; + + delta = low - (maketic & 0xff); + + if (delta >= -64 && delta <= 64) + return (maketic & ~0xff) + low; + if (delta > 64) + return (maketic & ~0xff) - 256 + low; + if (delta < -64) + return (maketic & ~0xff) + 256 + low; + + //I_Error("Error: ExpandTics: strange value %i at maketic %i", low, maketic); + doom_strcpy(error_buf, "Error: ExpandTics: strange value "); + doom_concat(error_buf, doom_itoa(low, 10)); + doom_concat(error_buf, " at maketic "); + doom_concat(error_buf, doom_itoa(maketic, 10)); + I_Error(error_buf); + return 0; +} + + +// +// HSendPacket +// +void HSendPacket(int node, int flags) +{ + netbuffer->checksum = NetbufferChecksum() | flags; + + if (!node) + { + reboundstore = *netbuffer; + reboundpacket = true; + return; + } + + if (demoplayback) + return; + + if (!netgame) + I_Error("Error: Tried to transmit to another node"); + + doomcom->command = CMD_SEND; + doomcom->remotenode = node; + doomcom->datalength = NetbufferSize(); + + if (debugfile) + { + int i; + int realretrans; + if (netbuffer->checksum & NCMD_RETRANSMIT) + realretrans = ExpandTics(netbuffer->retransmitfrom); + else + realretrans = -1; + + { + //fprintf(debugfile, "send (%i + %i, R %i) [%i] ", + // ExpandTics(netbuffer->starttic), + // netbuffer->numtics, realretrans, doomcom->datalength); + doom_fprint(debugfile, "send ("); + doom_fprint(debugfile, doom_itoa(ExpandTics(netbuffer->starttic), 10)); + doom_fprint(debugfile, " + "); + doom_fprint(debugfile, doom_itoa(netbuffer->numtics, 10)); + doom_fprint(debugfile, ", R "); + doom_fprint(debugfile, doom_itoa(realretrans, 10)); + doom_fprint(debugfile, ") ["); + doom_fprint(debugfile, doom_itoa(doomcom->datalength, 10)); + doom_fprint(debugfile, "] "); + } + + for (i = 0; i < doomcom->datalength; i++) + { + //fprintf(debugfile, "%i ", ((byte*)netbuffer)[i]); + doom_fprint(debugfile, doom_itoa(((byte*)netbuffer)[i], 10)); + doom_fprint(debugfile, " "); + } + + doom_fprint(debugfile, "\n"); + } + + I_NetCmd(); +} + + +// +// HGetPacket +// Returns false if no packet is waiting +// +doom_boolean HGetPacket(void) +{ + if (reboundpacket) + { + *netbuffer = reboundstore; + doomcom->remotenode = 0; + reboundpacket = false; + return true; + } + + if (!netgame) + return false; + + if (demoplayback) + return false; + + doomcom->command = CMD_GET; + I_NetCmd(); + + if (doomcom->remotenode == -1) + return false; + + if (doomcom->datalength != NetbufferSize()) + { + if (debugfile) + { + //fprintf(debugfile, "bad packet length %i\n", doomcom->datalength); + doom_fprint(debugfile, "bad packet length "); + doom_fprint(debugfile, doom_itoa(doomcom->datalength, 10)); + doom_fprint(debugfile, "\n"); + } + return false; + } + + if (NetbufferChecksum() != (netbuffer->checksum & NCMD_CHECKSUM)) + { + if (debugfile) + { + doom_fprint(debugfile, "bad packet checksum\n"); + } + return false; + } + + if (debugfile) + { + int realretrans; + int i; + + if (netbuffer->checksum & NCMD_SETUP) + { + doom_fprint(debugfile, "setup packet\n"); + } + else + { + if (netbuffer->checksum & NCMD_RETRANSMIT) + realretrans = ExpandTics(netbuffer->retransmitfrom); + else + realretrans = -1; + + { + //fprintf(debugfile, "get %i = (%i + %i, R %i)[%i] ", + // doomcom->remotenode, + // ExpandTics(netbuffer->starttic), + // netbuffer->numtics, realretrans, doomcom->datalength); + doom_fprint(debugfile, "get "); + doom_fprint(debugfile, doom_itoa(doomcom->remotenode, 10)); + doom_fprint(debugfile, " = ("); + doom_fprint(debugfile, doom_itoa(ExpandTics(netbuffer->starttic), 10)); + doom_fprint(debugfile, " + "); + doom_fprint(debugfile, doom_itoa(netbuffer->numtics, 10)); + doom_fprint(debugfile, ", R "); + doom_fprint(debugfile, doom_itoa(realretrans, 10)); + doom_fprint(debugfile, ")["); + doom_fprint(debugfile, doom_itoa(doomcom->datalength, 10)); + doom_fprint(debugfile, "] "); + } + + for (i = 0; i < doomcom->datalength; i++) + { + //fprintf(debugfile, "%i ", ((byte*)netbuffer)[i]); + doom_fprint(debugfile, doom_itoa(((byte*)netbuffer)[i], 10)); + doom_fprint(debugfile, " "); + } + doom_fprint(debugfile, "\n"); + } + } + return true; +} + + +// +// GetPackets +// +void GetPackets(void) +{ + int netconsole; + int netnode; + ticcmd_t* src, *dest; + int realend; + int realstart; + + while (HGetPacket()) + { + if (netbuffer->checksum & NCMD_SETUP) + continue; // extra setup packet + + netconsole = netbuffer->player & ~PL_DRONE; + netnode = doomcom->remotenode; + + // to save bytes, only the low byte of tic numbers are sent + // Figure out what the rest of the bytes are + realstart = ExpandTics(netbuffer->starttic); + realend = (realstart + netbuffer->numtics); + + // check for exiting the game + if (netbuffer->checksum & NCMD_EXIT) + { + if (!nodeingame[netnode]) + continue; + nodeingame[netnode] = false; + playeringame[netconsole] = false; + doom_strcpy(exitmsg, "Player 1 left the game"); + exitmsg[7] += netconsole; + players[consoleplayer].message = exitmsg; + if (demorecording) + G_CheckDemoStatus(); + continue; + } + + // check for a remote game kill + if (netbuffer->checksum & NCMD_KILL) + I_Error("Error: Killed by network driver"); + + nodeforplayer[netconsole] = netnode; + + // check for retransmit request + if (resendcount[netnode] <= 0 + && (netbuffer->checksum & NCMD_RETRANSMIT)) + { + resendto[netnode] = ExpandTics(netbuffer->retransmitfrom); + if (debugfile) + { + //fprintf(debugfile, "retransmit from %i\n", resendto[netnode]); + doom_fprint(debugfile, "retransmit from "); + doom_fprint(debugfile, doom_itoa(resendto[netnode], 10)); + doom_fprint(debugfile, "\n"); + } + resendcount[netnode] = RESENDCOUNT; + } + else + resendcount[netnode]--; + + // check for out of order / duplicated packet + if (realend == nettics[netnode]) + continue; + + if (realend < nettics[netnode]) + { + if (debugfile) + { + //fprintf(debugfile, + // "out of order packet (%i + %i)\n", + // realstart, netbuffer->numtics); + doom_fprint(debugfile, "out of order packet ("); + doom_fprint(debugfile, doom_itoa(realstart, 10)); + doom_fprint(debugfile, " + "); + doom_fprint(debugfile, doom_itoa(netbuffer->numtics, 10)); + doom_fprint(debugfile, ")\n"); + } + continue; + } + + // check for a missed packet + if (realstart > nettics[netnode]) + { + // stop processing until the other system resends the missed tics + if (debugfile) + { + //fprintf(debugfile, + // "missed tics from %i (%i - %i)\n", + // netnode, realstart, nettics[netnode]); + doom_fprint(debugfile, "missed tics from "); + doom_fprint(debugfile, doom_itoa(netnode, 10)); + doom_fprint(debugfile, " ("); + doom_fprint(debugfile, doom_itoa(realstart, 10)); + doom_fprint(debugfile, " - "); + doom_fprint(debugfile, doom_itoa(nettics[netnode], 10)); + doom_fprint(debugfile, ")\n"); + } + remoteresend[netnode] = true; + continue; + } + + // update command store from the packet + { + int start; + + remoteresend[netnode] = false; + + start = nettics[netnode] - realstart; + src = &netbuffer->cmds[start]; + + while (nettics[netnode] < realend) + { + dest = &netcmds[netconsole][nettics[netnode] % BACKUPTICS]; + nettics[netnode]++; + *dest = *src; + src++; + } + } + } +} + + +// +// NetUpdate +// Builds ticcmds for console player, +// sends out a packet +// +void NetUpdate(void) +{ + int nowtime; + int newtics; + int i, j; + int realstart; + int gameticdiv; + + // check time + nowtime = I_GetTime() / ticdup; + newtics = nowtime - gametime; + gametime = nowtime; + + if (newtics <= 0) // nothing new to update + goto listen; + + if (skiptics <= newtics) + { + newtics -= skiptics; + skiptics = 0; + } + else + { + skiptics -= newtics; + newtics = 0; + } + + netbuffer->player = consoleplayer; + + // build new ticcmds for console player + gameticdiv = gametic / ticdup; + for (i = 0; i < newtics; i++) + { + I_StartTic(); + D_ProcessEvents(); + if (maketic - gameticdiv >= BACKUPTICS / 2 - 1) + break; // can't hold any more + + //doom_print ("mk:%i ",maketic); + G_BuildTiccmd(&localcmds[maketic % BACKUPTICS]); + maketic++; + } + + + if (singletics) + return; // singletic update is syncronous + + // send the packet to the other nodes + for (i = 0; i < doomcom->numnodes; i++) + if (nodeingame[i]) + { + netbuffer->starttic = realstart = resendto[i]; + netbuffer->numtics = maketic - realstart; + if (netbuffer->numtics > BACKUPTICS) + I_Error("Error: NetUpdate: netbuffer->numtics > BACKUPTICS"); + + resendto[i] = maketic - doomcom->extratics; + + for (j = 0; j < netbuffer->numtics; j++) + netbuffer->cmds[j] = + localcmds[(realstart + j) % BACKUPTICS]; + + if (remoteresend[i]) + { + netbuffer->retransmitfrom = nettics[i]; + HSendPacket(i, NCMD_RETRANSMIT); + } + else + { + netbuffer->retransmitfrom = 0; + HSendPacket(i, 0); + } + } + + // listen for other packets +listen: + GetPackets(); +} + + +// +// CheckAbort +// +void CheckAbort(void) +{ + event_t* ev; + int stoptic; + + stoptic = I_GetTime() + 2; + while (I_GetTime() < stoptic) + I_StartTic(); + + I_StartTic(); + for (; eventtail != eventhead + ; ) + { + ev = &events[eventtail]; + if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE) + I_Error("Error: Network game synchronization aborted."); + } + + eventtail++; + eventtail = (eventtail) & (MAXEVENTS - 1); +} + + +// +// D_ArbitrateNetStart +// +void D_ArbitrateNetStart(void) +{ + int i; + doom_boolean gotinfo[MAXNETNODES]; + + autostart = true; + doom_memset(gotinfo, 0, sizeof(gotinfo)); + + if (doomcom->consoleplayer) + { + // listen for setup info from key player + doom_print("listening for network start info...\n"); + while (1) + { + CheckAbort(); + if (!HGetPacket()) + continue; + if (netbuffer->checksum & NCMD_SETUP) + { + if (netbuffer->player != VERSION) + I_Error("Error: Different DOOM versions cannot play a net game!"); + startskill = netbuffer->retransmitfrom & 15; + deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6; + nomonsters = (netbuffer->retransmitfrom & 0x20) > 0; + respawnparm = (netbuffer->retransmitfrom & 0x10) > 0; + startmap = netbuffer->starttic & 0x3f; + startepisode = netbuffer->starttic >> 6; + return; + } + } + } + else + { + // key player, send the setup info + doom_print("sending network start info...\n"); + do + { + CheckAbort(); + for (i = 0; i < doomcom->numnodes; i++) + { + netbuffer->retransmitfrom = startskill; + if (deathmatch) + netbuffer->retransmitfrom |= (deathmatch << 6); + if (nomonsters) + netbuffer->retransmitfrom |= 0x20; + if (respawnparm) + netbuffer->retransmitfrom |= 0x10; + netbuffer->starttic = startepisode * 64 + startmap; + netbuffer->player = VERSION; + netbuffer->numtics = 0; + HSendPacket(i, NCMD_SETUP); + } + +#if 1 + for (i = 10; i && HGetPacket(); --i) + { + if ((netbuffer->player & 0x7f) < MAXNETNODES) + gotinfo[netbuffer->player & 0x7f] = true; + } +#else + while (HGetPacket()) + { + gotinfo[netbuffer->player & 0x7f] = true; + } +#endif + + for (i = 1; i < doomcom->numnodes; i++) + if (!gotinfo[i]) + break; + } while (i < doomcom->numnodes); + } +} + + +// +// D_CheckNetGame +// Works out player numbers among the net participants +// +void D_CheckNetGame(void) +{ + int i; + + for (i = 0; i < MAXNETNODES; i++) + { + nodeingame[i] = false; + nettics[i] = 0; + remoteresend[i] = false; // set when local needs tics + resendto[i] = 0; // which tic to start sending + } + + // I_InitNetwork sets doomcom and netgame + I_InitNetwork(); + if (doomcom->id != DOOMCOM_ID) + I_Error("Error: Doomcom buffer invalid!"); + + netbuffer = &doomcom->data; + consoleplayer = displayplayer = doomcom->consoleplayer; + if (netgame) + D_ArbitrateNetStart(); + + //doom_print("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", + // startskill, deathmatch, startmap, startepisode); + doom_print("startskill "); + doom_print(doom_itoa(startskill, 10)); + doom_print(" deathmatch: "); + doom_print(doom_itoa(deathmatch, 10)); + doom_print(" startmap: "); + doom_print(doom_itoa(startmap, 10)); + doom_print(" startepisode: "); + doom_print(doom_itoa(startepisode, 10)); + doom_print("\n"); + + // read values out of doomcom + ticdup = doomcom->ticdup; + maxsend = BACKUPTICS / (2 * ticdup) - 1; + if (maxsend < 1) + maxsend = 1; + + for (i = 0; i < doomcom->numplayers; i++) + playeringame[i] = true; + for (i = 0; i < doomcom->numnodes; i++) + nodeingame[i] = true; + + //doom_print("player %i of %i (%i nodes)\n", + // consoleplayer + 1, doomcom->numplayers, doomcom->numnodes); + doom_print("player "); + doom_print(doom_itoa(consoleplayer + 1, 10)); + doom_print(" of "); + doom_print(doom_itoa(doomcom->numplayers, 10)); + doom_print(" ("); + doom_print(doom_itoa(doomcom->numnodes, 10)); + doom_print(" nodes)\n"); +} + + +// +// D_QuitNetGame +// Called before quitting to leave a net game +// without hanging the other players +// +void D_QuitNetGame(void) +{ + int i, j; + + if (debugfile) + doom_close(debugfile); + + if (!netgame || !usergame || consoleplayer == -1 || demoplayback) + return; + + // send a bunch of packets for security + netbuffer->player = consoleplayer; + netbuffer->numtics = 0; + for (i = 0; i < 4; i++) + { + for (j = 1; j < doomcom->numnodes; j++) + if (nodeingame[j]) + HSendPacket(j, NCMD_EXIT); + I_WaitVBL(1); + } +} + + +// +// TryRunTics +// +void TryRunTics(void) +{ + int i; + int lowtic; + int entertic; + static int oldentertics; + int realtics; + int availabletics; + int counts; + int numplaying; + + // get real tics + entertic = I_GetTime() / ticdup; + realtics = entertic - oldentertics; + oldentertics = entertic; + + // get available tics + NetUpdate(); + + lowtic = DOOM_MAXINT; + numplaying = 0; + for (i = 0; i < doomcom->numnodes; i++) + { + if (nodeingame[i]) + { + numplaying++; + if (nettics[i] < lowtic) + lowtic = nettics[i]; + } + } + availabletics = lowtic - gametic / ticdup; + + // decide how many tics to run + if (realtics < availabletics - 1) + counts = realtics + 1; + else if (realtics < availabletics) + counts = realtics; + else + counts = availabletics; + + if (counts < 1) + counts = 1; + + frameon++; + + if (debugfile) + { + //fprintf(debugfile, + // "=======real: %i avail: %i game: %i\n", + // realtics, availabletics, counts); + doom_fprint(debugfile, "=======real: "); + doom_fprint(debugfile, doom_itoa(realtics, 10)); + doom_fprint(debugfile, " avail: "); + doom_fprint(debugfile, doom_itoa(availabletics, 10)); + doom_fprint(debugfile, " game: "); + doom_fprint(debugfile, doom_itoa(counts, 10)); + doom_fprint(debugfile, "\n"); + } + + if (!demoplayback) + { + // ideally nettics[0] should be 1 - 3 tics above lowtic + // if we are consistantly slower, speed up time + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + break; + if (consoleplayer == i) + { + // the key player does not adapt + } + else + { + if (nettics[0] <= nettics[nodeforplayer[i]]) + { + gametime--; + // doom_print ("-"); + } + frameskip[frameon & 3] = (oldnettics > nettics[nodeforplayer[i]]); + oldnettics = nettics[0]; + if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) + { + skiptics = 1; + // doom_print ("+"); + } + } + }// demoplayback + + // wait for new tics if needed + while (lowtic < gametic / ticdup + counts) + { + NetUpdate(); + lowtic = DOOM_MAXINT; + + for (i = 0; i < doomcom->numnodes; i++) + if (nodeingame[i] && nettics[i] < lowtic) + lowtic = nettics[i]; + + if (lowtic < gametic / ticdup) + I_Error("Error: TryRunTics: lowtic < gametic"); + + // don't stay in here forever -- give the menu a chance to work + if (I_GetTime() / ticdup - entertic >= 20) + { + M_Ticker(); + return; + } + } + + // run the count * ticdup dics + while (counts--) + { + for (i = 0; i < ticdup; i++) + { + if (gametic / ticdup > lowtic) + I_Error("Error: gametic>lowtic"); + if (advancedemo) + D_DoAdvanceDemo(); + M_Ticker(); + G_Ticker(); + gametic++; + + // modify command for duplicated tics + if (i != ticdup - 1) + { + ticcmd_t* cmd; + int buf; + int j; + + buf = (gametic / ticdup) % BACKUPTICS; + for (j = 0; j < MAXPLAYERS; j++) + { + cmd = &netcmds[j][buf]; + cmd->chatchar = 0; + if (cmd->buttons & BT_SPECIAL) + cmd->buttons = 0; + } + } + } + NetUpdate(); // check for new console commands + } +} +GameMode_t gamemode = indetermined; +GameMission_t gamemission = doom; + +// Language. +Language_t language = english; + +// Set if homebrew PWAD stuff has been added. +doom_boolean modifiedgame; +char* endmsg[NUM_QUITMESSAGES + 1] = +{ + // DOOM1 + QUITMSG, + "please don't leave, there's more\ndemons to toast!", + "let's beat it -- this is turning\ninto a bloodbath!", + "i wouldn't leave if i were you.\ndos is much worse.", + "you're trying to say you like dos\nbetter than me, right?", + "don't leave yet -- there's a\ndemon around that corner!", + "ya know, next time you come in here\ni'm gonna toast ya.", + "go ahead and leave. see if i care.", + + // QuitDOOM II messages + "you want to quit?\nthen, thou hast lost an eighth!", + "don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!", + "get outta here and go back\nto your boring programs.", + "if i were your boss, i'd \n deathmatch ya in a minute!", + "look, bud. you leave now\nand you forfeit your body count!", + "just leave. when you come\nback, i'll be waiting with a bat.", + "you're lucky i don't smack\nyou for thinking about leaving.", + + // FinalDOOM? + "fuck you, pussy!\nget the fuck out!", + "you quit and i'll jizz\nin your cystholes!", + "if you leave, i'll make\nthe lord drink my jizz.", + "hey, ron! can we say\n'fuck' in the game?", + "i'd leave: this is just\nmore monsters and levels.\nwhat a load.", + "suck it down, asshole!\nyou're a fucking wimp!", + "don't quit now! we're \nstill spending your money!", + + // Internal debug. Different style, too. + "THIS IS NO MESSAGE!\nPage intentionally left blank." +}; +#define TEXTSPEED 3 +#define TEXTWAIT 250 + + +typedef struct +{ + char* name; + mobjtype_t type; +} castinfo_t; + + +// Stage of animation: +// 0 = text, 1 = art screen, 2 = character cast +int finalestage; + +int finalecount; + +char* e1text = E1TEXT; +char* e2text = E2TEXT; +char* e3text = E3TEXT; +char* e4text = E4TEXT; + +char* c1text = C1TEXT; +char* c2text = C2TEXT; +char* c3text = C3TEXT; +char* c4text = C4TEXT; +char* c5text = C5TEXT; +char* c6text = C6TEXT; + +char* p1text = P1TEXT; +char* p2text = P2TEXT; +char* p3text = P3TEXT; +char* p4text = P4TEXT; +char* p5text = P5TEXT; +char* p6text = P6TEXT; + +char* t1text = T1TEXT; +char* t2text = T2TEXT; +char* t3text = T3TEXT; +char* t4text = T4TEXT; +char* t5text = T5TEXT; +char* t6text = T6TEXT; + +char* finaletext; +char* finaleflat; + +castinfo_t castorder[] = { + {CC_ZOMBIE, MT_POSSESSED}, + {CC_SHOTGUN, MT_SHOTGUY}, + {CC_HEAVY, MT_CHAINGUY}, + {CC_IMP, MT_TROOP}, + {CC_DEMON, MT_SERGEANT}, + {CC_LOST, MT_SKULL}, + {CC_CACO, MT_HEAD}, + {CC_HELL, MT_KNIGHT}, + {CC_BARON, MT_BRUISER}, + {CC_ARACH, MT_BABY}, + {CC_PAIN, MT_PAIN}, + {CC_REVEN, MT_UNDEAD}, + {CC_MANCU, MT_FATSO}, + {CC_ARCH, MT_VILE}, + {CC_SPIDER, MT_SPIDER}, + {CC_CYBER, MT_CYBORG}, + {CC_HERO, MT_PLAYER}, + + {0,0} +}; + +int castnum; +int casttics; +state_t* caststate; +doom_boolean castdeath; +int castframes; +int castonmelee; +doom_boolean castattacking; + + +// +// F_StartCast +// +extern gamestate_t wipegamestate; +extern patch_t* hu_font[HU_FONTSIZE]; + + +void F_StartCast(void); +void F_CastTicker(void); +doom_boolean F_CastResponder(event_t* ev); +void F_CastDrawer(void); +void V_DrawPatchFlipped(int x, int y, int scrn, patch_t* patch); + + +// +// F_StartFinale +// +void F_StartFinale(void) +{ + gameaction = ga_nothing; + gamestate = GS_FINALE; + viewactive = false; + automapactive = false; + + // Okay - IWAD dependend stuff. + // This has been changed severly, and + // some stuff might have changed in the process. + switch (gamemode) + { + + // DOOM 1 - E1, E3 or E4, but each nine missions + case shareware: + case registered: + case retail: + { + S_ChangeMusic(mus_victor, true); + + switch (gameepisode) + { + case 1: + finaleflat = "FLOOR4_8"; + finaletext = e1text; + break; + case 2: + finaleflat = "SFLR6_1"; + finaletext = e2text; + break; + case 3: + finaleflat = "MFLR8_4"; + finaletext = e3text; + break; + case 4: + finaleflat = "MFLR8_3"; + finaletext = e4text; + break; + default: + // Ouch. + break; + } + break; + } + + // DOOM II and missions packs with E1, M34 + case commercial: + { + S_ChangeMusic(mus_read_m, true); + + switch (gamemap) + { + case 6: + finaleflat = "SLIME16"; + finaletext = c1text; + break; + case 11: + finaleflat = "RROCK14"; + finaletext = c2text; + break; + case 20: + finaleflat = "RROCK07"; + finaletext = c3text; + break; + case 30: + finaleflat = "RROCK17"; + finaletext = c4text; + break; + case 15: + finaleflat = "RROCK13"; + finaletext = c5text; + break; + case 31: + finaleflat = "RROCK19"; + finaletext = c6text; + break; + default: + // Ouch. + break; + } + break; + } + + + // Indeterminate. + default: + S_ChangeMusic(mus_read_m, true); + finaleflat = "F_SKY1"; // Not used anywhere else. + finaletext = c1text; // FIXME - other text, music? + break; + } + + finalestage = 0; + finalecount = 0; +} + + +doom_boolean F_Responder(event_t* event) +{ + if (finalestage == 2) + return F_CastResponder(event); + + return false; +} + + +// +// F_Ticker +// +void F_Ticker(void) +{ + int i; + + // check for skipping + if ((gamemode == commercial) + && (finalecount > 50)) + { + // go on to the next level + for (i = 0; i < MAXPLAYERS; i++) + if (players[i].cmd.buttons) + break; + + if (i < MAXPLAYERS) + { + if (gamemap == 30) + F_StartCast(); + else + gameaction = ga_worlddone; + } + } + + // advance animation + finalecount++; + + if (finalestage == 2) + { + F_CastTicker(); + return; + } + + if (gamemode == commercial) + return; + + if (!finalestage && finalecount > doom_strlen(finaletext) * TEXTSPEED + TEXTWAIT) + { + finalecount = 0; + finalestage = 1; + wipegamestate = -1; // force a wipe + if (gameepisode == 3) + S_StartMusic(mus_bunny); + } +} + + +// +// F_TextWrite +// +void F_TextWrite(void) +{ + byte* src; + byte* dest; + + int x, y, w; + int count; + char* ch; + int c; + int cx; + int cy; + + // erase the entire screen to a tiled background + src = W_CacheLumpName(finaleflat, PU_CACHE); + dest = screens[0]; + + for (y = 0; y < SCREENHEIGHT; y++) + { + for (x = 0; x < SCREENWIDTH / 64; x++) + { + doom_memcpy(dest, src + ((y & 63) << 6), 64); + dest += 64; + } + if (SCREENWIDTH & 63) + { + doom_memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); + dest += (SCREENWIDTH & 63); + } + } + + V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT); + + // draw some of the text onto the screen + cx = 10; + cy = 10; + ch = finaletext; + + count = (finalecount - 10) / TEXTSPEED; + if (count < 0) + count = 0; + for (; count; count--) + { + c = *ch++; + if (!c) + break; + if (c == '\n') + { + cx = 10; + cy += 11; + continue; + } + + c = doom_toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + if (cx + w > SCREENWIDTH) + break; + V_DrawPatch(cx, cy, 0, hu_font[c]); + cx += w; + } +} + + +// +// Final DOOM 2 animation +// Casting by id Software. +// in order of appearance +// +void F_StartCast(void) +{ + wipegamestate = -1; // force a screen wipe + castnum = 0; + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + casttics = caststate->tics; + castdeath = false; + finalestage = 2; + castframes = 0; + castonmelee = 0; + castattacking = false; + S_ChangeMusic(mus_evil, true); +} + + +// +// F_CastTicker +// +void F_CastTicker(void) +{ + int st; + int sfx; + + if (--casttics > 0) + return; // not time to change state yet + + if (caststate->tics == -1 || caststate->nextstate == S_NULL) + { + // switch from deathstate to next monster + castnum++; + castdeath = false; + if (castorder[castnum].name == 0) + castnum = 0; + if (mobjinfo[castorder[castnum].type].seesound) + S_StartSound(0, mobjinfo[castorder[castnum].type].seesound); + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + castframes = 0; + } + else + { + // just advance to next state in animation + if (caststate == &states[S_PLAY_ATK1]) + goto stopattack; // Oh, gross hack! + st = caststate->nextstate; + caststate = &states[st]; + castframes++; + + // sound hacks.... + switch (st) + { + case S_PLAY_ATK1: sfx = sfx_dshtgn; break; + case S_POSS_ATK2: sfx = sfx_pistol; break; + case S_SPOS_ATK2: sfx = sfx_shotgn; break; + case S_VILE_ATK2: sfx = sfx_vilatk; break; + case S_SKEL_FIST2: sfx = sfx_skeswg; break; + case S_SKEL_FIST4: sfx = sfx_skepch; break; + case S_SKEL_MISS2: sfx = sfx_skeatk; break; + case S_FATT_ATK8: + case S_FATT_ATK5: + case S_FATT_ATK2: sfx = sfx_firsht; break; + case S_CPOS_ATK2: + case S_CPOS_ATK3: + case S_CPOS_ATK4: sfx = sfx_shotgn; break; + case S_TROO_ATK3: sfx = sfx_claw; break; + case S_SARG_ATK2: sfx = sfx_sgtatk; break; + case S_BOSS_ATK2: + case S_BOS2_ATK2: + case S_HEAD_ATK2: sfx = sfx_firsht; break; + case S_SKULL_ATK2: sfx = sfx_sklatk; break; + case S_SPID_ATK2: + case S_SPID_ATK3: sfx = sfx_shotgn; break; + case S_BSPI_ATK2: sfx = sfx_plasma; break; + case S_CYBER_ATK2: + case S_CYBER_ATK4: + case S_CYBER_ATK6: sfx = sfx_rlaunc; break; + case S_PAIN_ATK3: sfx = sfx_sklatk; break; + default: sfx = 0; break; + } + + if (sfx) + S_StartSound(0, sfx); + } + + if (castframes == 12) + { + // go into attack frame + castattacking = true; + if (castonmelee) + caststate = &states[mobjinfo[castorder[castnum].type].meleestate]; + else + caststate = &states[mobjinfo[castorder[castnum].type].missilestate]; + castonmelee ^= 1; + if (caststate == &states[S_NULL]) + { + if (castonmelee) + caststate = + &states[mobjinfo[castorder[castnum].type].meleestate]; + else + caststate = + &states[mobjinfo[castorder[castnum].type].missilestate]; + } + } + + if (castattacking) + { + if (castframes == 24 + || caststate == &states[mobjinfo[castorder[castnum].type].seestate]) + { +stopattack: + castattacking = false; + castframes = 0; + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + } + } + + casttics = caststate->tics; + if (casttics == -1) + casttics = 15; +} + + +// +// F_CastResponder +// +doom_boolean F_CastResponder(event_t* ev) +{ + if (ev->type != ev_keydown) + return false; + + if (castdeath) + return true; // already in dying frames + + // go into death frame + castdeath = true; + caststate = &states[mobjinfo[castorder[castnum].type].deathstate]; + casttics = caststate->tics; + castframes = 0; + castattacking = false; + if (mobjinfo[castorder[castnum].type].deathsound) + S_StartSound(0, mobjinfo[castorder[castnum].type].deathsound); + + return true; +} + + +void F_CastPrint(char* text) +{ + char* ch; + int c; + int cx; + int w; + int width; + + // find width + ch = text; + width = 0; + + while (ch) + { + c = *ch++; + if (!c) + break; + c = doom_toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + width += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + width += w; + } + + // draw it + cx = 160 - width / 2; + ch = text; + while (ch) + { + c = *ch++; + if (!c) + break; + c = doom_toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + V_DrawPatch(cx, 180, 0, hu_font[c]); + cx += w; + } +} + + +// +// F_CastDrawer +// +void F_CastDrawer(void) +{ + spritedef_t* sprdef; + spriteframe_t* sprframe; + int lump; + doom_boolean flip; + patch_t* patch; + + // erase the entire screen to a background + V_DrawPatch(0, 0, 0, W_CacheLumpName("BOSSBACK", PU_CACHE)); + + F_CastPrint(castorder[castnum].name); + + // draw the current frame in the middle of the screen + sprdef = &sprites[caststate->sprite]; + sprframe = &sprdef->spriteframes[caststate->frame & FF_FRAMEMASK]; + lump = sprframe->lump[0]; + flip = (doom_boolean)sprframe->flip[0]; + + patch = W_CacheLumpNum(lump + firstspritelump, PU_CACHE); + if (flip) + V_DrawPatchFlipped(160, 170, 0, patch); + else + V_DrawPatch(160, 170, 0, patch); +} + + +// +// F_DrawPatchCol +// +void F_DrawPatchCol(int x, patch_t* patch, int col) +{ + column_t* column; + byte* source; + byte* dest; + byte* desttop; + int count; + + column = (column_t*)((byte*)patch + LONG(patch->columnofs[col])); + desttop = screens[0] + x; + + // step through the posts in a column + while (column->topdelta != 0xff) + { + source = (byte*)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + column = (column_t*)((byte*)column + column->length + 4); + } +} + + +// +// F_BunnyScroll +// +void F_BunnyScroll(void) +{ + int scrolled; + int x; + patch_t* p1; + patch_t* p2; + char name[10]; + int stage; + static int laststage; + + p1 = W_CacheLumpName("PFUB2", PU_LEVEL); + p2 = W_CacheLumpName("PFUB1", PU_LEVEL); + + V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT); + + scrolled = 320 - (finalecount - 230) / 2; + if (scrolled > 320) + scrolled = 320; + if (scrolled < 0) + scrolled = 0; + + for (x = 0; x < SCREENWIDTH; x++) + { + if (x + scrolled < 320) + F_DrawPatchCol(x, p1, x + scrolled); + else + F_DrawPatchCol(x, p2, x + scrolled - 320); + } + + if (finalecount < 1130) + return; + if (finalecount < 1180) + { + V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, + (SCREENHEIGHT - 8 * 8) / 2, 0, W_CacheLumpName("END0", PU_CACHE)); + laststage = 0; + return; + } + + stage = (finalecount - 1180) / 5; + if (stage > 6) + stage = 6; + if (stage > laststage) + { + S_StartSound(0, sfx_pistol); + laststage = stage; + } + + //doom_sprintf(name, "END%i", stage); + doom_strcpy(name, "END"); + doom_concat(name, doom_itoa(stage, 10)); + V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2, 0, W_CacheLumpName(name, PU_CACHE)); +} + + +// +// F_Drawer +// +void F_Drawer(void) +{ + if (finalestage == 2) + { + F_CastDrawer(); + return; + } + + if (!finalestage) + F_TextWrite(); + else + { + switch (gameepisode) + { + case 1: + if (gamemode == retail) + V_DrawPatch(0, 0, 0, + W_CacheLumpName("CREDIT", PU_CACHE)); + else + V_DrawPatch(0, 0, 0, + W_CacheLumpName("HELP2", PU_CACHE)); + break; + case 2: + V_DrawPatch(0, 0, 0, + W_CacheLumpName("VICTORY2", PU_CACHE)); + break; + case 3: + F_BunnyScroll(); + break; + case 4: + V_DrawPatch(0, 0, 0, + W_CacheLumpName("ENDPIC", PU_CACHE)); + break; + } + } +} +static doom_boolean go = 0; + +static byte* wipe_scr_start; +static byte* wipe_scr_end; +static byte* wipe_scr; + +static int* y; + + +void wipe_shittyColMajorXform(short* array, int width, int height) +{ + int x; + int y; + short* dest; + + dest = (short*)Z_Malloc(width * height * sizeof(short), PU_STATIC, 0); + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + dest[x * height + y] = array[y * width + x]; + + doom_memcpy(array, dest, width * height * 2); + + Z_Free(dest); +} + + +int wipe_initColorXForm(int width, int height, int ticks) +{ + doom_memcpy(wipe_scr, wipe_scr_start, width * height); + return 0; +} + + +int wipe_doColorXForm(int width, int height, int ticks) +{ + doom_boolean changed; + byte* w; + byte* e; + int newval; + + changed = false; + w = wipe_scr; + e = wipe_scr_end; + + while (w != wipe_scr + width * height) + { + if (*w != *e) + { + if (*w > *e) + { + newval = *w - ticks; + if (newval < *e) + *w = *e; + else + *w = newval; + changed = true; + } + else if (*w < *e) + { + newval = *w + ticks; + if (newval > *e) + *w = *e; + else + *w = newval; + changed = true; + } + } + w++; + e++; + } + + return !changed; +} + + +int wipe_exitColorXForm(int width, int height, int ticks) +{ + return 0; +} + + +int wipe_initMelt(int width, int height, int ticks) +{ + int i, r; + + // copy start screen to main screen + doom_memcpy(wipe_scr, wipe_scr_start, width * height); + + // makes this wipe faster (in theory) + // to have stuff in column-major format + wipe_shittyColMajorXform((short*)wipe_scr_start, width / 2, height); + wipe_shittyColMajorXform((short*)wipe_scr_end, width / 2, height); + + // setup initial column positions + // (y<0 => not ready to scroll yet) + y = (int*)Z_Malloc(width * sizeof(int), PU_STATIC, 0); + y[0] = -(M_Random() % 16); + for (i = 1; i < width; i++) + { + r = (M_Random() % 3) - 1; + y[i] = y[i - 1] + r; + if (y[i] > 0) y[i] = 0; + else if (y[i] == -16) y[i] = -15; + } + + return 0; +} + + +int wipe_doMelt(int width, int height, int ticks) +{ + int i; + int j; + int dy; + int idx; + + short* s; + short* d; + doom_boolean done = true; + + width /= 2; + + while (ticks--) + { + for (i = 0; i < width; i++) + { + if (y[i] < 0) + { + y[i]++; done = false; + } + else if (y[i] < height) + { + dy = (y[i] < 16) ? y[i] + 1 : 8; + if (y[i] + dy >= height) dy = height - y[i]; + s = &((short*)wipe_scr_end)[i * height + y[i]]; + d = &((short*)wipe_scr)[y[i] * width + i]; + idx = 0; + for (j = dy; j; j--) + { + d[idx] = *(s++); + idx += width; + } + y[i] += dy; + s = &((short*)wipe_scr_start)[i * height]; + d = &((short*)wipe_scr)[y[i] * width + i]; + idx = 0; + for (j = height - y[i]; j; j--) + { + d[idx] = *(s++); + idx += width; + } + done = false; + } + } + } + + return done; +} + + +int wipe_exitMelt(int width, int height, int ticks) +{ + Z_Free(y); + return 0; +} + + +int wipe_StartScreen(int x, int y, int width, int height) +{ + wipe_scr_start = screens[2]; + I_ReadScreen(wipe_scr_start); + return 0; +} + + +int wipe_EndScreen(int x, int y, int width, int height) +{ + wipe_scr_end = screens[3]; + I_ReadScreen(wipe_scr_end); + V_DrawBlock(x, y, 0, width, height, wipe_scr_start); // restore start scr. + return 0; +} + + +int wipe_ScreenWipe(int wipeno, int x, int y, int width, int height, int ticks) +{ + int rc; + static int (*wipes[])(int, int, int) = + { + wipe_initColorXForm, wipe_doColorXForm, wipe_exitColorXForm, + wipe_initMelt, wipe_doMelt, wipe_exitMelt + }; + + void V_MarkRect(int, int, int, int); + + // initial stuff + if (!go) + { + go = 1; + // wipe_scr = (byte *) Z_Malloc(width*height, PU_STATIC, 0); // DEBUG + wipe_scr = screens[0]; + (*wipes[wipeno * 3])(width, height, ticks); + } + + // do a piece of wipe-in + V_MarkRect(0, 0, width, height); + rc = (*wipes[wipeno * 3 + 1])(width, height, ticks); + // V_DrawBlock(x, y, 0, width, height, wipe_scr); // DEBUG + + // final stuff + if (rc) + { + go = 0; + (*wipes[wipeno * 3 + 2])(width, height, ticks); + } + + return !go; +} +#define SAVEGAMESIZE 0x2c000 +#define SAVESTRINGSIZE 24 +#define MAXPLMOVE (forwardmove[1]) +#define TURBOTHRESHOLD 0x32 +#define SLOWTURNTICS 6 +#define NUMKEYS 256 +#define BODYQUESIZE 32 +#define VERSIONSIZE 16 +#define DEMOMARKER 0x80 + + +// Prototypes +doom_boolean G_CheckDemoStatus(void); +void G_ReadDemoTiccmd(ticcmd_t* cmd); +void G_WriteDemoTiccmd(ticcmd_t* cmd); +void G_PlayerReborn(int player); +void G_InitNew(skill_t skill, int episode, int map); +void G_DoReborn(int playernum); +void G_DoLoadLevel(void); +void G_DoNewGame(void); +void G_DoLoadGame(void); +void G_DoPlayDemo(void); +void G_DoCompleted(void); +void G_DoWorldDone(void); +void G_DoSaveGame(void); +void P_SpawnPlayer(mapthing_t* mthing); +void R_ExecuteSetViewSize(void); + + +gameaction_t gameaction; +gamestate_t gamestate; +skill_t gameskill; +doom_boolean respawnmonsters; +int gameepisode; +int gamemap; + +doom_boolean paused; +doom_boolean sendpause; // send a pause event next tic +doom_boolean sendsave; // send a save event next tic +doom_boolean usergame; // ok to save / end game + +doom_boolean timingdemo; // if true, exit with report on completion +doom_boolean nodrawers; // for comparative timing purposes +doom_boolean noblit; // for comparative timing purposes +int starttime; // for comparative timing purposes + +doom_boolean viewactive; + +doom_boolean deathmatch; // only if started as net death +doom_boolean netgame; // only true if packets are broadcast +doom_boolean playeringame[MAXPLAYERS]; +player_t players[MAXPLAYERS]; + +int consoleplayer; // player taking events and displaying +int displayplayer; // view being displayed +int gametic; +int levelstarttic; // gametic at level start +int totalkills, totalitems, totalsecret; // for intermission + +char demoname[32]; +doom_boolean demorecording; +doom_boolean demoplayback; +doom_boolean netdemo; +byte* demobuffer; +byte* demo_p; +byte* demoend; +doom_boolean singledemo; // quit after playing a demo from cmdline + +doom_boolean precache = true; // if true, load all graphics at start + +wbstartstruct_t wminfo; // parms for world map / intermission + +short consistancy[MAXPLAYERS][BACKUPTICS]; + +byte* savebuffer; + +// +// controls (have defaults) +// +int key_right; +int key_left; + +int key_up; +int key_down; +int key_strafeleft; +int key_straferight; +int key_fire; +int key_use; +int key_strafe; +int key_speed; + +int mousebfire; +int mousebstrafe; +int mousebforward; +int mousemove; + +int joybfire; +int joybstrafe; +int joybuse; +int joybspeed; + +fixed_t forwardmove[2] = { 0x19, 0x32 }; +fixed_t sidemove[2] = { 0x18, 0x28 }; +fixed_t angleturn[3] = { 640, 1280, 320 }; // + slow turn + +doom_boolean gamekeydown[NUMKEYS]; +int turnheld; // for accelerative turning + +doom_boolean mousearray[4]; +doom_boolean* mousebuttons = &mousearray[1]; // allow [-1] + +// mouse values are used once +int mousex; +int mousey; + +int dclicktime; +int dclickstate; +int dclicks; +int dclicktime2; +int dclickstate2; +int dclicks2; + +// joystick values are repeated +int joyxmove; +int joyymove; +doom_boolean joyarray[5]; +doom_boolean* joybuttons = &joyarray[1]; // allow [-1] + +int savegameslot; +char savedescription[32]; + +mobj_t* bodyque[BODYQUESIZE]; +int bodyqueslot; + +void* statcopy; // for statistics driver + +// DOOM Par Times +int pars[4][10] = +{ + {0}, + {0,30,75,120,90,165,180,180,30,165}, + {0,90,90,90,120,90,360,240,30,170}, + {0,90,45,90,150,90,90,165,30,135} +}; + +// DOOM II Par Times +int cpars[32] = +{ + 30,90,120,120,90,150,120,120,270,90, // 1-10 + 210,150,150,150,210,150,420,150,210,150, // 11-20 + 240,150,180,150,150,300,330,420,300,180, // 21-30 + 120,30 // 31-32 +}; + +doom_boolean secretexit; + +char savename[256]; + +skill_t d_skill; +int d_episode; +int d_map; + +char* defdemoname; + + +extern gamestate_t wipegamestate; +extern char* pagename; +extern doom_boolean setsizeneeded; + +// The sky texture to be used instead of the F_SKY1 dummy. +extern int skytexture; + + +int G_CmdChecksum(ticcmd_t* cmd) +{ + int i; + int sum = 0; + + for (i = 0; i < sizeof(*cmd) / 4 - 1; i++) + sum += ((int*)cmd)[i]; + + return sum; +} + + +// +// G_BuildTiccmd +// Builds a ticcmd from all of the available inputs +// or reads it from the demo buffer. +// If recording a demo, write it out +// +void G_BuildTiccmd(ticcmd_t* cmd) +{ + int i; + doom_boolean strafe; + doom_boolean bstrafe; + int speed; + int tspeed; + int forward; + int side; + + ticcmd_t* base; + + base = I_BaseTiccmd(); // empty, or external driver + doom_memcpy(cmd, base, sizeof(*cmd)); + + cmd->consistancy = + consistancy[consoleplayer][maketic % BACKUPTICS]; + + + strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] + || joybuttons[joybstrafe]; + + extern int always_run; + doom_boolean running = always_run ? (gamekeydown[key_speed] ? false : true) : (gamekeydown[key_speed] ? true : false); + speed = running || joybuttons[joybspeed]; + + forward = side = 0; + + // use two stage accelerative turning + // on the keyboard and joystick + if (joyxmove < 0 + || joyxmove > 0 + || gamekeydown[key_right] + || gamekeydown[key_left]) + turnheld += ticdup; + else + turnheld = 0; + + if (turnheld < SLOWTURNTICS) + tspeed = 2; // slow turn + else + tspeed = speed; + + // let movement keys cancel each other out + if (strafe) + { + if (gamekeydown[key_right]) + { + side += sidemove[speed]; + } + if (gamekeydown[key_left]) + { + side -= sidemove[speed]; + } + if (joyxmove > 0) + side += sidemove[speed]; + if (joyxmove < 0) + side -= sidemove[speed]; + + } + else + { + if (gamekeydown[key_right]) + cmd->angleturn -= angleturn[tspeed]; + if (gamekeydown[key_left]) + cmd->angleturn += angleturn[tspeed]; + if (joyxmove > 0) + cmd->angleturn -= angleturn[tspeed]; + if (joyxmove < 0) + cmd->angleturn += angleturn[tspeed]; + } + + if (gamekeydown[key_up]) + { + forward += forwardmove[speed]; + } + if (gamekeydown[key_down]) + { + forward -= forwardmove[speed]; + } + if (joyymove < 0) + forward += forwardmove[speed]; + if (joyymove > 0) + forward -= forwardmove[speed]; + if (gamekeydown[key_straferight]) + side += sidemove[speed]; + if (gamekeydown[key_strafeleft]) + side -= sidemove[speed]; + + // buttons + cmd->chatchar = HU_dequeueChatChar(); + + if (gamekeydown[key_fire] || mousebuttons[mousebfire] + || joybuttons[joybfire]) + cmd->buttons |= BT_ATTACK; + + if (gamekeydown[key_use] || joybuttons[joybuse]) + { + cmd->buttons |= BT_USE; + // clear double clicks if hit use button + dclicks = 0; + } + + // chainsaw overrides + for (i = 0; i < NUMWEAPONS - 1; i++) + if (gamekeydown['1' + i]) + { + cmd->buttons |= BT_CHANGE; + cmd->buttons |= i << BT_WEAPONSHIFT; + break; + } + + // mouse + if (mousebuttons[mousebforward]) + forward += forwardmove[speed]; + + // forward double click + if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1) + { + dclickstate = mousebuttons[mousebforward]; + if (dclickstate) + dclicks++; + if (dclicks == 2) + { + cmd->buttons |= BT_USE; + dclicks = 0; + } + else + dclicktime = 0; + } + else + { + dclicktime += ticdup; + if (dclicktime > 20) + { + dclicks = 0; + dclickstate = 0; + } + } + + // strafe double click + bstrafe = + mousebuttons[mousebstrafe] + || joybuttons[joybstrafe]; + if (bstrafe != dclickstate2 && dclicktime2 > 1) + { + dclickstate2 = bstrafe; + if (dclickstate2) + dclicks2++; + if (dclicks2 == 2) + { + cmd->buttons |= BT_USE; + dclicks2 = 0; + } + else + dclicktime2 = 0; + } + else + { + dclicktime2 += ticdup; + if (dclicktime2 > 20) + { + dclicks2 = 0; + dclickstate2 = 0; + } + } + + if (mousemove) + forward += mousey; + if (strafe) + side += mousex * 2; + else + cmd->angleturn -= mousex * 0x8; + + mousex = mousey = 0; + + if (forward > MAXPLMOVE) + forward = MAXPLMOVE; + else if (forward < -MAXPLMOVE) + forward = -MAXPLMOVE; + if (side > MAXPLMOVE) + side = MAXPLMOVE; + else if (side < -MAXPLMOVE) + side = -MAXPLMOVE; + + cmd->forwardmove += forward; + cmd->sidemove += side; + + // special buttons + if (sendpause) + { + sendpause = false; + cmd->buttons = BT_SPECIAL | BTS_PAUSE; + } + + if (sendsave) + { + sendsave = false; + cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot << BTS_SAVESHIFT); + } +} + + +// +// G_DoLoadLevel +// +void G_DoLoadLevel(void) +{ + int i; + + // Set the sky map. + // First thing, we have a dummy sky texture name, + // a flat. The data is in the WAD only because + // we look for an actual index, instead of simply + // setting one. + skyflatnum = R_FlatNumForName(SKYFLATNAME); + + // DOOM determines the sky texture to be used + // depending on the current episode, and the game version. + if ((gamemode == commercial) + || (gamemode == pack_tnt) + || (gamemode == pack_plut)) + { + skytexture = R_TextureNumForName("SKY3"); + if (gamemap < 12) + skytexture = R_TextureNumForName("SKY1"); + else + if (gamemap < 21) + skytexture = R_TextureNumForName("SKY2"); + } + + levelstarttic = gametic; // for time calculation + + if (wipegamestate == GS_LEVEL) + wipegamestate = -1; // force a wipe + + gamestate = GS_LEVEL; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].playerstate == PST_DEAD) + players[i].playerstate = PST_REBORN; + doom_memset(players[i].frags, 0, sizeof(players[i].frags)); + } + + P_SetupLevel(gameepisode, gamemap, 0, gameskill); + displayplayer = consoleplayer; // view the guy you are playing + starttime = I_GetTime(); + gameaction = ga_nothing; + Z_CheckHeap(); + + // clear cmd building stuff + doom_memset(gamekeydown, 0, sizeof(gamekeydown)); + joyxmove = joyymove = 0; + mousex = mousey = 0; + sendpause = sendsave = paused = false; + doom_memset(mousebuttons, 0, sizeof(*mousebuttons) * 3); + doom_memset(joybuttons, 0, sizeof(*joybuttons) * 4); +} + + +// +// G_Responder +// Get info needed to make ticcmd_ts for the players. +// +doom_boolean G_Responder(event_t* ev) +{ + // allow spy mode changes even during the demo + if (gamestate == GS_LEVEL && ev->type == ev_keydown + && ev->data1 == KEY_F12 && (singledemo || !deathmatch)) + { + // spy mode + do + { + displayplayer++; + if (displayplayer == MAXPLAYERS) + displayplayer = 0; + } while (!playeringame[displayplayer] && displayplayer != consoleplayer); + return true; + } + + // any other key pops up menu if in demos + if (gameaction == ga_nothing && !singledemo && + (demoplayback || gamestate == GS_DEMOSCREEN) + ) + { + if (ev->type == ev_keydown || + (ev->type == ev_mouse && ev->data1) || + (ev->type == ev_joystick && ev->data1)) + { + M_StartControlPanel(); + return true; + } + return false; + } + + if (gamestate == GS_LEVEL) + { +#if 0 + if (devparm && ev->type == ev_keydown && ev->data1 == ';') + { + G_DeathMatchSpawnPlayer(0); + return true; + } +#endif + if (HU_Responder(ev)) + return true; // chat ate the event + if (ST_Responder(ev)) + return true; // status window ate it + if (AM_Responder(ev)) + return true; // automap ate it + } + + if (gamestate == GS_FINALE) + { + if (F_Responder(ev)) + return true; // finale ate the event + } + + switch (ev->type) + { + case ev_keydown: + if (ev->data1 == KEY_PAUSE) + { + sendpause = true; + return true; + } + if (ev->data1 < NUMKEYS) + gamekeydown[ev->data1] = true; + return true; // eat key down events + + case ev_keyup: + if (ev->data1 < NUMKEYS) + gamekeydown[ev->data1] = false; + return false; // always let key up events filter down + + case ev_mouse: + mousebuttons[0] = ev->data1 & 1; + mousebuttons[1] = ev->data1 & 2; + mousebuttons[2] = ev->data1 & 4; + mousex = ev->data2 * (mouseSensitivity + 5) / 10; + mousey = ev->data3 * (mouseSensitivity + 5) / 10; + return true; // eat events + + case ev_joystick: + joybuttons[0] = ev->data1 & 1; + joybuttons[1] = ev->data1 & 2; + joybuttons[2] = ev->data1 & 4; + joybuttons[3] = ev->data1 & 8; + joyxmove = ev->data2; + joyymove = ev->data3; + return true; // eat events + + default: + break; + } + + return false; +} + + + +// +// G_Ticker +// Make ticcmd_ts for the players. +// +void G_Ticker(void) +{ + int i; + int buf; + ticcmd_t* cmd; + + // do player reborns if needed + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].playerstate == PST_REBORN) + G_DoReborn(i); + + // do things to change the game state + while (gameaction != ga_nothing) + { + switch (gameaction) + { + case ga_loadlevel: + G_DoLoadLevel(); + break; + case ga_newgame: + G_DoNewGame(); + break; + case ga_loadgame: + G_DoLoadGame(); + break; + case ga_savegame: + G_DoSaveGame(); + break; + case ga_playdemo: + G_DoPlayDemo(); + break; + case ga_completed: + G_DoCompleted(); + break; + case ga_victory: + F_StartFinale(); + break; + case ga_worlddone: + G_DoWorldDone(); + break; + case ga_screenshot: + M_ScreenShot(); + gameaction = ga_nothing; + break; + case ga_nothing: + break; + } + } + + // get commands, check consistancy, + // and build new consistancy check + buf = (gametic / ticdup) % BACKUPTICS; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + cmd = &players[i].cmd; + + doom_memcpy(cmd, &netcmds[i][buf], sizeof(ticcmd_t)); + + if (demoplayback) + G_ReadDemoTiccmd(cmd); + if (demorecording) + G_WriteDemoTiccmd(cmd); + + // check for turbo cheats + if (cmd->forwardmove > TURBOTHRESHOLD + && !(gametic & 31) && ((gametic >> 5) & 3) == i) + { + static char turbomessage[80]; + extern char* player_names[4]; + //doom_sprintf(turbomessage, "%s is turbo!", player_names[i]); + doom_strcpy(turbomessage, player_names[i]); + doom_concat(turbomessage, " is turbo!"); + players[consoleplayer].message = turbomessage; + } + + if (netgame && !netdemo && !(gametic % ticdup)) + { + if (gametic > BACKUPTICS + && consistancy[i][buf] != cmd->consistancy) + { + //I_Error("Error: consistency failure (%i should be %i)", + // cmd->consistancy, consistancy[i][buf]); + + doom_strcpy(error_buf, "Error: consistency failure ("); + doom_concat(error_buf, doom_itoa(cmd->consistancy, 10)); + doom_concat(error_buf, " should be "); + doom_concat(error_buf, doom_itoa(consistancy[i][buf], 10)); + doom_concat(error_buf, ")"); + I_Error(error_buf); + } + if (players[i].mo) + consistancy[i][buf] = players[i].mo->x; + else + consistancy[i][buf] = rndindex; + } + } + } + + // check for special buttons + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (players[i].cmd.buttons & BT_SPECIAL) + { + switch (players[i].cmd.buttons & BT_SPECIALMASK) + { + case BTS_PAUSE: + paused ^= 1; + if (paused) + S_PauseSound(); + else + S_ResumeSound(); + break; + + case BTS_SAVEGAME: + if (!savedescription[0]) + doom_strcpy(savedescription, "NET GAME"); + savegameslot = + (players[i].cmd.buttons & BTS_SAVEMASK) >> BTS_SAVESHIFT; + gameaction = ga_savegame; + break; + } + } + } + } + + // do main actions + switch (gamestate) + { + case GS_LEVEL: + P_Ticker(); + ST_Ticker(); + AM_Ticker(); + HU_Ticker(); + break; + + case GS_INTERMISSION: + WI_Ticker(); + break; + + case GS_FINALE: + F_Ticker(); + break; + + case GS_DEMOSCREEN: + D_PageTicker(); + break; + } +} + + +// +// PLAYER STRUCTURE FUNCTIONS +// also see P_SpawnPlayer in P_Things +// + +// +// G_InitPlayer +// Called at the start. +// Called by the game initialization functions. +// +void G_InitPlayer(int player) +{ + player_t* p; + + // set up the saved info + p = &players[player]; + + // clear everything else to defaults + G_PlayerReborn(player); +} + + +// +// G_PlayerFinishLevel +// Can when a player completes a level. +// +void G_PlayerFinishLevel(int player) +{ + player_t* p; + + p = &players[player]; + + doom_memset(p->powers, 0, sizeof(p->powers)); + doom_memset(p->cards, 0, sizeof(p->cards)); + p->mo->flags &= ~MF_SHADOW; // cancel invisibility + p->extralight = 0; // cancel gun flashes + p->fixedcolormap = 0; // cancel ir gogles + p->damagecount = 0; // no palette changes + p->bonuscount = 0; +} + + +// +// G_PlayerReborn +// Called after a player dies +// almost everything is cleared and initialized +// +void G_PlayerReborn(int player) +{ + player_t* p; + int i; + int frags[MAXPLAYERS]; + int killcount; + int itemcount; + int secretcount; + + doom_memcpy(frags, players[player].frags, sizeof(frags)); + killcount = players[player].killcount; + itemcount = players[player].itemcount; + secretcount = players[player].secretcount; + + p = &players[player]; + doom_memset(p, 0, sizeof(*p)); + + doom_memcpy(players[player].frags, frags, sizeof(players[player].frags)); + players[player].killcount = killcount; + players[player].itemcount = itemcount; + players[player].secretcount = secretcount; + + p->usedown = p->attackdown = true; // don't do anything immediately + p->playerstate = PST_LIVE; + p->health = MAXHEALTH; + p->readyweapon = p->pendingweapon = wp_pistol; + p->weaponowned[wp_fist] = true; + p->weaponowned[wp_pistol] = true; + p->ammo[am_clip] = 50; + + for (i = 0; i < NUMAMMO; i++) + p->maxammo[i] = maxammo[i]; +} + +// +// G_CheckSpot +// Returns false if the player cannot be respawned +// at the given mapthing_t spot +// because something is occupying it +// + +doom_boolean G_CheckSpot(int playernum, mapthing_t* mthing) +{ + fixed_t x; + fixed_t y; + subsector_t* ss; + unsigned an; + mobj_t* mo; + int i; + + if (!players[playernum].mo) + { + // first spawn of level, before corpses + for (i = 0; i < playernum; i++) + if (players[i].mo->x == mthing->x << FRACBITS + && players[i].mo->y == mthing->y << FRACBITS) + return false; + return true; + } + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + if (!P_CheckPosition(players[playernum].mo, x, y)) + return false; + + // flush an old corpse if needed + if (bodyqueslot >= BODYQUESIZE) + P_RemoveMobj(bodyque[bodyqueslot % BODYQUESIZE]); + bodyque[bodyqueslot % BODYQUESIZE] = players[playernum].mo; + bodyqueslot++; + + // spawn a teleport fog + ss = R_PointInSubsector(x, y); + an = (ANG45 * (mthing->angle / 45)) >> ANGLETOFINESHIFT; + + mo = P_SpawnMobj(x + 20 * finecosine[an], y + 20 * finesine[an] + , ss->sector->floorheight + , MT_TFOG); + + if (players[consoleplayer].viewz != 1) + S_StartSound(mo, sfx_telept); // don't start sound on first frame + + return true; +} + + +// +// G_DeathMatchSpawnPlayer +// Spawns a player at one of the random death match spots +// called at level load and each death +// +void G_DeathMatchSpawnPlayer(int playernum) +{ + int i, j; + int selections; + + selections = (int)(deathmatch_p - deathmatchstarts); + if (selections < 4) + { + //I_Error("Error: Only %i deathmatch spots, 4 required", selections); + + doom_strcpy(error_buf, "Error: Only "); + doom_concat(error_buf, doom_itoa(selections, 10)); + doom_concat(error_buf, " deathmatch spots, 4 required"); + I_Error(error_buf); + } + + for (j = 0; j < 20; j++) + { + i = P_Random() % selections; + if (G_CheckSpot(playernum, &deathmatchstarts[i])) + { + deathmatchstarts[i].type = playernum + 1; + P_SpawnPlayer(&deathmatchstarts[i]); + return; + } + } + + // no good spot, so the player will probably get stuck + P_SpawnPlayer(&playerstarts[playernum]); +} + +// +// G_DoReborn +// +void G_DoReborn(int playernum) +{ + int i; + + if (!netgame) + { + // reload the level from scratch + gameaction = ga_loadlevel; + } + else + { + // respawn at the start + + // first dissasociate the corpse + players[playernum].mo->player = 0; + + // spawn at random spot if in death match + if (deathmatch) + { + G_DeathMatchSpawnPlayer(playernum); + return; + } + + if (G_CheckSpot(playernum, &playerstarts[playernum])) + { + P_SpawnPlayer(&playerstarts[playernum]); + return; + } + + // try to spawn at one of the other players spots + for (i = 0; i < MAXPLAYERS; i++) + { + if (G_CheckSpot(playernum, &playerstarts[i])) + { + playerstarts[i].type = playernum + 1; // fake as other player + P_SpawnPlayer(&playerstarts[i]); + playerstarts[i].type = i + 1; // restore + return; + } + // he's going to be inside something. Too bad. + } + P_SpawnPlayer(&playerstarts[playernum]); + } +} + + +void G_ScreenShot(void) +{ + gameaction = ga_screenshot; +} + + +// +// G_DoCompleted +// +void G_ExitLevel(void) +{ + secretexit = false; + gameaction = ga_completed; +} + +// Here's for the german edition. +void G_SecretExitLevel(void) +{ + // IF NO WOLF3D LEVELS, NO SECRET EXIT! + if ((gamemode == commercial) + && (W_CheckNumForName("map31") < 0)) + secretexit = false; + else + secretexit = true; + gameaction = ga_completed; +} + +void G_DoCompleted(void) +{ + int i; + + gameaction = ga_nothing; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + G_PlayerFinishLevel(i); // take away cards and stuff + + if (automapactive) + AM_Stop(); + + if (gamemode != commercial) + switch (gamemap) + { + case 8: + gameaction = ga_victory; + return; + case 9: + for (i = 0; i < MAXPLAYERS; i++) + players[i].didsecret = true; + break; + } + + if ((gamemap == 8) + && (gamemode != commercial)) + { + // victory + gameaction = ga_victory; + return; + } + + if ((gamemap == 9) + && (gamemode != commercial)) + { + // exit secret level + for (i = 0; i < MAXPLAYERS; i++) + players[i].didsecret = true; + } + + + wminfo.didsecret = players[consoleplayer].didsecret; + wminfo.epsd = gameepisode - 1; + wminfo.last = gamemap - 1; + + // wminfo.next is 0 biased, unlike gamemap + if (gamemode == commercial) + { + if (secretexit) + switch (gamemap) + { + case 15: wminfo.next = 30; break; + case 31: wminfo.next = 31; break; + } + else + switch (gamemap) + { + case 31: + case 32: wminfo.next = 15; break; + default: wminfo.next = gamemap; + } + } + else + { + if (secretexit) + wminfo.next = 8; // go to secret level + else if (gamemap == 9) + { + // returning from secret level + switch (gameepisode) + { + case 1: + wminfo.next = 3; + break; + case 2: + wminfo.next = 5; + break; + case 3: + wminfo.next = 6; + break; + case 4: + wminfo.next = 2; + break; + } + } + else + wminfo.next = gamemap; // go to next level + } + + wminfo.maxkills = totalkills; + wminfo.maxitems = totalitems; + wminfo.maxsecret = totalsecret; + wminfo.maxfrags = 0; + if (gamemode == commercial) + wminfo.partime = 35 * cpars[gamemap - 1]; + else + wminfo.partime = 35 * pars[gameepisode][gamemap]; + wminfo.pnum = consoleplayer; + + for (i = 0; i < MAXPLAYERS; i++) + { + wminfo.plyr[i].in = playeringame[i]; + wminfo.plyr[i].skills = players[i].killcount; + wminfo.plyr[i].sitems = players[i].itemcount; + wminfo.plyr[i].ssecret = players[i].secretcount; + wminfo.plyr[i].stime = leveltime; + doom_memcpy(wminfo.plyr[i].frags, players[i].frags + , sizeof(wminfo.plyr[i].frags)); + } + + gamestate = GS_INTERMISSION; + viewactive = false; + automapactive = false; + + if (statcopy) + doom_memcpy(statcopy, &wminfo, sizeof(wminfo)); + + WI_Start(&wminfo); +} + + +// +// G_WorldDone +// +void G_WorldDone(void) +{ + gameaction = ga_worlddone; + + if (secretexit) + players[consoleplayer].didsecret = true; + + if (gamemode == commercial) + { + switch (gamemap) + { + case 15: + case 31: + if (!secretexit) + break; + case 6: + case 11: + case 20: + case 30: + F_StartFinale(); + break; + } + } +} + +void G_DoWorldDone(void) +{ + gamestate = GS_LEVEL; + gamemap = wminfo.next + 1; + G_DoLoadLevel(); + gameaction = ga_nothing; + viewactive = true; +} + + + +// +// G_InitFromSavegame +// Can be called by the startup code or the menu task. +// + +void G_LoadGame(char* name) +{ + doom_strcpy(savename, name); + gameaction = ga_loadgame; +} + + +void G_DoLoadGame(void) +{ + int length; + int i; + int a, b, c; + char vcheck[VERSIONSIZE]; + + gameaction = ga_nothing; + + length = M_ReadFile(savename, &savebuffer); + save_p = savebuffer + SAVESTRINGSIZE; + + // skip the description field + doom_memset(vcheck, 0, sizeof(vcheck)); + //doom_sprintf(vcheck, "version %i", VERSION); + doom_strcpy(vcheck, "version "); + doom_concat(vcheck, doom_itoa(VERSION, 10)); + if (doom_strcmp((const char*)save_p, (const char*)vcheck)) + return; // bad version + save_p += VERSIONSIZE; + + gameskill = *save_p++; + gameepisode = *save_p++; + gamemap = *save_p++; + for (i = 0; i < MAXPLAYERS; i++) + playeringame[i] = *save_p++; + + // load a base level + G_InitNew(gameskill, gameepisode, gamemap); + + // get the times + a = *save_p++; + b = *save_p++; + c = *save_p++; + leveltime = (a << 16) + (b << 8) + c; + + // dearchive all the modifications + P_UnArchivePlayers(); + P_UnArchiveWorld(); + P_UnArchiveThinkers(); + P_UnArchiveSpecials(); + + if (*save_p != 0x1d) + I_Error("Error: Bad savegame"); + + // done + Z_Free(savebuffer); + + if (setsizeneeded) + R_ExecuteSetViewSize(); + + // draw the pattern into the back screen + R_FillBackScreen(); +} + + +// +// G_SaveGame +// Called by the menu task. +// Description is a 24 byte text string +// +void G_SaveGame(int slot, char* description) +{ + savegameslot = slot; + doom_strcpy(savedescription, description); + sendsave = true; +} + +void G_DoSaveGame(void) +{ + char name[100]; + char name2[VERSIONSIZE]; + char* description; + int length; + int i; + +#if 0 + if (M_CheckParm("-cdrom")) + doom_sprintf(name, "c:\\doomdata\\"SAVEGAMENAME"%d.dsg", savegameslot); + else +#endif + { + //doom_sprintf(name, SAVEGAMENAME"%d.dsg", savegameslot); + doom_strcpy(name, SAVEGAMENAME); + doom_concat(name, doom_itoa(savegameslot, 10)); + doom_concat(name, ".dsg"); + } + description = savedescription; + + save_p = savebuffer = screens[1] + 0x4000; + + doom_memcpy(save_p, description, SAVESTRINGSIZE); + save_p += SAVESTRINGSIZE; + doom_memset(name2, 0, sizeof(name2)); + //doom_sprintf(name2, "version %i", VERSION); + doom_strcpy(name2, "version "); + doom_concat(name2, doom_itoa(VERSION, 10)); + doom_memcpy(save_p, name2, VERSIONSIZE); + save_p += VERSIONSIZE; + + *save_p++ = gameskill; + *save_p++ = gameepisode; + *save_p++ = gamemap; + for (i = 0; i < MAXPLAYERS; i++) + *save_p++ = playeringame[i]; + *save_p++ = leveltime >> 16; + *save_p++ = leveltime >> 8; + *save_p++ = leveltime; + + P_ArchivePlayers(); + P_ArchiveWorld(); + P_ArchiveThinkers(); + P_ArchiveSpecials(); + + *save_p++ = 0x1d; // consistancy marker + + length = (int)(save_p - savebuffer); + if (length > SAVEGAMESIZE) + I_Error("Error: Savegame buffer overrun"); + M_WriteFile(name, savebuffer, length); + gameaction = ga_nothing; + savedescription[0] = 0; + + players[consoleplayer].message = GGSAVED; + + // draw the pattern into the back screen + R_FillBackScreen(); +} + + +// +// G_InitNew +// Can be called by the startup code or the menu task, +// consoleplayer, displayplayer, playeringame[] should be set. +// + +void G_DeferedInitNew(skill_t skill, int episode, int map) +{ + d_skill = skill; + d_episode = episode; + d_map = map; + gameaction = ga_newgame; +} + + +void G_DoNewGame(void) +{ + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = 0; + respawnparm = false; + fastparm = false; + nomonsters = false; + consoleplayer = 0; + G_InitNew(d_skill, d_episode, d_map); + gameaction = ga_nothing; +} + +void G_InitNew(skill_t skill, int episode, int map) +{ + int i; + + if (paused) + { + paused = false; + S_ResumeSound(); + } + + + if (skill > sk_nightmare) + skill = sk_nightmare; + + + // This was quite messy with SPECIAL and commented parts. + // Supposedly hacks to make the latest edition work. + // It might not work properly. + if (episode < 1) + episode = 1; + + if (gamemode == retail) + { + if (episode > 4) + episode = 4; + } + else if (gamemode == shareware) + { + if (episode > 1) + episode = 1; // only start episode 1 on shareware + } + else + { + if (episode > 3) + episode = 3; + } + + + + if (map < 1) + map = 1; + + if ((map > 9) + && (gamemode != commercial)) + map = 9; + + M_ClearRandom(); + + if (skill == sk_nightmare || respawnparm) + respawnmonsters = true; + else + respawnmonsters = false; + + if (fastparm || (skill == sk_nightmare && gameskill != sk_nightmare)) + { + for (i = S_SARG_RUN1; i <= S_SARG_PAIN2; i++) + states[i].tics >>= 1; + mobjinfo[MT_BRUISERSHOT].speed = 20 * FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 20 * FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 20 * FRACUNIT; + } + else if (skill != sk_nightmare && gameskill == sk_nightmare) + { + for (i = S_SARG_RUN1; i <= S_SARG_PAIN2; i++) + states[i].tics <<= 1; + mobjinfo[MT_BRUISERSHOT].speed = 15 * FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 10 * FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 10 * FRACUNIT; + } + + + // force players to be initialized upon first level load + for (i = 0; i < MAXPLAYERS; i++) + players[i].playerstate = PST_REBORN; + + usergame = true; // will be set false if a demo + paused = false; + demoplayback = false; + automapactive = false; + viewactive = true; + gameepisode = episode; + gamemap = map; + gameskill = skill; + + viewactive = true; + + // set the sky map for the episode + if (gamemode == commercial) + { + skytexture = R_TextureNumForName("SKY3"); + if (gamemap < 12) + skytexture = R_TextureNumForName("SKY1"); + else + if (gamemap < 21) + skytexture = R_TextureNumForName("SKY2"); + } + else + switch (episode) + { + case 1: + skytexture = R_TextureNumForName("SKY1"); + break; + case 2: + skytexture = R_TextureNumForName("SKY2"); + break; + case 3: + skytexture = R_TextureNumForName("SKY3"); + break; + case 4: // Special Edition sky + skytexture = R_TextureNumForName("SKY4"); + break; + } + + G_DoLoadLevel(); +} + + +// +// DEMO RECORDING +// + +void G_ReadDemoTiccmd(ticcmd_t* cmd) +{ + if (*demo_p == DEMOMARKER) + { + // end of demo data stream + G_CheckDemoStatus(); + return; + } + cmd->forwardmove = ((signed char)*demo_p++); + cmd->sidemove = ((signed char)*demo_p++); + cmd->angleturn = ((unsigned char)*demo_p++) << 8; + cmd->buttons = (unsigned char)*demo_p++; +} + + +void G_WriteDemoTiccmd(ticcmd_t* cmd) +{ + if (gamekeydown['q']) // press q to end demo recording + G_CheckDemoStatus(); + *demo_p++ = cmd->forwardmove; + *demo_p++ = cmd->sidemove; + *demo_p++ = (cmd->angleturn + 128) >> 8; + *demo_p++ = cmd->buttons; + demo_p -= 4; + if (demo_p > demoend - 16) + { + // no more space + G_CheckDemoStatus(); + return; + } + + G_ReadDemoTiccmd(cmd); // make SURE it is exactly the same +} + + +// +// G_RecordDemo +// +void G_RecordDemo(char* name) +{ + int i; + int maxsize; + + usergame = false; + doom_strcpy(demoname, name); + doom_concat(demoname, ".lmp"); + maxsize = 0x20000; + i = M_CheckParm("-maxdemo"); + if (i && i < myargc - 1) + maxsize = doom_atoi(myargv[i + 1]) * 1024; + demobuffer = Z_Malloc(maxsize, PU_STATIC, 0); + demoend = demobuffer + maxsize; + + demorecording = true; +} + + +void G_BeginRecording(void) +{ + int i; + + demo_p = demobuffer; + + *demo_p++ = VERSION; + *demo_p++ = gameskill; + *demo_p++ = gameepisode; + *demo_p++ = gamemap; + *demo_p++ = deathmatch; + *demo_p++ = respawnparm; + *demo_p++ = fastparm; + *demo_p++ = nomonsters; + *demo_p++ = consoleplayer; + + for (i = 0; i < MAXPLAYERS; i++) + *demo_p++ = playeringame[i]; +} + + +// +// G_PlayDemo +// + + +void G_DeferedPlayDemo(char* name) +{ + defdemoname = name; + gameaction = ga_playdemo; +} + +void G_DoPlayDemo(void) +{ + skill_t skill; + int i, episode, map; + + gameaction = ga_nothing; + demobuffer = demo_p = W_CacheLumpName(defdemoname, PU_STATIC); + byte demo_version = *demo_p++; + if (demo_version != VERSION && + demo_version != 109) // Demos seem to run fine with version 109 + { + //doom_print("Demo is from a different game version! Demo Verson = %i, this version = %i\n", (int)demo_version, VERSION); + doom_print("Demo is from a different game version! Demo Verson = "); + doom_print(doom_itoa((int)demo_version, 10)); + doom_print(", this version = "); + doom_print(doom_itoa(VERSION, 10)); + doom_print("\n"); + gameaction = ga_nothing; + return; + } + + skill = *demo_p++; + episode = *demo_p++; + map = *demo_p++; + deathmatch = *demo_p++; + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + consoleplayer = *demo_p++; + + for (i = 0; i < MAXPLAYERS; i++) + playeringame[i] = *demo_p++; + if (playeringame[1]) + { + netgame = true; + netdemo = true; + } + + // don't spend a lot of time in loadlevel + precache = false; + G_InitNew(skill, episode, map); + precache = true; + + usergame = false; + demoplayback = true; +} + +// +// G_TimeDemo +// +void G_TimeDemo(char* name) +{ + nodrawers = M_CheckParm("-nodraw"); + noblit = M_CheckParm("-noblit"); + timingdemo = true; + singletics = true; + + defdemoname = name; + gameaction = ga_playdemo; +} + + +/* +=================== += += G_CheckDemoStatus += += Called after a death or level completion to allow demos to be cleaned up += Returns true if a new demo loop action will take place +=================== +*/ + +doom_boolean G_CheckDemoStatus(void) +{ + int endtime; + + if (timingdemo) + { + endtime = I_GetTime(); + //I_Error("Error: timed %i gametics in %i realtics", gametic + // , endtime - starttime); + + doom_strcpy(error_buf, "Error: timed "); + doom_concat(error_buf, doom_itoa(gametic, 10)); + doom_concat(error_buf, " gametics in "); + doom_concat(error_buf, doom_itoa(endtime - starttime, 10)); + doom_concat(error_buf, " realtics"); + I_Error(error_buf); + } + + if (demoplayback) + { + if (singledemo) + I_Quit(); + + Z_ChangeTag(demobuffer, PU_CACHE); + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = 0; + respawnparm = false; + fastparm = false; + nomonsters = false; + consoleplayer = 0; + D_AdvanceDemo(); + return true; + } + + if (demorecording) + { + *demo_p++ = DEMOMARKER; + M_WriteFile(demoname, demobuffer, (int)(demo_p - demobuffer)); + Z_Free(demobuffer); + demorecording = false; + //I_Error("Error: Demo %s recorded", demoname); + + doom_strcpy(error_buf, "Error: Demo "); + doom_concat(error_buf, demoname); + doom_concat(error_buf, " recorded"); + I_Error(error_buf); + } + + return false; +} +#define noterased viewwindowx + + +extern doom_boolean automapactive; // in AM_map.c + + +void HUlib_init(void) +{ +} + + +void HUlib_clearTextLine(hu_textline_t* t) +{ + t->len = 0; + t->l[0] = 0; + t->needsupdate = true; +} + + +void HUlib_initTextLine(hu_textline_t* t, int x, int y, patch_t** f, int sc) +{ + t->x = x; + t->y = y; + t->f = f; + t->sc = sc; + HUlib_clearTextLine(t); +} + + +doom_boolean HUlib_addCharToTextLine(hu_textline_t* t, char ch) +{ + if (t->len == HU_MAXLINELENGTH) + return false; + else + { + t->l[t->len++] = ch; + t->l[t->len] = 0; + t->needsupdate = 4; + return true; + } +} + + +doom_boolean HUlib_delCharFromTextLine(hu_textline_t* t) +{ + if (!t->len) return false; + else + { + t->l[--t->len] = 0; + t->needsupdate = 4; + return true; + } +} + + +void HUlib_drawTextLine(hu_textline_t* l, doom_boolean drawcursor) +{ + int i; + int w; + int x; + unsigned char c; + + // draw the new stuff + x = l->x; + for (i = 0; i < l->len; i++) + { + c = doom_toupper(l->l[i]); + if (c != ' ' + && c >= l->sc + && c <= '_') + { + w = SHORT(l->f[c - l->sc]->width); + if (x + w > SCREENWIDTH) + break; + V_DrawPatchDirect(x, l->y, FG, l->f[c - l->sc]); + x += w; + } + else + { + x += 4; + if (x >= SCREENWIDTH) + break; + } + } + + // draw the cursor if requested + if (drawcursor + && x + SHORT(l->f['_' - l->sc]->width) <= SCREENWIDTH) + { + V_DrawPatchDirect(x, l->y, FG, l->f['_' - l->sc]); + } +} + + +// sorta called by HU_Erase and just better darn get things straight +void HUlib_eraseTextLine(hu_textline_t* l) +{ + int lh; + int y; + int yoffset; + static doom_boolean lastautomapactive = true; + + // Only erases when NOT in automap and the screen is reduced, + // and the text must either need updating or refreshing + // (because of a recent change back from the automap) + + if (!automapactive && + viewwindowx && l->needsupdate) + { + lh = SHORT(l->f[0]->height) + 1; + for (y = l->y, yoffset = y * SCREENWIDTH; y < l->y + lh; y++, yoffset += SCREENWIDTH) + { + if (y < viewwindowy || y >= viewwindowy + viewheight) + R_VideoErase(yoffset, SCREENWIDTH); // erase entire line + else + { + R_VideoErase(yoffset, viewwindowx); // erase left border + R_VideoErase(yoffset + viewwindowx + viewwidth, viewwindowx); + // erase right border + } + } + } + + lastautomapactive = automapactive; + if (l->needsupdate) l->needsupdate--; +} + + +void HUlib_initSText(hu_stext_t* s, + int x, + int y, + int h, + patch_t** font, + int startchar, + doom_boolean* on) +{ + int i; + + s->h = h; + s->on = on; + s->laston = true; + s->cl = 0; + for (i = 0; i < h; i++) + HUlib_initTextLine(&s->l[i], + x, y - i * (SHORT(font[0]->height) + 1), + font, startchar); +} + + +void HUlib_addLineToSText(hu_stext_t* s) +{ + + int i; + + // add a clear line + if (++s->cl == s->h) + s->cl = 0; + HUlib_clearTextLine(&s->l[s->cl]); + + // everything needs updating + for (i = 0; i < s->h; i++) + s->l[i].needsupdate = 4; +} + + +void HUlib_addMessageToSText(hu_stext_t* s, char* prefix, char* msg) +{ + HUlib_addLineToSText(s); + if (prefix) + while (*prefix) + HUlib_addCharToTextLine(&s->l[s->cl], *(prefix++)); + + while (*msg) + HUlib_addCharToTextLine(&s->l[s->cl], *(msg++)); +} + + +void HUlib_drawSText(hu_stext_t* s) +{ + int i, idx; + hu_textline_t* l; + + if (!*s->on) + return; // if not on, don't draw + + // draw everything + for (i = 0; i < s->h; i++) + { + idx = s->cl - i; + if (idx < 0) + idx += s->h; // handle queue of lines + + l = &s->l[idx]; + + // need a decision made here on whether to skip the draw + HUlib_drawTextLine(l, false); // no cursor, please + } +} + + +void HUlib_eraseSText(hu_stext_t* s) +{ + int i; + + for (i = 0; i < s->h; i++) + { + if (s->laston && !*s->on) + s->l[i].needsupdate = 4; + HUlib_eraseTextLine(&s->l[i]); + } + s->laston = *s->on; +} + + +void HUlib_initIText(hu_itext_t* it, + int x, + int y, + patch_t** font, + int startchar, + doom_boolean* on) +{ + it->lm = 0; // default left margin is start of text + it->on = on; + it->laston = true; + HUlib_initTextLine(&it->l, x, y, font, startchar); +} + + +// The following deletion routines adhere to the left margin restriction +void HUlib_delCharFromIText(hu_itext_t* it) +{ + if (it->l.len != it->lm) + HUlib_delCharFromTextLine(&it->l); +} + + +void HUlib_eraseLineFromIText(hu_itext_t* it) +{ + while (it->lm != it->l.len) + HUlib_delCharFromTextLine(&it->l); +} + + +// Resets left margin as well +void HUlib_resetIText(hu_itext_t* it) +{ + it->lm = 0; + HUlib_clearTextLine(&it->l); +} + + +void HUlib_addPrefixToIText(hu_itext_t* it, char* str) +{ + while (*str) + HUlib_addCharToTextLine(&it->l, *(str++)); + it->lm = it->l.len; +} + + +// wrapper function for handling general keyed input. +// returns true if it ate the key +doom_boolean HUlib_keyInIText(hu_itext_t* it, unsigned char ch) +{ + if (ch >= ' ' && ch <= '_') + HUlib_addCharToTextLine(&it->l, (char)ch); + else + if (ch == KEY_BACKSPACE) + HUlib_delCharFromIText(it); + else + if (ch != KEY_ENTER) + return false; // did not eat key + + return true; // ate the key +} + + +void HUlib_drawIText(hu_itext_t* it) +{ + hu_textline_t* l = &it->l; + + if (!*it->on) + return; + HUlib_drawTextLine(l, true); // draw the line w/ cursor +} + + +void HUlib_eraseIText(hu_itext_t* it) +{ + if (it->laston && !*it->on) + it->l.needsupdate = 4; + HUlib_eraseTextLine(&it->l); + it->laston = *it->on; +} +#define HU_TITLE (mapnames[(gameepisode-1)*9+gamemap-1]) +#define HU_TITLE2 (mapnames2[gamemap-1]) +#define HU_TITLEP (mapnamesp[gamemap-1]) +#define HU_TITLET (mapnamest[gamemap-1]) +#define HU_TITLEHEIGHT 1 +#define HU_TITLEX 0 +#define HU_TITLEY (167 - SHORT(hu_font[0]->height)) +#define HU_INPUTTOGGLE 't' +#define HU_INPUTX HU_MSGX +#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0]->height) +1)) +#define HU_INPUTWIDTH 64 +#define HU_INPUTHEIGHT 1 +#define QUEUESIZE 128 + + +static player_t* plr; +static hu_textline_t w_title; +static hu_itext_t w_chat; +static doom_boolean always_off = false; +static char chat_dest[MAXPLAYERS]; +static hu_itext_t w_inputbuffer[MAXPLAYERS]; +static doom_boolean message_on; +static doom_boolean message_nottobefuckedwith; +static hu_stext_t w_message; +static int message_counter; +static doom_boolean headsupactive = false; +static char chatchars[QUEUESIZE]; +static int head = 0; +static int tail = 0; + +char* chat_macros[] = +{ + HUSTR_CHATMACRO0, + HUSTR_CHATMACRO1, + HUSTR_CHATMACRO2, + HUSTR_CHATMACRO3, + HUSTR_CHATMACRO4, + HUSTR_CHATMACRO5, + HUSTR_CHATMACRO6, + HUSTR_CHATMACRO7, + HUSTR_CHATMACRO8, + HUSTR_CHATMACRO9 +}; + +char* player_names[] = +{ + HUSTR_PLRGREEN, + HUSTR_PLRINDIGO, + HUSTR_PLRBROWN, + HUSTR_PLRRED +}; + +const char* shiftxform; + +const char french_shiftxform[] = +{ + 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, + ' ', '!', '"', '#', '$', '%', '&', + '"', // shift-' + '(', ')', '*', '+', + '?', // shift-, + '_', // shift-- + '>', // shift-. + '?', // shift-/ + '0', // shift-0 + '1', // shift-1 + '2', // shift-2 + '3', // shift-3 + '4', // shift-4 + '5', // shift-5 + '6', // shift-6 + '7', // shift-7 + '8', // shift-8 + '9', // shift-9 + '/', + '.', // shift-; + '<', + '+', // shift-= + '>', '?', '@', + 'A', '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', + '[', // shift-[ + '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK + ']', // shift-] + '"', '_', + '\'', // shift-` + 'A', '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', + '{', '|', '}', '~', 127 + +}; + +const char english_shiftxform[] = +{ + 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, + ' ', '!', '"', '#', '$', '%', '&', + '"', // shift-' + '(', ')', '*', '+', + '<', // shift-, + '_', // shift-- + '>', // shift-. + '?', // shift-/ + ')', // shift-0 + '!', // shift-1 + '@', // shift-2 + '#', // shift-3 + '$', // shift-4 + '%', // shift-5 + '^', // shift-6 + '&', // shift-7 + '*', // shift-8 + '(', // shift-9 + ':', + ':', // shift-; + '<', + '+', // shift-= + '>', '?', '@', + 'A', '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', + '[', // shift-[ + '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK + ']', // shift-] + '"', '_', + '\'', // shift-` + 'A', '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', + '{', '|', '}', '~', 127 +}; + +char frenchKeyMap[128] = +{ + 0, + 1,2,3,4,5,6,7,8,9,10, + 11,12,13,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30, + 31, + ' ','!','"','#','$','%','&','%','(',')','*','+',';','-',':','!', + '0','1','2','3','4','5','6','7','8','9',':','M','<','=','>','?', + '@','Q','B','C','D','E','F','G','H','I','J','K','L',',','N','O', + 'P','A','R','S','T','U','V','Z','X','Y','W','^','\\','$','^','_', + '@','Q','B','C','D','E','F','G','H','I','J','K','L',',','N','O', + 'P','A','R','S','T','U','V','Z','X','Y','W','^','\\','$','^',127 +}; + +char chat_char; // remove later. +patch_t* hu_font[HU_FONTSIZE]; +doom_boolean chat_on; +doom_boolean message_dontfuckwithme; + +extern int showMessages; +extern doom_boolean automapactive; + + +// +// Builtin map names. +// The actual names can be found in DStrings.h. +// + +char* mapnames[] = // DOOM shareware/registered/retail (Ultimate) names. +{ + + HUSTR_E1M1, + HUSTR_E1M2, + HUSTR_E1M3, + HUSTR_E1M4, + HUSTR_E1M5, + HUSTR_E1M6, + HUSTR_E1M7, + HUSTR_E1M8, + HUSTR_E1M9, + + HUSTR_E2M1, + HUSTR_E2M2, + HUSTR_E2M3, + HUSTR_E2M4, + HUSTR_E2M5, + HUSTR_E2M6, + HUSTR_E2M7, + HUSTR_E2M8, + HUSTR_E2M9, + + HUSTR_E3M1, + HUSTR_E3M2, + HUSTR_E3M3, + HUSTR_E3M4, + HUSTR_E3M5, + HUSTR_E3M6, + HUSTR_E3M7, + HUSTR_E3M8, + HUSTR_E3M9, + + HUSTR_E4M1, + HUSTR_E4M2, + HUSTR_E4M3, + HUSTR_E4M4, + HUSTR_E4M5, + HUSTR_E4M6, + HUSTR_E4M7, + HUSTR_E4M8, + HUSTR_E4M9, + + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL" +}; + +char* mapnames2[] = // DOOM 2 map names. +{ + HUSTR_1, + HUSTR_2, + HUSTR_3, + HUSTR_4, + HUSTR_5, + HUSTR_6, + HUSTR_7, + HUSTR_8, + HUSTR_9, + HUSTR_10, + HUSTR_11, + + HUSTR_12, + HUSTR_13, + HUSTR_14, + HUSTR_15, + HUSTR_16, + HUSTR_17, + HUSTR_18, + HUSTR_19, + HUSTR_20, + + HUSTR_21, + HUSTR_22, + HUSTR_23, + HUSTR_24, + HUSTR_25, + HUSTR_26, + HUSTR_27, + HUSTR_28, + HUSTR_29, + HUSTR_30, + HUSTR_31, + HUSTR_32 +}; + + +char* mapnamesp[] = // Plutonia WAD map names. +{ + PHUSTR_1, + PHUSTR_2, + PHUSTR_3, + PHUSTR_4, + PHUSTR_5, + PHUSTR_6, + PHUSTR_7, + PHUSTR_8, + PHUSTR_9, + PHUSTR_10, + PHUSTR_11, + + PHUSTR_12, + PHUSTR_13, + PHUSTR_14, + PHUSTR_15, + PHUSTR_16, + PHUSTR_17, + PHUSTR_18, + PHUSTR_19, + PHUSTR_20, + + PHUSTR_21, + PHUSTR_22, + PHUSTR_23, + PHUSTR_24, + PHUSTR_25, + PHUSTR_26, + PHUSTR_27, + PHUSTR_28, + PHUSTR_29, + PHUSTR_30, + PHUSTR_31, + PHUSTR_32 +}; + + +char* mapnamest[] = // TNT WAD map names. +{ + THUSTR_1, + THUSTR_2, + THUSTR_3, + THUSTR_4, + THUSTR_5, + THUSTR_6, + THUSTR_7, + THUSTR_8, + THUSTR_9, + THUSTR_10, + THUSTR_11, + + THUSTR_12, + THUSTR_13, + THUSTR_14, + THUSTR_15, + THUSTR_16, + THUSTR_17, + THUSTR_18, + THUSTR_19, + THUSTR_20, + + THUSTR_21, + THUSTR_22, + THUSTR_23, + THUSTR_24, + THUSTR_25, + THUSTR_26, + THUSTR_27, + THUSTR_28, + THUSTR_29, + THUSTR_30, + THUSTR_31, + THUSTR_32 +}; + +char ForeignTranslation(unsigned char ch) +{ + return ch < 128 ? frenchKeyMap[ch] : ch; +} + +void HU_Init(void) +{ + + int i; + int j; + char buffer[9]; + + if (french) + shiftxform = french_shiftxform; + else + shiftxform = english_shiftxform; + + // load the heads-up font + j = HU_FONTSTART; + for (i = 0; i < HU_FONTSIZE; i++) + { + //if (j == 40) __debugbreak(); + //doom_sprintf(buffer, "STCFN%.3d", j++); + doom_strcpy(buffer, "STCFN"); + if (j < 100) doom_concat(buffer, "0"); + if (j < 10) doom_concat(buffer, "0"); + doom_concat(buffer, doom_itoa(j++, 10)); + hu_font[i] = (patch_t*)W_CacheLumpName(buffer, PU_STATIC); + } +} + +void HU_Stop(void) +{ + headsupactive = false; +} + +void HU_Start(void) +{ + int i; + char* s; + + if (headsupactive) + HU_Stop(); + + plr = &players[consoleplayer]; + message_on = false; + message_dontfuckwithme = false; + message_nottobefuckedwith = false; + chat_on = false; + + // create the message widget + HUlib_initSText(&w_message, + HU_MSGX, HU_MSGY, HU_MSGHEIGHT, + hu_font, + HU_FONTSTART, &message_on); + + // create the map title widget + HUlib_initTextLine(&w_title, + HU_TITLEX, HU_TITLEY, + hu_font, + HU_FONTSTART); + + switch (gamemode) + { + case shareware: + case registered: + case retail: + s = HU_TITLE; + break; + + /* FIXME + case pack_plut: + s = HU_TITLEP; + break; + case pack_tnt: + s = HU_TITLET; + break; + */ + + case commercial: + default: + s = HU_TITLE2; + break; + } + + while (*s) + HUlib_addCharToTextLine(&w_title, *(s++)); + + // create the chat widget + HUlib_initIText(&w_chat, + HU_INPUTX, HU_INPUTY, + hu_font, + HU_FONTSTART, &chat_on); + + // create the inputbuffer widgets + for (i = 0; i < MAXPLAYERS; i++) + HUlib_initIText(&w_inputbuffer[i], 0, 0, 0, 0, &always_off); + + headsupactive = true; +} + +void HU_Drawer(void) +{ + HUlib_drawSText(&w_message); + HUlib_drawIText(&w_chat); + if (automapactive) + HUlib_drawTextLine(&w_title, false); +} + +void HU_Erase(void) +{ + HUlib_eraseSText(&w_message); + HUlib_eraseIText(&w_chat); + HUlib_eraseTextLine(&w_title); +} + +void HU_Ticker(void) +{ + int i, rc; + char c; + + // tick down message counter if message is up + if (message_counter && !--message_counter) + { + message_on = false; + message_nottobefuckedwith = false; + } + + if (showMessages || message_dontfuckwithme) + { + + // display message if necessary + if ((plr->message && !message_nottobefuckedwith) + || (plr->message && message_dontfuckwithme)) + { + HUlib_addMessageToSText(&w_message, 0, plr->message); + plr->message = 0; + message_on = true; + message_counter = HU_MSGTIMEOUT; + message_nottobefuckedwith = message_dontfuckwithme; + message_dontfuckwithme = 0; + } + + } // else message_on = false; + + // check for incoming chat characters + if (netgame) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + if (i != consoleplayer + && (c = players[i].cmd.chatchar)) + { + if (c <= HU_BROADCAST) + chat_dest[i] = c; + else + { + if (c >= 'a' && c <= 'z') + c = (char)shiftxform[(unsigned char)c]; + rc = HUlib_keyInIText(&w_inputbuffer[i], c); + if (rc && c == KEY_ENTER) + { + if (w_inputbuffer[i].l.len + && (chat_dest[i] == consoleplayer + 1 + || chat_dest[i] == HU_BROADCAST)) + { + HUlib_addMessageToSText(&w_message, + player_names[i], + w_inputbuffer[i].l.l); + + message_nottobefuckedwith = true; + message_on = true; + message_counter = HU_MSGTIMEOUT; + if (gamemode == commercial) + S_StartSound(0, sfx_radio); + else + S_StartSound(0, sfx_tink); + } + HUlib_resetIText(&w_inputbuffer[i]); + } + } + players[i].cmd.chatchar = 0; + } + } + } +} + +void HU_queueChatChar(char c) +{ + if (((head + 1) & (QUEUESIZE - 1)) == tail) + { + plr->message = HUSTR_MSGU; + } + else + { + chatchars[head] = c; + head = (head + 1) & (QUEUESIZE - 1); + } +} + +char HU_dequeueChatChar(void) +{ + char c; + + if (head != tail) + { + c = chatchars[tail]; + tail = (tail + 1) & (QUEUESIZE - 1); + } + else + { + c = 0; + } + + return c; +} + +doom_boolean HU_Responder(event_t* ev) +{ + + static char lastmessage[HU_MAXLINELENGTH + 1]; + char* macromessage; + doom_boolean eatkey = false; + static doom_boolean shiftdown = false; + static doom_boolean altdown = false; + unsigned char c; + int i; + int numplayers; + + static char destination_keys[MAXPLAYERS] = + { + HUSTR_KEYGREEN, + HUSTR_KEYINDIGO, + HUSTR_KEYBROWN, + HUSTR_KEYRED + }; + + static int num_nobrainers = 0; + + numplayers = 0; + for (i = 0; i < MAXPLAYERS; i++) + numplayers += playeringame[i]; + + if (ev->data1 == KEY_RSHIFT) + { + shiftdown = ev->type == ev_keydown; + return false; + } + else if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT) + { + altdown = ev->type == ev_keydown; + return false; + } + + if (ev->type != ev_keydown) + return false; + + if (!chat_on) + { + if (ev->data1 == HU_MSGREFRESH) + { + message_on = true; + message_counter = HU_MSGTIMEOUT; + eatkey = true; + } + else if (netgame && ev->data1 == HU_INPUTTOGGLE) + { + eatkey = chat_on = true; + HUlib_resetIText(&w_chat); + HU_queueChatChar(HU_BROADCAST); + } + else if (netgame && numplayers > 2) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (ev->data1 == destination_keys[i]) + { + if (playeringame[i] && i != consoleplayer) + { + eatkey = chat_on = true; + HUlib_resetIText(&w_chat); + HU_queueChatChar(i + 1); + break; + } + else if (i == consoleplayer) + { + num_nobrainers++; + if (num_nobrainers < 3) + plr->message = HUSTR_TALKTOSELF1; + else if (num_nobrainers < 6) + plr->message = HUSTR_TALKTOSELF2; + else if (num_nobrainers < 9) + plr->message = HUSTR_TALKTOSELF3; + else if (num_nobrainers < 32) + plr->message = HUSTR_TALKTOSELF4; + else + plr->message = HUSTR_TALKTOSELF5; + } + } + } + } + } + else + { + c = ev->data1; + // send a macro + if (altdown) + { + c = c - '0'; + if (c > 9) + return false; + macromessage = chat_macros[c]; + + // kill last message with a '\n' + HU_queueChatChar(KEY_ENTER); // DEBUG!!! + + // send the macro message + while (*macromessage) + HU_queueChatChar(*macromessage++); + HU_queueChatChar(KEY_ENTER); + + // leave chat mode and notify that it was sent + chat_on = false; + doom_strcpy(lastmessage, chat_macros[c]); + plr->message = lastmessage; + eatkey = true; + } + else + { + if (french) + c = ForeignTranslation(c); + if (shiftdown || (c >= 'a' && c <= 'z')) + c = shiftxform[c]; + eatkey = HUlib_keyInIText(&w_chat, c); + if (eatkey) + { + HU_queueChatChar(c); + } + if (c == KEY_ENTER) + { + chat_on = false; + if (w_chat.l.len) + { + doom_strcpy(lastmessage, w_chat.l.l); + plr->message = lastmessage; + } + } + else if (c == KEY_ESCAPE) + chat_on = false; + } + } + + return eatkey; +} +#if defined(I_NET_ENABLED) +#define _CRT_SECURE_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#endif +//#include "doom_config.h" + +#if defined(I_NET_ENABLED) +#if defined(DOOM_WIN32) +#define _CRT_SECURE_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS +//#include +//#include +#define IPPORT_USERRESERVED 5000 +#pragma comment(lib, "ws2_32.lib") +#else + //#include + //#include + //#include + //#include +#define SOCKET int +#endif +#else +#define IPPORT_USERRESERVED 5000 +#endif + +//#include "i_system.h" +//#include "d_event.h" +//#include "d_net.h" +//#include "m_argv.h" +//#include "doomstat.h" +//#include "i_net.h" + + +// For some odd reason... +#if !defined(DOOM_APPLE) // It doesn't complain on Win32? O_o +#define ntohl(x) \ + ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \ + (((unsigned long int)(x) & 0x0000ff00U) << 8) | \ + (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \ + (((unsigned long int)(x) & 0xff000000U) >> 24))) + +#define ntohs(x) \ + ((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \ + (((unsigned short int)(x) & 0xff00) >> 8))) \ + +#define htonl(x) ntohl(x) +#define htons(x) ntohs(x) +#endif + + +// +// NETWORKING +// + +int DOOMPORT = (IPPORT_USERRESERVED + 0x1d); +int DOOMPORT_SEND = (IPPORT_USERRESERVED + 0x1e); + +#if defined(I_NET_ENABLED) +SOCKET sendsocket; +SOCKET insocket; + +struct sockaddr_in sendaddress[MAXNETNODES]; +#endif + +void (*netget) (void); +void (*netsend) (void); + + +void NetSend(void); +doom_boolean NetListen(void); + + +// +// UDPsocket +// +#if defined(I_NET_ENABLED) +SOCKET UDPsocket(void) +{ + SOCKET s; + + // allocate a socket + s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0) + { + //I_Error("Error: can't create socket: %s", strerror(errno)); + + doom_strcpy(error_buf, "Error: can't create socket: "); + doom_concat(error_buf, strerror(errno)); + I_Error(error_buf); + } + + return s; +} +#endif + + +// +// BindToLocalPort +// +#if defined(I_NET_ENABLED) +void BindToLocalPort(SOCKET s, int port) +{ + int v; + struct sockaddr_in address; + + doom_memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = port; + + v = bind(s, (void*)&address, sizeof(address)); + if (v == -1) + { + //I_Error("Error: BindToPort: bind: %s", strerror(errno)); + + doom_strcpy(error_buf, "Error: BindToPort: bind: "); + doom_concat(error_buf, strerror(errno)); + I_Error(error_buf); + } +} +#endif + + +// +// PacketSend +// +void PacketSend(void) +{ +#if defined(I_NET_ENABLED) + int c; + doomdata_t sw; + + // byte swap + sw.checksum = htonl(netbuffer->checksum); + sw.player = netbuffer->player; + sw.retransmitfrom = netbuffer->retransmitfrom; + sw.starttic = netbuffer->starttic; + sw.numtics = netbuffer->numtics; + for (c = 0; c < netbuffer->numtics; c++) + { + sw.cmds[c].forwardmove = netbuffer->cmds[c].forwardmove; + sw.cmds[c].sidemove = netbuffer->cmds[c].sidemove; + sw.cmds[c].angleturn = htons(netbuffer->cmds[c].angleturn); + sw.cmds[c].consistancy = htons(netbuffer->cmds[c].consistancy); + sw.cmds[c].chatchar = netbuffer->cmds[c].chatchar; + sw.cmds[c].buttons = netbuffer->cmds[c].buttons; + } + + //doom_print ("sending %i\n",gametic); + c = sendto(sendsocket, (const char*)&sw, doomcom->datalength + , 0, (void*)&sendaddress[doomcom->remotenode] + , sizeof(sendaddress[doomcom->remotenode])); +#endif +} + + +// +// PacketGet +// +void PacketGet(void) +{ +#if defined(I_NET_ENABLED) + int i; + int c; + struct sockaddr_in fromaddress; +#if defined(__APPLE__) + socklen_t fromlen; +#else + int fromlen; +#endif + doomdata_t sw; + + fromlen = sizeof(fromaddress); + c = recvfrom(insocket, (char*)&sw, sizeof(sw), 0 + , (struct sockaddr*)&fromaddress, &fromlen); + if (c == -1) + { +#if defined(DOOM_WIN32) + int r = WSAGetLastError(); + if (r != WSAEWOULDBLOCK) + { + //I_Error("Error: GetPacket: %i", r); + + doom_strcpy(error_buf, "Error: GetPacket: "); + doom_concat(error_buf, doom_itoa(r, 10)); + I_Error(error_buf); + } +#else + if (errno != EWOULDBLOCK) + { + //I_Error("Error: GetPacket: %s", strerror(errno)); + + doom_strcpy(error_buf, "Error: GetPacket: "); + doom_concat(error_buf, strerror(errno)); + I_Error(error_buf); + } +#endif + doomcom->remotenode = -1; // no packet + return; + } + + { + static int first = 1; + if (first) + { + //doom_print("len=%d:p=[0x%x 0x%x] \n", c, *(int*)&sw, *((int*)&sw + 1)); + doom_print("len="); + doom_print(doom_itoa(c, 10)); + doom_print(":p=[0x"); + doom_print(doom_itoa(*(int*)&sw, 16)); + doom_print(" 0x"); + doom_print(doom_itoa(*((int*)&sw + 1), 16)); + doom_print("] \n"); + } + first = 0; + } + + // find remote node number + for (i = 0; i < doomcom->numnodes; i++) + if (fromaddress.sin_addr.s_addr == sendaddress[i].sin_addr.s_addr) + break; + + if (i == doomcom->numnodes) + { + // packet is not from one of the players (new game broadcast) + doomcom->remotenode = -1; // no packet + return; + } + + doomcom->remotenode = i; // good packet from a game player + doomcom->datalength = c; + + // byte swap + netbuffer->checksum = ntohl(sw.checksum); + netbuffer->player = sw.player; + netbuffer->retransmitfrom = sw.retransmitfrom; + netbuffer->starttic = sw.starttic; + netbuffer->numtics = sw.numtics; + + for (c = 0; c < netbuffer->numtics; c++) + { + netbuffer->cmds[c].forwardmove = sw.cmds[c].forwardmove; + netbuffer->cmds[c].sidemove = sw.cmds[c].sidemove; + netbuffer->cmds[c].angleturn = ntohs(sw.cmds[c].angleturn); + netbuffer->cmds[c].consistancy = ntohs(sw.cmds[c].consistancy); + netbuffer->cmds[c].chatchar = sw.cmds[c].chatchar; + netbuffer->cmds[c].buttons = sw.cmds[c].buttons; + } +#endif +} + + +int GetLocalAddress(void) +{ +#if defined(I_NET_ENABLED) + char hostname[1024]; + struct hostent* hostentry; // host information entry + int v; + + // get local address + v = gethostname(hostname, sizeof(hostname)); + if (v == -1) + { + //I_Error("Error: GetLocalAddress : gethostname: errno %d", errno); + + doom_strcpy(error_buf, "Error: GetLocalAddress : gethostname: errno "); + doom_concat(error_buf, strerror(errno)); + I_Error(error_buf); + } + + hostentry = gethostbyname(hostname); + if (!hostentry) + { + I_Error("Error: GetLocalAddress : gethostbyname: couldn't get local host"); + } + + return *(int*)hostentry->h_addr_list[0]; +#else + return 0; +#endif +} + + +// +// I_InitNetwork +// +void I_InitNetwork(void) +{ +#if defined(I_NET_ENABLED) +#if defined(DOOM_WIN32) + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); + + u_long trueval = 1; +#else + doom_boolean trueval = true; +#endif +#endif + int i; + int p; + struct hostent* hostentry; // host information entry + + doomcom = doom_malloc(sizeof(*doomcom)); + doom_memset(doomcom, 0, sizeof(*doomcom)); + + // set up for network + i = M_CheckParm("-dup"); + if (i && i < myargc - 1) + { + doomcom->ticdup = myargv[i + 1][0] - '0'; + if (doomcom->ticdup < 1) + doomcom->ticdup = 1; + if (doomcom->ticdup > 9) + doomcom->ticdup = 9; + } + else + doomcom->ticdup = 1; + + if (M_CheckParm("-extratic")) + doomcom->extratics = 1; + else + doomcom->extratics = 0; + + p = M_CheckParm("-port"); + if (p && p < myargc - 1) + { + DOOMPORT = doom_atoi(myargv[p + 1]); + //doom_print("using alternate port %i\n", DOOMPORT); + doom_print("using alternate port "); + doom_print(doom_itoa(DOOMPORT, 10)); + doom_print("\n"); + } + + p = M_CheckParm("-sendport"); + if (p && p < myargc - 1) + { + DOOMPORT_SEND = doom_atoi(myargv[p + 1]); + //doom_print("using alternate send port %i\n", DOOMPORT_SEND); + doom_print("using alternate send port "); + doom_print(doom_itoa(DOOMPORT_SEND, 10)); + doom_print("\n"); + } + + // parse network game options, + // -net ... + i = M_CheckParm("-net"); + if (!i) + { + // single player game + netgame = false; + doomcom->id = DOOMCOM_ID; + doomcom->numplayers = doomcom->numnodes = 1; + doomcom->deathmatch = false; + doomcom->consoleplayer = 0; + return; + } + +#if defined(I_NET_ENABLED) + netsend = PacketSend; + netget = PacketGet; + netgame = true; + + // parse player number and host list + doomcom->consoleplayer = myargv[i + 1][0] - '1'; + + doomcom->numnodes = 1; // this node for sure + + i++; + while (++i < myargc && myargv[i][0] != '-') + { + sendaddress[doomcom->numnodes].sin_family = AF_INET; + sendaddress[doomcom->numnodes].sin_port = htons(DOOMPORT); + if (myargv[i][0] == '.') + { + sendaddress[doomcom->numnodes].sin_addr.s_addr + = inet_addr(myargv[i] + 1); + } + else + { + hostentry = gethostbyname(myargv[i]); + if (!hostentry) + { + //I_Error("Error: gethostbyname: couldn't find %s", myargv[i]); + + doom_strcpy(error_buf, "Error: gethostbyname: couldn't find "); + doom_concat(error_buf, myargv[i]); + I_Error(error_buf); + } + sendaddress[doomcom->numnodes].sin_addr.s_addr + = *(int*)hostentry->h_addr_list[0]; + } + doomcom->numnodes++; + } + + doomcom->id = DOOMCOM_ID; + doomcom->numplayers = doomcom->numnodes; + + // build message to receive + insocket = UDPsocket(); + BindToLocalPort(insocket, htons(DOOMPORT)); +#if defined(DOOM_WIN32) + ioctlsocket(insocket, FIONBIO, &trueval); +#else + ioctl(insocket, FIONBIO, &trueval); +#endif + + sendsocket = UDPsocket(); +#endif +} + + +void I_NetCmd(void) +{ +#if defined(I_NET_ENABLED) + if (doomcom->command == CMD_SEND) + { + netsend(); + } + else if (doomcom->command == CMD_GET) + { + netget(); + } + else + { + //I_Error("Error: Bad net cmd: %i\n", doomcom->command); + + doom_strcpy(error_buf, "Error: Bad net cmd: "); + doom_concat(error_buf, doom_itoa(doomcom->command, 10)); + I_Error(error_buf); + } +#endif +} +#define SAMPLECOUNT 512 +#define NUM_CHANNELS 8 +// It is 2 for 16bit, and 2 for two channels. +#define BUFMUL 4 +#define MIXBUFFERSIZE (SAMPLECOUNT*BUFMUL) + +#define SAMPLERATE 11025 // Hz +#define SAMPLESIZE 2 // 16bit + +#define MAX_QUEUED_MIDI_MSGS 256 + +#define EVENT_RELEASE_NOTE 0 +#define EVENT_PLAY_NOTE 1 +#define EVENT_PITCH_BEND 2 +#define EVENT_SYSTEM_EVENT 3 +#define EVENT_CONTROLLER 4 +#define EVENT_END_OF_MEASURE 5 +#define EVENT_FINISH 6 +#define EVENT_UNUSED 7 + +#define CONTROLLER_EVENT_ALL_SOUNDS_OFF 10 +#define CONTROLLER_EVENT_ALL_NOTES_OFF 11 +#define CONTROLLER_EVENT_MONO 12 +#define CONTROLLER_EVENT_POLY 13 +#define CONTROLLER_EVENT_RESET_ALL_CONTROLLERS 14 +#define CONTROLLER_EVENT_EVENT 15 + +#define CONTROLLER_CHANGE_INSTRUMENT 0 +#define CONTROLLER_BANK_SELECT 1 +#define CONTROLLER_MODULATION 2 +#define CONTROLLER_VOLUME 3 +#define CONTROLLER_PAN 4 +#define CONTROLLER_EXPRESSION 5 +#define CONTROLLER_REVERB 6 +#define CONTROLLER_CHORUS 7 +#define CONTROLLER_SUSTAIN 8 +#define CONTROLLER_SOFT 9 + + +typedef struct +{ + char ID[4]; + unsigned short scoreLen; + unsigned short scoreStart; + unsigned short channels; + unsigned short sec_channels; + unsigned short instrCnt; + unsigned short dummy; +} mus_header_t; + + +// A quick hack to establish a protocol between +// synchronous mix buffer updates and asynchronous +// audio writes. Probably redundant with gametic. +static int flag = 0; + +static unsigned char* mus_data = 0; +static mus_header_t mus_header; +static int mus_offset = 0; +static int mus_delay = 0; +static doom_boolean mus_loop = false; +static doom_boolean mus_playing = false; +static int mus_volume = 127; +static int mus_channel_volumes[16] = { 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 }; + +static int looping = 0; +static int musicdies = -1; + + +// The number of internal mixing channels, +// the samples calculated for each mixing step, +// the size of the 16bit, 2 hardware channel (stereo) +// mixing buffer, and the samplerate of the raw data. + +// The actual lengths of all sound effects. +int lengths[NUMSFX]; + +// The actual output device. +int audio_fd; + +// The global mixing buffer. +// Basically, samples from all active internal channels +// are modifed and added, and stored in the buffer +// that is submitted to the audio device. +signed short mixbuffer[MIXBUFFERSIZE]; + + +// The channel step amount... +unsigned int channelstep[NUM_CHANNELS]; +// ... and a 0.16 bit remainder of last step. +unsigned int channelstepremainder[NUM_CHANNELS]; + +// The channel data pointers, start and end. +unsigned char* channels[NUM_CHANNELS]; +unsigned char* channelsend[NUM_CHANNELS]; + +// Time/gametic that the channel started playing, +// used to determine oldest, which automatically +// has lowest priority. +// In case number of active sounds exceeds +// available channels. +int channelstart[NUM_CHANNELS]; + +// The sound in channel handles, +// determined on registration, +// might be used to unregister/stop/modify, +// currently unused. +int channelhandles[NUM_CHANNELS]; + +// SFX id of the playing sound effect. +// Used to catch duplicates (like chainsaw). +int channelids[NUM_CHANNELS]; + +// Pitch to stepping lookup, unused. +int steptable[256]; + +// Volume lookups. +int vol_lookup[128 * 256]; + +// Hardware left and right channel volume lookup. +int* channelleftvol_lookup[NUM_CHANNELS]; +int* channelrightvol_lookup[NUM_CHANNELS]; + +unsigned long queued_midi_msgs[MAX_QUEUED_MIDI_MSGS]; +int queue_midi_head = 0; +int queue_midi_tail = 0; + + +void TickSong(); + + +// +// This function loads the sound data from the WAD lump, +// for single sound. +// +void* getsfx(char* sfxname, int* len) +{ + unsigned char* sfx; + unsigned char* paddedsfx; + int i; + int size; + int paddedsize; + char name[20]; + int sfxlump; + + // Get the sound data from the WAD, allocate lump + // in zone memory. + //doom_sprintf(name, "ds%s", sfxname); + doom_strcpy(name, "ds"); + doom_concat(name, sfxname); + + // Now, there is a severe problem with the + // sound handling, in it is not (yet/anymore) + // gamemode aware. That means, sounds from + // DOOM II will be requested even with DOOM + // shareware. + // The sound list is wired into sounds.c, + // which sets the external variable. + // I do not do runtime patches to that + // variable. Instead, we will use a + // default sound for replacement. + if (W_CheckNumForName(name) == -1) + sfxlump = W_GetNumForName("dspistol"); + else + sfxlump = W_GetNumForName(name); + + size = W_LumpLength(sfxlump); + + sfx = (unsigned char*)W_CacheLumpNum(sfxlump, PU_STATIC); + + // Pads the sound effect out to the mixing buffer size. + // The original realloc would interfere with zone memory. + paddedsize = ((size - 8 + (SAMPLECOUNT - 1)) / SAMPLECOUNT) * SAMPLECOUNT; + + // Allocate from zone memory. + paddedsfx = (unsigned char*)Z_Malloc(paddedsize + 8, PU_STATIC, 0); + // ddt: (unsigned char *) realloc(sfx, paddedsize+8); + // This should interfere with zone memory handling, + // which does not kick in in the soundserver. + + // Now copy and pad. + doom_memcpy(paddedsfx, sfx, size); + for (i = size; i < paddedsize + 8; i++) + paddedsfx[i] = 128; + + // Remove the cached lump. + Z_Free(sfx); + + // Preserve padded length. + *len = paddedsize; + + // Return allocated padded data. + return (void*)(paddedsfx + 8); +} + + +// +// This function adds a sound to the +// list of currently active sounds, +// which is maintained as a given number +// (eight, usually) of internal channels. +// Returns a handle. +// +int addsfx(int sfxid, int volume, int step, int seperation) +{ + static unsigned short handlenums = 0; + + int i; + int rc = -1; + + int oldest = gametic; + int oldestnum = 0; + int slot; + + int rightvol; + int leftvol; + + // Chainsaw troubles. + // Play these sound effects only one at a time. + if (sfxid == sfx_sawup + || sfxid == sfx_sawidl + || sfxid == sfx_sawful + || sfxid == sfx_sawhit + || sfxid == sfx_stnmov + || sfxid == sfx_pistol) + { + // Loop all channels, check. + for (i = 0; i < NUM_CHANNELS; i++) + { + // Active, and using the same SFX? + if ((channels[i]) + && (channelids[i] == sfxid)) + { + // Reset. + channels[i] = 0; + // We are sure that iff, + // there will only be one. + break; + } + } + } + + // Loop all channels to find oldest SFX. + for (i = 0; (i < NUM_CHANNELS) && (channels[i]); i++) + { + if (channelstart[i] < oldest) + { + oldestnum = i; + oldest = channelstart[i]; + } + } + + // Tales from the cryptic. + // If we found a channel, fine. + // If not, we simply overwrite the first one, 0. + // Probably only happens at startup. + if (i == NUM_CHANNELS) + slot = oldestnum; + else + slot = i; + + // Okay, in the less recent channel, + // we will handle the new SFX. + // Set pointer to raw data. + channels[slot] = (unsigned char*)S_sfx[sfxid].data; + // Set pointer to end of raw data. + channelsend[slot] = channels[slot] + lengths[sfxid]; + + // Reset current handle number, limited to 0..100. + if (!handlenums) + handlenums = 100; + + // Assign current handle number. + // Preserved so sounds could be stopped (unused). + channelhandles[slot] = rc = handlenums++; + + // Set stepping??? + // Kinda getting the impression this is never used. + channelstep[slot] = step; + // ??? + channelstepremainder[slot] = 0; + // Should be gametic, I presume. + channelstart[slot] = gametic; + + // Separation, that is, orientation/stereo. + // range is: 1 - 256 + seperation += 1; + + // Per left/right channel. + // x^2 seperation, + // adjust volume properly. + leftvol = + volume - ((volume * seperation * seperation) >> 16); ///(256*256); + seperation = seperation - 257; + rightvol = + volume - ((volume * seperation * seperation) >> 16); + + // Sanity check, clamp volume. + if (rightvol < 0 || rightvol > 127) + I_Error("Error: rightvol out of bounds"); + + if (leftvol < 0 || leftvol > 127) + I_Error("Error: leftvol out of bounds"); + + // Get the proper lookup table piece + // for this volume level??? + channelleftvol_lookup[slot] = &vol_lookup[leftvol * 256]; + channelrightvol_lookup[slot] = &vol_lookup[rightvol * 256]; + + // Preserve sound SFX id, + // e.g. for avoiding duplicates of chainsaw. + channelids[slot] = sfxid; + + // You tell me. + return rc; +} + + +// +// SFX API +// Note: this was called by S_Init. +// However, whatever they did in the +// old DPMS based DOS version, this +// were simply dummies in the Linux +// version. +// See soundserver initdata(). +// +void I_SetChannels() +{ + // Init internal lookups (raw data, mixing buffer, channels). + // This function sets up internal lookups used during + // the mixing process. + int i; + int j; + + int* steptablemid = steptable + 128; + + // Okay, reset internal mixing channels to zero. + /*for (i=0; iname); + doom_strcpy(namebuf, "ds"); + doom_concat(namebuf, sfx->name); + return W_GetNumForName(namebuf); +} + + +// +// Starting a sound means adding it +// to the current list of active sounds +// in the internal channels. +// As the SFX info struct contains +// e.g. a pointer to the raw data, +// it is ignored. +// As our sound handling does not handle +// priority, it is ignored. +// Pitching (that is, increased speed of playback) +// is set, but currently not used by mixing. +// +int I_StartSound(int id, int vol, int sep, int pitch, int priority) +{ + // Returns a handle (not used). + id = addsfx(id, vol, steptable[pitch], sep); + return id; +} + + +void I_StopSound(int handle) +{ + // You need the handle returned by StartSound. + // Would be looping all channels, + // tracking down the handle, + // an setting the channel to zero. +} + + +int I_SoundIsPlaying(int handle) +{ + // Ouch. + return gametic < handle; +} + + +// +// This function loops all active (internal) sound +// channels, retrieves a given number of samples +// from the raw sound data, modifies it according +// to the current (internal) channel parameters, +// mixes the per channel samples into the global +// mixbuffer, clamping it to the allowed range, +// and sets up everything for transferring the +// contents of the mixbuffer to the (two) +// hardware channels (left and right, that is). +// +// This function currently supports only 16bit. +// +void I_UpdateSound(void) +{ + static int song_tick_progress = 0; + + // Mix current sound data. + // Data, from raw sound, for right and left. + register unsigned int sample; + register int dl; + register int dr; + + // Pointers in global mixbuffer, left, right, end. + signed short* leftout; + signed short* rightout; + signed short* leftend; + // Step in mixbuffer, left and right, thus two. + int step; + + // Mixing channel index. + int chan; + + // Do music first. [dsl] TODO: if we have embedded synth + //static int song_progress = 0; + //song_progress += 512; + //while (song_progress > 0) + //{ + // TickSong(); // About 7 music ticks per sound sampling (X for doubt) + // song_progress -= 78; + //} + + // Left and right channel + // are in global mixbuffer, alternating. + leftout = mixbuffer; + rightout = mixbuffer + 1; + step = 2; + + // Determine end, for left channel only + // (right channel is implicit). + leftend = mixbuffer + SAMPLECOUNT * step; + + // Mix sounds into the mixing buffer. + // Loop over step*SAMPLECOUNT, + // that is 512 values for two channels. + while (leftout != leftend) + { + // Reset left/right value. + dl = 0; + dr = 0; + + // Love thy L2 chache - made this a loop. + // Now more channels could be set at compile time + // as well. Thus loop those channels. + for (chan = 0; chan < NUM_CHANNELS; chan++) + { + // Check channel, if active. + if (channels[chan]) + { + // Get the raw data from the channel. + sample = *channels[chan]; + // Add left and right part + // for this channel (sound) + // to the current data. + // Adjust volume accordingly. + dl += channelleftvol_lookup[chan][sample]; + dr += channelrightvol_lookup[chan][sample]; + // Increment index ??? + channelstepremainder[chan] += channelstep[chan]; + // MSB is next sample??? + channels[chan] += channelstepremainder[chan] >> 16; + // Limit to LSB??? + channelstepremainder[chan] &= 65536 - 1; + + // Check whether we are done. + if (channels[chan] >= channelsend[chan]) + channels[chan] = 0; + } + } + + // Clamp to range. Left hardware channel. + // Has been char instead of short. + // if (dl > 127) *leftout = 127; + // else if (dl < -128) *leftout = -128; + // else *leftout = dl; + + if (dl > 0x7fff) + *leftout = 0x7fff; + else if (dl < -0x8000) + *leftout = -0x8000; + else + *leftout = dl; + + // Same for right hardware channel. + if (dr > 0x7fff) + *rightout = 0x7fff; + else if (dr < -0x8000) + *rightout = -0x8000; + else + *rightout = dr; + + // Increment current pointers in mixbuffer. + leftout += step; + rightout += step; + } +} + + +//extern doom_sound_callbacks_t doom_sound_callbacks; +//extern doom_boolean skip_next_sound_update; + +// +// This would be used to write out the mixbuffer +// during each game loop update. +// Updates sound buffer and audio device at runtime. +// It is called during Timer interrupt with SNDINTR. +// Mixing now done synchronous, and +// only output be done asynchronous? +// +void I_SubmitSound(void) +{ +} + + +void I_UpdateSoundParams(int handle, int vol, int sep, int pitch) +{ + // I fail too see that this is used. + // Would be using the handle to identify + // on which channel the sound might be active, + // and resetting the channel parameters. +} + + +void I_ShutdownSound(void) +{ + // Wait till all pending sounds are finished. + int done = 0; + int i; + + // FIXME (below). + doom_print("I_ShutdownSound: NOT finishing pending sounds\n"); + + while (!done) + { + for (i = 0; i < 8 && !channels[i]; i++); + + // FIXME. No proper channel output. + //if (i==8) + done = 1; + } + + // Done. + return; +} + + +void I_InitSound() +{ + int i; + + // Secure and configure sound device first. + doom_print("I_InitSound: "); + + // Initialize external data (all sounds) at start, keep static. + doom_print("I_InitSound: "); + + for (i = 1; i < NUMSFX; i++) + { + // Alias? Example is the chaingun sound linked to pistol. + if (!S_sfx[i].link) + { + // Load data from WAD file. + S_sfx[i].data = getsfx(S_sfx[i].name, &lengths[i]); + } + else + { + // Previously loaded already? + S_sfx[i].data = S_sfx[i].link->data; + lengths[i] = lengths[(S_sfx[i].link - S_sfx) / sizeof(sfxinfo_t)]; + } + } + + doom_print(" pre-cached all sound data\n"); + + // Now initialize mixbuffer with zero. + for (i = 0; i < MIXBUFFERSIZE; i++) + mixbuffer[i] = 0; + + // Finished initialization. + doom_print("I_InitSound: sound module ready\n"); +} + + +// +// MUSIC API. +// +void I_InitMusic(void) +{ +} + + +void I_ShutdownMusic(void) +{ +} + + +void I_PlaySong(int handle, int looping) +{ + musicdies = gametic + TICRATE * 30; + + mus_loop = looping ? true : false; + mus_playing = true; +} + + +void I_PauseSong(int handle) +{ + mus_playing = false; +} + + +void I_ResumeSong(int handle) +{ + if (mus_data) mus_playing = true; +} + + +static void reset_all_channels() +{ + for (int i = 0; i < 16; ++i) + queued_midi_msgs[(queue_midi_tail++) % MAX_QUEUED_MIDI_MSGS] = 0b10110000 | i | (123 << 8); +} + + +void I_StopSong(int handle) +{ + mus_data = 0; + mus_delay = 0; + mus_offset = 0; + mus_playing = false; + + reset_all_channels(); +} + + +void I_UnRegisterSong(int handle) +{ + I_StopSong(handle); +} + + +int I_RegisterSong(void* data) +{ + doom_memcpy(&mus_header, data, sizeof(mus_header_t)); + if (doom_strncmp(mus_header.ID, "MUS", 3) != 0 || mus_header.ID[3] != 0x1A) return 0; + + mus_data = (unsigned char*)data; + mus_delay = 0; + mus_offset = mus_header.scoreStart; + mus_playing = false; + + return 1; +} + + +// Is the song playing? +int I_QrySongPlaying(int handle) +{ + return mus_playing; +} + + +unsigned long I_TickSong() +{ + unsigned long midi_event = 0; + + // Dequeue MIDI events + if (queue_midi_head != queue_midi_tail) + return queued_midi_msgs[(queue_midi_head++) % MAX_QUEUED_MIDI_MSGS]; + + if (!mus_playing || !mus_data) return 0; + + if (mus_delay <= 0) + { + int event = (int)mus_data[mus_offset++]; + int type = (event & 0b01110000) >> 4; + int channel = event & 0b00001111; + + if (channel == 15) channel = 9; // Percussion is 9 on GM + else if (channel == 9) channel = 15; + + switch (type) + { + case EVENT_RELEASE_NOTE: + { + int note = (int)mus_data[mus_offset++] & 0b01111111; + midi_event = (0x00000080 | channel | (note << 8)); + break; + } + case EVENT_PLAY_NOTE: + { + int note_bytes = (int)mus_data[mus_offset++]; + int note = note_bytes & 0b01111111; + int vol = 127; + if (note_bytes & 0b10000000) vol = (int)mus_data[mus_offset++] & 0b01111111; + midi_event = (0x00000090 | channel | (note << 8) | (vol << 16)); + break; + } + case EVENT_PITCH_BEND: + { + int bend_amount = (int)mus_data[mus_offset++] * 64; + int l = bend_amount & 0b01111111; + int m = (bend_amount & 0b1111111110000000) >> 7; + midi_event = (0x000000E0 | channel | (l << 8) | (m << 16)); + break; + } + case EVENT_SYSTEM_EVENT: + { + int controller = (int)mus_data[mus_offset++] & 0b01111111; + switch (controller) + { + case CONTROLLER_EVENT_ALL_SOUNDS_OFF: + midi_event = (0x000000B0 | channel | (120 << 8)); + break; + case CONTROLLER_EVENT_ALL_NOTES_OFF: + midi_event = (0x000000B0 | channel | (123 << 8)); + break; + case CONTROLLER_EVENT_MONO: + midi_event = (0x000000B0 | channel | (126 << 8)); + break; + case CONTROLLER_EVENT_POLY: + midi_event = (0x000000B0 | channel | (127 << 8)); + break; + case CONTROLLER_EVENT_RESET_ALL_CONTROLLERS: + midi_event = (0x000000B0 | channel | (121 << 8)); + break; + case CONTROLLER_EVENT_EVENT: // Doom never implemented + break; + } + break; + } + case EVENT_CONTROLLER: + { + int controller = (int)mus_data[mus_offset++] & 0b01111111; + int value = (int)mus_data[mus_offset++] & 0b01111111; + switch (controller) + { + case CONTROLLER_CHANGE_INSTRUMENT: + midi_event = (0x000000C0 | channel | (value << 8)); + break; + case CONTROLLER_BANK_SELECT: + midi_event = (0x000000B0 | channel | 0x2000 | (value << 16)); + break; + case CONTROLLER_MODULATION: + midi_event = (0x000000B0 | channel | 0x0100 | (value << 16)); + break; + case CONTROLLER_VOLUME: + mus_channel_volumes[channel] = value; + midi_event = (0x000000B0 | channel | 0x0700 | (((mus_channel_volumes[channel] * mus_volume) / 127) << 16)); + break; + case CONTROLLER_PAN: + midi_event = (0x000000B0 | channel | 0x0A00 | (value << 16)); + break; + case CONTROLLER_EXPRESSION: + midi_event = (0x000000B0 | channel | 0x0B00 | (value << 16)); + break; + case CONTROLLER_REVERB: + midi_event = (0x000000B0 | channel | 0x5B00 | (value << 16)); + break; + case CONTROLLER_CHORUS: + midi_event = (0x000000B0 | channel | 0x5D00 | (value << 16)); + break; + case CONTROLLER_SUSTAIN: + midi_event = (0x000000B0 | channel | 0x4000 | (value << 16)); + break; + case CONTROLLER_SOFT: + midi_event = (0x000000B0 | channel | 0x4300 | (value << 16)); + break; + } + break; + } + case EVENT_END_OF_MEASURE: + { + break; + } + case EVENT_FINISH: + { + // Loop + if (mus_loop) + { + mus_delay = 0; + mus_offset = mus_header.scoreStart; + break; + } + else + { + mus_playing = false; + return 0; + } + } + case EVENT_UNUSED: + { + int dummy = (int)mus_data[mus_offset++]; + break; + } + } + + if (event & 0b10000000) // Followed by delay + { + mus_delay = 0; + int delay_byte = 0; + do + { + delay_byte = mus_data[mus_offset++]; + mus_delay = mus_delay * 128 + delay_byte & 0b01111111; + } while (delay_byte & 0b10000000); + + return midi_event; + } + } + + mus_delay--; + + return midi_event; +} +int mb_used = 6 * (sizeof(void*) / 4); +ticcmd_t emptycmd; + + +extern doom_boolean demorecording; + + +void I_Tactile(int on, int off, int total) +{ +} + + +ticcmd_t* I_BaseTiccmd(void) +{ + return &emptycmd; +} + + +int I_GetHeapSize(void) +{ + return mb_used * 1024 * 1024; +} + + +byte* I_ZoneBase(int* size) +{ + *size = mb_used * 1024 * 1024; + return (byte*)doom_malloc(*size); +} + + +// +// I_GetTime +// returns time in 1/70th second tics +// +int I_GetTime(void) +{ + int sec, usec; + int newtics; + static int basetime = 0; + + doom_gettime(&sec, &usec); + if (!basetime) + basetime = sec; + newtics = (sec - basetime) * TICRATE + usec * TICRATE / 1000000; + return newtics; +} + + +// +// I_Init +// +void I_Init(void) +{ + I_InitSound(); +} + + +// +// I_Quit +// +void I_Quit(void) +{ + D_QuitNetGame(); + I_ShutdownSound(); + I_ShutdownMusic(); + M_SaveDefaults(); + I_ShutdownGraphics(); + doom_exit(0); +} + + +void I_WaitVBL(int count) +{ +#if 0 // [pd] Never sleep in main thread +#ifdef SGI + sginap(1); +#else +#ifdef SUN + sleep(0); +#else + usleep(count * (1000000 / 70)); +#endif +#endif +#endif +} + + +void I_BeginRead(void) +{ +} + + +void I_EndRead(void) +{ +} + + +byte* I_AllocLow(int length) +{ + byte* mem; + + mem = (byte*)doom_malloc(length); + doom_memset(mem, 0, length); + return mem; +} + + +// +// I_Error +// +void I_Error(char* error) +{ + // Message first. + if (error) doom_print(error); + doom_print("\n"); + + // Shutdown. Here might be other errors. + if (demorecording) + G_CheckDemoStatus(); + + D_QuitNetGame(); + I_ShutdownGraphics(); + + doom_exit(-1); +} +#define POINTER_WARP_COUNTDOWN 1 + + +// Fake mouse handling. +// This cannot work properly w/o DGA. +// Needs an invisible mouse cursor at least. +doom_boolean grabMouse; +int doPointerWarp = POINTER_WARP_COUNTDOWN; + +unsigned char screen_palette[256 * 3]; + +doom_boolean mousemoved = false; +doom_boolean shmFinished; + + +// Blocky mode, +// replace each 320x200 pixel with multiply*multiply pixels. +// According to Dave Taylor, it still is a bonehead thing +// to use .... +static int multiply = 1; +static int lastmousex = 0; +static int lastmousey = 0; + + +void I_ShutdownGraphics(void) +{ +} + + +// +// I_StartFrame +// +void I_StartFrame(void) +{ +} + + +void I_GetEvent(void) +{ +} + + +// +// I_StartTic +// +void I_StartTic(void) +{ +} + + +// +// I_UpdateNoBlit +// +void I_UpdateNoBlit(void) +{ + // what is this? +} + + +// +// I_FinishUpdate +// +void I_FinishUpdate(void) +{ + static int lasttic; + int tics; + int i; + + // draws little dots on the bottom of the screen + if (devparm) + { + i = I_GetTime(); + tics = i - lasttic; + lasttic = i; + if (tics > 20) tics = 20; + + for (i = 0; i < tics * 2; i += 2) + screens[0][(SCREENHEIGHT - 1) * SCREENWIDTH + i] = 0xff; + for (; i < 20 * 2; i += 2) + screens[0][(SCREENHEIGHT - 1) * SCREENWIDTH + i] = 0x0; + + } +} + + +// +// I_ReadScreen +// +void I_ReadScreen(byte* scr) +{ + doom_memcpy(scr, screens[0], SCREENWIDTH * SCREENHEIGHT); +} + + +// +// I_SetPalette +// +void I_SetPalette(byte* palette) +{ + doom_memcpy(screen_palette, palette, 256 * 3); +} + + +void I_InitGraphics(void) +{ + screens[0] = (unsigned char*)doom_malloc(SCREENWIDTH * SCREENHEIGHT); +} +char* sprnames[NUMSPRITES + 1] = { + "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",0 +}; + + +void A_Light0(); +void A_WeaponReady(); +void A_Lower(); +void A_Raise(); +void A_Punch(); +void A_ReFire(); +void A_FirePistol(); +void A_Light1(); +void A_FireShotgun(); +void A_Light2(); +void A_FireShotgun2(); +void A_CheckReload(); +void A_OpenShotgun2(); +void A_LoadShotgun2(); +void A_CloseShotgun2(); +void A_FireCGun(); +void A_GunFlash(); +void A_FireMissile(); +void A_Saw(); +void A_FirePlasma(); +void A_BFGsound(); +void A_FireBFG(); +void A_BFGSpray(); +void A_Explode(); +void A_Pain(); +void A_PlayerScream(); +void A_Fall(); +void A_XScream(); +void A_Look(); +void A_Chase(); +void A_FaceTarget(); +void A_PosAttack(); +void A_Scream(); +void A_SPosAttack(); +void A_VileChase(); +void A_VileStart(); +void A_VileTarget(); +void A_VileAttack(); +void A_StartFire(); +void A_Fire(); +void A_FireCrackle(); +void A_Tracer(); +void A_SkelWhoosh(); +void A_SkelFist(); +void A_SkelMissile(); +void A_FatRaise(); +void A_FatAttack1(); +void A_FatAttack2(); +void A_FatAttack3(); +void A_BossDeath(); +void A_CPosAttack(); +void A_CPosRefire(); +void A_TroopAttack(); +void A_SargAttack(); +void A_HeadAttack(); +void A_BruisAttack(); +void A_SkullAttack(); +void A_Metal(); +void A_SpidRefire(); +void A_BabyMetal(); +void A_BspiAttack(); +void A_Hoof(); +void A_CyberAttack(); +void A_PainAttack(); +void A_PainDie(); +void A_KeenDie(); +void A_BrainPain(); +void A_BrainScream(); +void A_BrainDie(); +void A_BrainAwake(); +void A_BrainSpit(); +void A_SpawnSound(); +void A_SpawnFly(); +void A_BrainExplode(); + + +state_t states[NUMSTATES] = { + {SPR_TROO,0,-1,{0},S_NULL,0,0}, // S_NULL + {SPR_SHTG,4,0,{(actionf_p1)A_Light0},S_NULL,0,0}, // S_LIGHTDONE + {SPR_PUNG,0,1,{(actionf_p1)A_WeaponReady},S_PUNCH,0,0}, // S_PUNCH + {SPR_PUNG,0,1,{(actionf_p1)A_Lower},S_PUNCHDOWN,0,0}, // S_PUNCHDOWN + {SPR_PUNG,0,1,{(actionf_p1)A_Raise},S_PUNCHUP,0,0}, // S_PUNCHUP + {SPR_PUNG,1,4,{0},S_PUNCH2,0,0}, // S_PUNCH1 + {SPR_PUNG,2,4,{(actionf_p1)A_Punch},S_PUNCH3,0,0}, // S_PUNCH2 + {SPR_PUNG,3,5,{0},S_PUNCH4,0,0}, // S_PUNCH3 + {SPR_PUNG,2,4,{0},S_PUNCH5,0,0}, // S_PUNCH4 + {SPR_PUNG,1,5,{(actionf_p1)A_ReFire},S_PUNCH,0,0}, // S_PUNCH5 + {SPR_PISG,0,1,{(actionf_p1)A_WeaponReady},S_PISTOL,0,0},// S_PISTOL + {SPR_PISG,0,1,{(actionf_p1)A_Lower},S_PISTOLDOWN,0,0}, // S_PISTOLDOWN + {SPR_PISG,0,1,{(actionf_p1)A_Raise},S_PISTOLUP,0,0}, // S_PISTOLUP + {SPR_PISG,0,4,{0},S_PISTOL2,0,0}, // S_PISTOL1 + {SPR_PISG,1,6,{(actionf_p1)A_FirePistol},S_PISTOL3,0,0},// S_PISTOL2 + {SPR_PISG,2,4,{0},S_PISTOL4,0,0}, // S_PISTOL3 + {SPR_PISG,1,5,{(actionf_p1)A_ReFire},S_PISTOL,0,0}, // S_PISTOL4 + {SPR_PISF,32768,7,{(actionf_p1)A_Light1},S_LIGHTDONE,0,0}, // S_PISTOLFLASH + {SPR_SHTG,0,1,{(actionf_p1)A_WeaponReady},S_SGUN,0,0}, // S_SGUN + {SPR_SHTG,0,1,{(actionf_p1)A_Lower},S_SGUNDOWN,0,0}, // S_SGUNDOWN + {SPR_SHTG,0,1,{(actionf_p1)A_Raise},S_SGUNUP,0,0}, // S_SGUNUP + {SPR_SHTG,0,3,{0},S_SGUN2,0,0}, // S_SGUN1 + {SPR_SHTG,0,7,{(actionf_p1)A_FireShotgun},S_SGUN3,0,0}, // S_SGUN2 + {SPR_SHTG,1,5,{0},S_SGUN4,0,0}, // S_SGUN3 + {SPR_SHTG,2,5,{0},S_SGUN5,0,0}, // S_SGUN4 + {SPR_SHTG,3,4,{0},S_SGUN6,0,0}, // S_SGUN5 + {SPR_SHTG,2,5,{0},S_SGUN7,0,0}, // S_SGUN6 + {SPR_SHTG,1,5,{0},S_SGUN8,0,0}, // S_SGUN7 + {SPR_SHTG,0,3,{0},S_SGUN9,0,0}, // S_SGUN8 + {SPR_SHTG,0,7,{(actionf_p1)A_ReFire},S_SGUN,0,0}, // S_SGUN9 + {SPR_SHTF,32768,4,{(actionf_p1)A_Light1},S_SGUNFLASH2,0,0}, // S_SGUNFLASH1 + {SPR_SHTF,32769,3,{(actionf_p1)A_Light2},S_LIGHTDONE,0,0}, // S_SGUNFLASH2 + {SPR_SHT2,0,1,{(actionf_p1)A_WeaponReady},S_DSGUN,0,0}, // S_DSGUN + {SPR_SHT2,0,1,{(actionf_p1)A_Lower},S_DSGUNDOWN,0,0}, // S_DSGUNDOWN + {SPR_SHT2,0,1,{(actionf_p1)A_Raise},S_DSGUNUP,0,0}, // S_DSGUNUP + {SPR_SHT2,0,3,{0},S_DSGUN2,0,0}, // S_DSGUN1 + {SPR_SHT2,0,7,{(actionf_p1)A_FireShotgun2},S_DSGUN3,0,0}, // S_DSGUN2 + {SPR_SHT2,1,7,{0},S_DSGUN4,0,0}, // S_DSGUN3 + {SPR_SHT2,2,7,{(actionf_p1)A_CheckReload},S_DSGUN5,0,0}, // S_DSGUN4 + {SPR_SHT2,3,7,{(actionf_p1)A_OpenShotgun2},S_DSGUN6,0,0}, // S_DSGUN5 + {SPR_SHT2,4,7,{0},S_DSGUN7,0,0}, // S_DSGUN6 + {SPR_SHT2,5,7,{(actionf_p1)A_LoadShotgun2},S_DSGUN8,0,0}, // S_DSGUN7 + {SPR_SHT2,6,6,{0},S_DSGUN9,0,0}, // S_DSGUN8 + {SPR_SHT2,7,6,{(actionf_p1)A_CloseShotgun2},S_DSGUN10,0,0}, // S_DSGUN9 + {SPR_SHT2,0,5,{(actionf_p1)A_ReFire},S_DSGUN,0,0}, // S_DSGUN10 + {SPR_SHT2,1,7,{0},S_DSNR2,0,0}, // S_DSNR1 + {SPR_SHT2,0,3,{0},S_DSGUNDOWN,0,0}, // S_DSNR2 + {SPR_SHT2,32776,5,{(actionf_p1)A_Light1},S_DSGUNFLASH2,0,0}, // S_DSGUNFLASH1 + {SPR_SHT2,32777,4,{(actionf_p1)A_Light2},S_LIGHTDONE,0,0}, // S_DSGUNFLASH2 + {SPR_CHGG,0,1,{(actionf_p1)A_WeaponReady},S_CHAIN,0,0}, // S_CHAIN + {SPR_CHGG,0,1,{(actionf_p1)A_Lower},S_CHAINDOWN,0,0}, // S_CHAINDOWN + {SPR_CHGG,0,1,{(actionf_p1)A_Raise},S_CHAINUP,0,0}, // S_CHAINUP + {SPR_CHGG,0,4,{(actionf_p1)A_FireCGun},S_CHAIN2,0,0}, // S_CHAIN1 + {SPR_CHGG,1,4,{(actionf_p1)A_FireCGun},S_CHAIN3,0,0}, // S_CHAIN2 + {SPR_CHGG,1,0,{(actionf_p1)A_ReFire},S_CHAIN,0,0}, // S_CHAIN3 + {SPR_CHGF,32768,5,{(actionf_p1)A_Light1},S_LIGHTDONE,0,0}, // S_CHAINFLASH1 + {SPR_CHGF,32769,5,{(actionf_p1)A_Light2},S_LIGHTDONE,0,0}, // S_CHAINFLASH2 + {SPR_MISG,0,1,{(actionf_p1)A_WeaponReady},S_MISSILE,0,0}, // S_MISSILE + {SPR_MISG,0,1,{(actionf_p1)A_Lower},S_MISSILEDOWN,0,0}, // S_MISSILEDOWN + {SPR_MISG,0,1,{(actionf_p1)A_Raise},S_MISSILEUP,0,0}, // S_MISSILEUP + {SPR_MISG,1,8,{(actionf_p1)A_GunFlash},S_MISSILE2,0,0}, // S_MISSILE1 + {SPR_MISG,1,12,{(actionf_p1)A_FireMissile},S_MISSILE3,0,0}, // S_MISSILE2 + {SPR_MISG,1,0,{(actionf_p1)A_ReFire},S_MISSILE,0,0}, // S_MISSILE3 + {SPR_MISF,32768,3,{(actionf_p1)A_Light1},S_MISSILEFLASH2,0,0}, // S_MISSILEFLASH1 + {SPR_MISF,32769,4,{0},S_MISSILEFLASH3,0,0}, // S_MISSILEFLASH2 + {SPR_MISF,32770,4,{(actionf_p1)A_Light2},S_MISSILEFLASH4,0,0}, // S_MISSILEFLASH3 + {SPR_MISF,32771,4,{(actionf_p1)A_Light2},S_LIGHTDONE,0,0}, // S_MISSILEFLASH4 + {SPR_SAWG,2,4,{(actionf_p1)A_WeaponReady},S_SAWB,0,0}, // S_SAW + {SPR_SAWG,3,4,{(actionf_p1)A_WeaponReady},S_SAW,0,0}, // S_SAWB + {SPR_SAWG,2,1,{(actionf_p1)A_Lower},S_SAWDOWN,0,0}, // S_SAWDOWN + {SPR_SAWG,2,1,{(actionf_p1)A_Raise},S_SAWUP,0,0}, // S_SAWUP + {SPR_SAWG,0,4,{(actionf_p1)A_Saw},S_SAW2,0,0}, // S_SAW1 + {SPR_SAWG,1,4,{(actionf_p1)A_Saw},S_SAW3,0,0}, // S_SAW2 + {SPR_SAWG,1,0,{(actionf_p1)A_ReFire},S_SAW,0,0}, // S_SAW3 + {SPR_PLSG,0,1,{(actionf_p1)A_WeaponReady},S_PLASMA,0,0}, // S_PLASMA + {SPR_PLSG,0,1,{(actionf_p1)A_Lower},S_PLASMADOWN,0,0}, // S_PLASMADOWN + {SPR_PLSG,0,1,{(actionf_p1)A_Raise},S_PLASMAUP,0,0}, // S_PLASMAUP + {SPR_PLSG,0,3,{(actionf_p1)A_FirePlasma},S_PLASMA2,0,0}, // S_PLASMA1 + {SPR_PLSG,1,20,{(actionf_p1)A_ReFire},S_PLASMA,0,0}, // S_PLASMA2 + {SPR_PLSF,32768,4,{(actionf_p1)A_Light1},S_LIGHTDONE,0,0}, // S_PLASMAFLASH1 + {SPR_PLSF,32769,4,{(actionf_p1)A_Light1},S_LIGHTDONE,0,0}, // S_PLASMAFLASH2 + {SPR_BFGG,0,1,{(actionf_p1)A_WeaponReady},S_BFG,0,0}, // S_BFG + {SPR_BFGG,0,1,{(actionf_p1)A_Lower},S_BFGDOWN,0,0}, // S_BFGDOWN + {SPR_BFGG,0,1,{(actionf_p1)A_Raise},S_BFGUP,0,0}, // S_BFGUP + {SPR_BFGG,0,20,{(actionf_p1)A_BFGsound},S_BFG2,0,0}, // S_BFG1 + {SPR_BFGG,1,10,{(actionf_p1)A_GunFlash},S_BFG3,0,0}, // S_BFG2 + {SPR_BFGG,1,10,{(actionf_p1)A_FireBFG},S_BFG4,0,0}, // S_BFG3 + {SPR_BFGG,1,20,{(actionf_p1)A_ReFire},S_BFG,0,0}, // S_BFG4 + {SPR_BFGF,32768,11,{(actionf_p1)A_Light1},S_BFGFLASH2,0,0}, // S_BFGFLASH1 + {SPR_BFGF,32769,6,{(actionf_p1)A_Light2},S_LIGHTDONE,0,0}, // S_BFGFLASH2 + {SPR_BLUD,2,8,{0},S_BLOOD2,0,0}, // S_BLOOD1 + {SPR_BLUD,1,8,{0},S_BLOOD3,0,0}, // S_BLOOD2 + {SPR_BLUD,0,8,{0},S_NULL,0,0}, // S_BLOOD3 + {SPR_PUFF,32768,4,{0},S_PUFF2,0,0}, // S_PUFF1 + {SPR_PUFF,1,4,{0},S_PUFF3,0,0}, // S_PUFF2 + {SPR_PUFF,2,4,{0},S_PUFF4,0,0}, // S_PUFF3 + {SPR_PUFF,3,4,{0},S_NULL,0,0}, // S_PUFF4 + {SPR_BAL1,32768,4,{0},S_TBALL2,0,0}, // S_TBALL1 + {SPR_BAL1,32769,4,{0},S_TBALL1,0,0}, // S_TBALL2 + {SPR_BAL1,32770,6,{0},S_TBALLX2,0,0}, // S_TBALLX1 + {SPR_BAL1,32771,6,{0},S_TBALLX3,0,0}, // S_TBALLX2 + {SPR_BAL1,32772,6,{0},S_NULL,0,0}, // S_TBALLX3 + {SPR_BAL2,32768,4,{0},S_RBALL2,0,0}, // S_RBALL1 + {SPR_BAL2,32769,4,{0},S_RBALL1,0,0}, // S_RBALL2 + {SPR_BAL2,32770,6,{0},S_RBALLX2,0,0}, // S_RBALLX1 + {SPR_BAL2,32771,6,{0},S_RBALLX3,0,0}, // S_RBALLX2 + {SPR_BAL2,32772,6,{0},S_NULL,0,0}, // S_RBALLX3 + {SPR_PLSS,32768,6,{0},S_PLASBALL2,0,0}, // S_PLASBALL + {SPR_PLSS,32769,6,{0},S_PLASBALL,0,0}, // S_PLASBALL2 + {SPR_PLSE,32768,4,{0},S_PLASEXP2,0,0}, // S_PLASEXP + {SPR_PLSE,32769,4,{0},S_PLASEXP3,0,0}, // S_PLASEXP2 + {SPR_PLSE,32770,4,{0},S_PLASEXP4,0,0}, // S_PLASEXP3 + {SPR_PLSE,32771,4,{0},S_PLASEXP5,0,0}, // S_PLASEXP4 + {SPR_PLSE,32772,4,{0},S_NULL,0,0}, // S_PLASEXP5 + {SPR_MISL,32768,1,{0},S_ROCKET,0,0}, // S_ROCKET + {SPR_BFS1,32768,4,{0},S_BFGSHOT2,0,0}, // S_BFGSHOT + {SPR_BFS1,32769,4,{0},S_BFGSHOT,0,0}, // S_BFGSHOT2 + {SPR_BFE1,32768,8,{0},S_BFGLAND2,0,0}, // S_BFGLAND + {SPR_BFE1,32769,8,{0},S_BFGLAND3,0,0}, // S_BFGLAND2 + {SPR_BFE1,32770,8,{(actionf_p1)A_BFGSpray},S_BFGLAND4,0,0}, // S_BFGLAND3 + {SPR_BFE1,32771,8,{0},S_BFGLAND5,0,0}, // S_BFGLAND4 + {SPR_BFE1,32772,8,{0},S_BFGLAND6,0,0}, // S_BFGLAND5 + {SPR_BFE1,32773,8,{0},S_NULL,0,0}, // S_BFGLAND6 + {SPR_BFE2,32768,8,{0},S_BFGEXP2,0,0}, // S_BFGEXP + {SPR_BFE2,32769,8,{0},S_BFGEXP3,0,0}, // S_BFGEXP2 + {SPR_BFE2,32770,8,{0},S_BFGEXP4,0,0}, // S_BFGEXP3 + {SPR_BFE2,32771,8,{0},S_NULL,0,0}, // S_BFGEXP4 + {SPR_MISL,32769,8,{(actionf_p1)A_Explode},S_EXPLODE2,0,0}, // S_EXPLODE1 + {SPR_MISL,32770,6,{0},S_EXPLODE3,0,0}, // S_EXPLODE2 + {SPR_MISL,32771,4,{0},S_NULL,0,0}, // S_EXPLODE3 + {SPR_TFOG,32768,6,{0},S_TFOG01,0,0}, // S_TFOG + {SPR_TFOG,32769,6,{0},S_TFOG02,0,0}, // S_TFOG01 + {SPR_TFOG,32768,6,{0},S_TFOG2,0,0}, // S_TFOG02 + {SPR_TFOG,32769,6,{0},S_TFOG3,0,0}, // S_TFOG2 + {SPR_TFOG,32770,6,{0},S_TFOG4,0,0}, // S_TFOG3 + {SPR_TFOG,32771,6,{0},S_TFOG5,0,0}, // S_TFOG4 + {SPR_TFOG,32772,6,{0},S_TFOG6,0,0}, // S_TFOG5 + {SPR_TFOG,32773,6,{0},S_TFOG7,0,0}, // S_TFOG6 + {SPR_TFOG,32774,6,{0},S_TFOG8,0,0}, // S_TFOG7 + {SPR_TFOG,32775,6,{0},S_TFOG9,0,0}, // S_TFOG8 + {SPR_TFOG,32776,6,{0},S_TFOG10,0,0}, // S_TFOG9 + {SPR_TFOG,32777,6,{0},S_NULL,0,0}, // S_TFOG10 + {SPR_IFOG,32768,6,{0},S_IFOG01,0,0}, // S_IFOG + {SPR_IFOG,32769,6,{0},S_IFOG02,0,0}, // S_IFOG01 + {SPR_IFOG,32768,6,{0},S_IFOG2,0,0}, // S_IFOG02 + {SPR_IFOG,32769,6,{0},S_IFOG3,0,0}, // S_IFOG2 + {SPR_IFOG,32770,6,{0},S_IFOG4,0,0}, // S_IFOG3 + {SPR_IFOG,32771,6,{0},S_IFOG5,0,0}, // S_IFOG4 + {SPR_IFOG,32772,6,{0},S_NULL,0,0}, // S_IFOG5 + {SPR_PLAY,0,-1,{0},S_NULL,0,0}, // S_PLAY + {SPR_PLAY,0,4,{0},S_PLAY_RUN2,0,0}, // S_PLAY_RUN1 + {SPR_PLAY,1,4,{0},S_PLAY_RUN3,0,0}, // S_PLAY_RUN2 + {SPR_PLAY,2,4,{0},S_PLAY_RUN4,0,0}, // S_PLAY_RUN3 + {SPR_PLAY,3,4,{0},S_PLAY_RUN1,0,0}, // S_PLAY_RUN4 + {SPR_PLAY,4,12,{0},S_PLAY,0,0}, // S_PLAY_ATK1 + {SPR_PLAY,32773,6,{0},S_PLAY_ATK1,0,0}, // S_PLAY_ATK2 + {SPR_PLAY,6,4,{0},S_PLAY_PAIN2,0,0}, // S_PLAY_PAIN + {SPR_PLAY,6,4,{(actionf_p1)A_Pain},S_PLAY,0,0}, // S_PLAY_PAIN2 + {SPR_PLAY,7,10,{0},S_PLAY_DIE2,0,0}, // S_PLAY_DIE1 + {SPR_PLAY,8,10,{(actionf_p1)A_PlayerScream},S_PLAY_DIE3,0,0}, // S_PLAY_DIE2 + {SPR_PLAY,9,10,{(actionf_p1)A_Fall},S_PLAY_DIE4,0,0}, // S_PLAY_DIE3 + {SPR_PLAY,10,10,{0},S_PLAY_DIE5,0,0}, // S_PLAY_DIE4 + {SPR_PLAY,11,10,{0},S_PLAY_DIE6,0,0}, // S_PLAY_DIE5 + {SPR_PLAY,12,10,{0},S_PLAY_DIE7,0,0}, // S_PLAY_DIE6 + {SPR_PLAY,13,-1,{0},S_NULL,0,0}, // S_PLAY_DIE7 + {SPR_PLAY,14,5,{0},S_PLAY_XDIE2,0,0}, // S_PLAY_XDIE1 + {SPR_PLAY,15,5,{(actionf_p1)A_XScream},S_PLAY_XDIE3,0,0}, // S_PLAY_XDIE2 + {SPR_PLAY,16,5,{(actionf_p1)A_Fall},S_PLAY_XDIE4,0,0}, // S_PLAY_XDIE3 + {SPR_PLAY,17,5,{0},S_PLAY_XDIE5,0,0}, // S_PLAY_XDIE4 + {SPR_PLAY,18,5,{0},S_PLAY_XDIE6,0,0}, // S_PLAY_XDIE5 + {SPR_PLAY,19,5,{0},S_PLAY_XDIE7,0,0}, // S_PLAY_XDIE6 + {SPR_PLAY,20,5,{0},S_PLAY_XDIE8,0,0}, // S_PLAY_XDIE7 + {SPR_PLAY,21,5,{0},S_PLAY_XDIE9,0,0}, // S_PLAY_XDIE8 + {SPR_PLAY,22,-1,{0},S_NULL,0,0}, // S_PLAY_XDIE9 + {SPR_POSS,0,10,{(actionf_p1)A_Look},S_POSS_STND2,0,0}, // S_POSS_STND + {SPR_POSS,1,10,{(actionf_p1)A_Look},S_POSS_STND,0,0}, // S_POSS_STND2 + {SPR_POSS,0,4,{(actionf_p1)A_Chase},S_POSS_RUN2,0,0}, // S_POSS_RUN1 + {SPR_POSS,0,4,{(actionf_p1)A_Chase},S_POSS_RUN3,0,0}, // S_POSS_RUN2 + {SPR_POSS,1,4,{(actionf_p1)A_Chase},S_POSS_RUN4,0,0}, // S_POSS_RUN3 + {SPR_POSS,1,4,{(actionf_p1)A_Chase},S_POSS_RUN5,0,0}, // S_POSS_RUN4 + {SPR_POSS,2,4,{(actionf_p1)A_Chase},S_POSS_RUN6,0,0}, // S_POSS_RUN5 + {SPR_POSS,2,4,{(actionf_p1)A_Chase},S_POSS_RUN7,0,0}, // S_POSS_RUN6 + {SPR_POSS,3,4,{(actionf_p1)A_Chase},S_POSS_RUN8,0,0}, // S_POSS_RUN7 + {SPR_POSS,3,4,{(actionf_p1)A_Chase},S_POSS_RUN1,0,0}, // S_POSS_RUN8 + {SPR_POSS,4,10,{(actionf_p1)A_FaceTarget},S_POSS_ATK2,0,0}, // S_POSS_ATK1 + {SPR_POSS,5,8,{(actionf_p1)A_PosAttack},S_POSS_ATK3,0,0}, // S_POSS_ATK2 + {SPR_POSS,4,8,{0},S_POSS_RUN1,0,0}, // S_POSS_ATK3 + {SPR_POSS,6,3,{0},S_POSS_PAIN2,0,0}, // S_POSS_PAIN + {SPR_POSS,6,3,{(actionf_p1)A_Pain},S_POSS_RUN1,0,0}, // S_POSS_PAIN2 + {SPR_POSS,7,5,{0},S_POSS_DIE2,0,0}, // S_POSS_DIE1 + {SPR_POSS,8,5,{(actionf_p1)A_Scream},S_POSS_DIE3,0,0}, // S_POSS_DIE2 + {SPR_POSS,9,5,{(actionf_p1)A_Fall},S_POSS_DIE4,0,0}, // S_POSS_DIE3 + {SPR_POSS,10,5,{0},S_POSS_DIE5,0,0}, // S_POSS_DIE4 + {SPR_POSS,11,-1,{0},S_NULL,0,0}, // S_POSS_DIE5 + {SPR_POSS,12,5,{0},S_POSS_XDIE2,0,0}, // S_POSS_XDIE1 + {SPR_POSS,13,5,{(actionf_p1)A_XScream},S_POSS_XDIE3,0,0}, // S_POSS_XDIE2 + {SPR_POSS,14,5,{(actionf_p1)A_Fall},S_POSS_XDIE4,0,0}, // S_POSS_XDIE3 + {SPR_POSS,15,5,{0},S_POSS_XDIE5,0,0}, // S_POSS_XDIE4 + {SPR_POSS,16,5,{0},S_POSS_XDIE6,0,0}, // S_POSS_XDIE5 + {SPR_POSS,17,5,{0},S_POSS_XDIE7,0,0}, // S_POSS_XDIE6 + {SPR_POSS,18,5,{0},S_POSS_XDIE8,0,0}, // S_POSS_XDIE7 + {SPR_POSS,19,5,{0},S_POSS_XDIE9,0,0}, // S_POSS_XDIE8 + {SPR_POSS,20,-1,{0},S_NULL,0,0}, // S_POSS_XDIE9 + {SPR_POSS,10,5,{0},S_POSS_RAISE2,0,0}, // S_POSS_RAISE1 + {SPR_POSS,9,5,{0},S_POSS_RAISE3,0,0}, // S_POSS_RAISE2 + {SPR_POSS,8,5,{0},S_POSS_RAISE4,0,0}, // S_POSS_RAISE3 + {SPR_POSS,7,5,{0},S_POSS_RUN1,0,0}, // S_POSS_RAISE4 + {SPR_SPOS,0,10,{(actionf_p1)A_Look},S_SPOS_STND2,0,0}, // S_SPOS_STND + {SPR_SPOS,1,10,{(actionf_p1)A_Look},S_SPOS_STND,0,0}, // S_SPOS_STND2 + {SPR_SPOS,0,3,{(actionf_p1)A_Chase},S_SPOS_RUN2,0,0}, // S_SPOS_RUN1 + {SPR_SPOS,0,3,{(actionf_p1)A_Chase},S_SPOS_RUN3,0,0}, // S_SPOS_RUN2 + {SPR_SPOS,1,3,{(actionf_p1)A_Chase},S_SPOS_RUN4,0,0}, // S_SPOS_RUN3 + {SPR_SPOS,1,3,{(actionf_p1)A_Chase},S_SPOS_RUN5,0,0}, // S_SPOS_RUN4 + {SPR_SPOS,2,3,{(actionf_p1)A_Chase},S_SPOS_RUN6,0,0}, // S_SPOS_RUN5 + {SPR_SPOS,2,3,{(actionf_p1)A_Chase},S_SPOS_RUN7,0,0}, // S_SPOS_RUN6 + {SPR_SPOS,3,3,{(actionf_p1)A_Chase},S_SPOS_RUN8,0,0}, // S_SPOS_RUN7 + {SPR_SPOS,3,3,{(actionf_p1)A_Chase},S_SPOS_RUN1,0,0}, // S_SPOS_RUN8 + {SPR_SPOS,4,10,{(actionf_p1)A_FaceTarget},S_SPOS_ATK2,0,0}, // S_SPOS_ATK1 + {SPR_SPOS,32773,10,{(actionf_p1)A_SPosAttack},S_SPOS_ATK3,0,0}, // S_SPOS_ATK2 + {SPR_SPOS,4,10,{0},S_SPOS_RUN1,0,0}, // S_SPOS_ATK3 + {SPR_SPOS,6,3,{0},S_SPOS_PAIN2,0,0}, // S_SPOS_PAIN + {SPR_SPOS,6,3,{(actionf_p1)A_Pain},S_SPOS_RUN1,0,0}, // S_SPOS_PAIN2 + {SPR_SPOS,7,5,{0},S_SPOS_DIE2,0,0}, // S_SPOS_DIE1 + {SPR_SPOS,8,5,{(actionf_p1)A_Scream},S_SPOS_DIE3,0,0}, // S_SPOS_DIE2 + {SPR_SPOS,9,5,{(actionf_p1)A_Fall},S_SPOS_DIE4,0,0}, // S_SPOS_DIE3 + {SPR_SPOS,10,5,{0},S_SPOS_DIE5,0,0}, // S_SPOS_DIE4 + {SPR_SPOS,11,-1,{0},S_NULL,0,0}, // S_SPOS_DIE5 + {SPR_SPOS,12,5,{0},S_SPOS_XDIE2,0,0}, // S_SPOS_XDIE1 + {SPR_SPOS,13,5,{(actionf_p1)A_XScream},S_SPOS_XDIE3,0,0}, // S_SPOS_XDIE2 + {SPR_SPOS,14,5,{(actionf_p1)A_Fall},S_SPOS_XDIE4,0,0}, // S_SPOS_XDIE3 + {SPR_SPOS,15,5,{0},S_SPOS_XDIE5,0,0}, // S_SPOS_XDIE4 + {SPR_SPOS,16,5,{0},S_SPOS_XDIE6,0,0}, // S_SPOS_XDIE5 + {SPR_SPOS,17,5,{0},S_SPOS_XDIE7,0,0}, // S_SPOS_XDIE6 + {SPR_SPOS,18,5,{0},S_SPOS_XDIE8,0,0}, // S_SPOS_XDIE7 + {SPR_SPOS,19,5,{0},S_SPOS_XDIE9,0,0}, // S_SPOS_XDIE8 + {SPR_SPOS,20,-1,{0},S_NULL,0,0}, // S_SPOS_XDIE9 + {SPR_SPOS,11,5,{0},S_SPOS_RAISE2,0,0}, // S_SPOS_RAISE1 + {SPR_SPOS,10,5,{0},S_SPOS_RAISE3,0,0}, // S_SPOS_RAISE2 + {SPR_SPOS,9,5,{0},S_SPOS_RAISE4,0,0}, // S_SPOS_RAISE3 + {SPR_SPOS,8,5,{0},S_SPOS_RAISE5,0,0}, // S_SPOS_RAISE4 + {SPR_SPOS,7,5,{0},S_SPOS_RUN1,0,0}, // S_SPOS_RAISE5 + {SPR_VILE,0,10,{(actionf_p1)A_Look},S_VILE_STND2,0,0}, // S_VILE_STND + {SPR_VILE,1,10,{(actionf_p1)A_Look},S_VILE_STND,0,0}, // S_VILE_STND2 + {SPR_VILE,0,2,{(actionf_p1)A_VileChase},S_VILE_RUN2,0,0}, // S_VILE_RUN1 + {SPR_VILE,0,2,{(actionf_p1)A_VileChase},S_VILE_RUN3,0,0}, // S_VILE_RUN2 + {SPR_VILE,1,2,{(actionf_p1)A_VileChase},S_VILE_RUN4,0,0}, // S_VILE_RUN3 + {SPR_VILE,1,2,{(actionf_p1)A_VileChase},S_VILE_RUN5,0,0}, // S_VILE_RUN4 + {SPR_VILE,2,2,{(actionf_p1)A_VileChase},S_VILE_RUN6,0,0}, // S_VILE_RUN5 + {SPR_VILE,2,2,{(actionf_p1)A_VileChase},S_VILE_RUN7,0,0}, // S_VILE_RUN6 + {SPR_VILE,3,2,{(actionf_p1)A_VileChase},S_VILE_RUN8,0,0}, // S_VILE_RUN7 + {SPR_VILE,3,2,{(actionf_p1)A_VileChase},S_VILE_RUN9,0,0}, // S_VILE_RUN8 + {SPR_VILE,4,2,{(actionf_p1)A_VileChase},S_VILE_RUN10,0,0}, // S_VILE_RUN9 + {SPR_VILE,4,2,{(actionf_p1)A_VileChase},S_VILE_RUN11,0,0}, // S_VILE_RUN10 + {SPR_VILE,5,2,{(actionf_p1)A_VileChase},S_VILE_RUN12,0,0}, // S_VILE_RUN11 + {SPR_VILE,5,2,{(actionf_p1)A_VileChase},S_VILE_RUN1,0,0}, // S_VILE_RUN12 + {SPR_VILE,32774,0,{(actionf_p1)A_VileStart},S_VILE_ATK2,0,0}, // S_VILE_ATK1 + {SPR_VILE,32774,10,{(actionf_p1)A_FaceTarget},S_VILE_ATK3,0,0}, // S_VILE_ATK2 + {SPR_VILE,32775,8,{(actionf_p1)A_VileTarget},S_VILE_ATK4,0,0}, // S_VILE_ATK3 + {SPR_VILE,32776,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK5,0,0}, // S_VILE_ATK4 + {SPR_VILE,32777,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK6,0,0}, // S_VILE_ATK5 + {SPR_VILE,32778,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK7,0,0}, // S_VILE_ATK6 + {SPR_VILE,32779,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK8,0,0}, // S_VILE_ATK7 + {SPR_VILE,32780,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK9,0,0}, // S_VILE_ATK8 + {SPR_VILE,32781,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK10,0,0}, // S_VILE_ATK9 + {SPR_VILE,32782,8,{(actionf_p1)A_VileAttack},S_VILE_ATK11,0,0}, // S_VILE_ATK10 + {SPR_VILE,32783,20,{0},S_VILE_RUN1,0,0}, // S_VILE_ATK11 + {SPR_VILE,32794,10,{0},S_VILE_HEAL2,0,0}, // S_VILE_HEAL1 + {SPR_VILE,32795,10,{0},S_VILE_HEAL3,0,0}, // S_VILE_HEAL2 + {SPR_VILE,32796,10,{0},S_VILE_RUN1,0,0}, // S_VILE_HEAL3 + {SPR_VILE,16,5,{0},S_VILE_PAIN2,0,0}, // S_VILE_PAIN + {SPR_VILE,16,5,{(actionf_p1)A_Pain},S_VILE_RUN1,0,0}, // S_VILE_PAIN2 + {SPR_VILE,16,7,{0},S_VILE_DIE2,0,0}, // S_VILE_DIE1 + {SPR_VILE,17,7,{(actionf_p1)A_Scream},S_VILE_DIE3,0,0}, // S_VILE_DIE2 + {SPR_VILE,18,7,{(actionf_p1)A_Fall},S_VILE_DIE4,0,0}, // S_VILE_DIE3 + {SPR_VILE,19,7,{0},S_VILE_DIE5,0,0}, // S_VILE_DIE4 + {SPR_VILE,20,7,{0},S_VILE_DIE6,0,0}, // S_VILE_DIE5 + {SPR_VILE,21,7,{0},S_VILE_DIE7,0,0}, // S_VILE_DIE6 + {SPR_VILE,22,7,{0},S_VILE_DIE8,0,0}, // S_VILE_DIE7 + {SPR_VILE,23,5,{0},S_VILE_DIE9,0,0}, // S_VILE_DIE8 + {SPR_VILE,24,5,{0},S_VILE_DIE10,0,0}, // S_VILE_DIE9 + {SPR_VILE,25,-1,{0},S_NULL,0,0}, // S_VILE_DIE10 + {SPR_FIRE,32768,2,{(actionf_p1)A_StartFire},S_FIRE2,0,0}, // S_FIRE1 + {SPR_FIRE,32769,2,{(actionf_p1)A_Fire},S_FIRE3,0,0}, // S_FIRE2 + {SPR_FIRE,32768,2,{(actionf_p1)A_Fire},S_FIRE4,0,0}, // S_FIRE3 + {SPR_FIRE,32769,2,{(actionf_p1)A_Fire},S_FIRE5,0,0}, // S_FIRE4 + {SPR_FIRE,32770,2,{(actionf_p1)A_FireCrackle},S_FIRE6,0,0}, // S_FIRE5 + {SPR_FIRE,32769,2,{(actionf_p1)A_Fire},S_FIRE7,0,0}, // S_FIRE6 + {SPR_FIRE,32770,2,{(actionf_p1)A_Fire},S_FIRE8,0,0}, // S_FIRE7 + {SPR_FIRE,32769,2,{(actionf_p1)A_Fire},S_FIRE9,0,0}, // S_FIRE8 + {SPR_FIRE,32770,2,{(actionf_p1)A_Fire},S_FIRE10,0,0}, // S_FIRE9 + {SPR_FIRE,32771,2,{(actionf_p1)A_Fire},S_FIRE11,0,0}, // S_FIRE10 + {SPR_FIRE,32770,2,{(actionf_p1)A_Fire},S_FIRE12,0,0}, // S_FIRE11 + {SPR_FIRE,32771,2,{(actionf_p1)A_Fire},S_FIRE13,0,0}, // S_FIRE12 + {SPR_FIRE,32770,2,{(actionf_p1)A_Fire},S_FIRE14,0,0}, // S_FIRE13 + {SPR_FIRE,32771,2,{(actionf_p1)A_Fire},S_FIRE15,0,0}, // S_FIRE14 + {SPR_FIRE,32772,2,{(actionf_p1)A_Fire},S_FIRE16,0,0}, // S_FIRE15 + {SPR_FIRE,32771,2,{(actionf_p1)A_Fire},S_FIRE17,0,0}, // S_FIRE16 + {SPR_FIRE,32772,2,{(actionf_p1)A_Fire},S_FIRE18,0,0}, // S_FIRE17 + {SPR_FIRE,32771,2,{(actionf_p1)A_Fire},S_FIRE19,0,0}, // S_FIRE18 + {SPR_FIRE,32772,2,{(actionf_p1)A_FireCrackle},S_FIRE20,0,0}, // S_FIRE19 + {SPR_FIRE,32773,2,{(actionf_p1)A_Fire},S_FIRE21,0,0}, // S_FIRE20 + {SPR_FIRE,32772,2,{(actionf_p1)A_Fire},S_FIRE22,0,0}, // S_FIRE21 + {SPR_FIRE,32773,2,{(actionf_p1)A_Fire},S_FIRE23,0,0}, // S_FIRE22 + {SPR_FIRE,32772,2,{(actionf_p1)A_Fire},S_FIRE24,0,0}, // S_FIRE23 + {SPR_FIRE,32773,2,{(actionf_p1)A_Fire},S_FIRE25,0,0}, // S_FIRE24 + {SPR_FIRE,32774,2,{(actionf_p1)A_Fire},S_FIRE26,0,0}, // S_FIRE25 + {SPR_FIRE,32775,2,{(actionf_p1)A_Fire},S_FIRE27,0,0}, // S_FIRE26 + {SPR_FIRE,32774,2,{(actionf_p1)A_Fire},S_FIRE28,0,0}, // S_FIRE27 + {SPR_FIRE,32775,2,{(actionf_p1)A_Fire},S_FIRE29,0,0}, // S_FIRE28 + {SPR_FIRE,32774,2,{(actionf_p1)A_Fire},S_FIRE30,0,0}, // S_FIRE29 + {SPR_FIRE,32775,2,{(actionf_p1)A_Fire},S_NULL,0,0}, // S_FIRE30 + {SPR_PUFF,1,4,{0},S_SMOKE2,0,0}, // S_SMOKE1 + {SPR_PUFF,2,4,{0},S_SMOKE3,0,0}, // S_SMOKE2 + {SPR_PUFF,1,4,{0},S_SMOKE4,0,0}, // S_SMOKE3 + {SPR_PUFF,2,4,{0},S_SMOKE5,0,0}, // S_SMOKE4 + {SPR_PUFF,3,4,{0},S_NULL,0,0}, // S_SMOKE5 + {SPR_FATB,32768,2,{(actionf_p1)A_Tracer},S_TRACER2,0,0}, // S_TRACER + {SPR_FATB,32769,2,{(actionf_p1)A_Tracer},S_TRACER,0,0}, // S_TRACER2 + {SPR_FBXP,32768,8,{0},S_TRACEEXP2,0,0}, // S_TRACEEXP1 + {SPR_FBXP,32769,6,{0},S_TRACEEXP3,0,0}, // S_TRACEEXP2 + {SPR_FBXP,32770,4,{0},S_NULL,0,0}, // S_TRACEEXP3 + {SPR_SKEL,0,10,{(actionf_p1)A_Look},S_SKEL_STND2,0,0}, // S_SKEL_STND + {SPR_SKEL,1,10,{(actionf_p1)A_Look},S_SKEL_STND,0,0}, // S_SKEL_STND2 + {SPR_SKEL,0,2,{(actionf_p1)A_Chase},S_SKEL_RUN2,0,0}, // S_SKEL_RUN1 + {SPR_SKEL,0,2,{(actionf_p1)A_Chase},S_SKEL_RUN3,0,0}, // S_SKEL_RUN2 + {SPR_SKEL,1,2,{(actionf_p1)A_Chase},S_SKEL_RUN4,0,0}, // S_SKEL_RUN3 + {SPR_SKEL,1,2,{(actionf_p1)A_Chase},S_SKEL_RUN5,0,0}, // S_SKEL_RUN4 + {SPR_SKEL,2,2,{(actionf_p1)A_Chase},S_SKEL_RUN6,0,0}, // S_SKEL_RUN5 + {SPR_SKEL,2,2,{(actionf_p1)A_Chase},S_SKEL_RUN7,0,0}, // S_SKEL_RUN6 + {SPR_SKEL,3,2,{(actionf_p1)A_Chase},S_SKEL_RUN8,0,0}, // S_SKEL_RUN7 + {SPR_SKEL,3,2,{(actionf_p1)A_Chase},S_SKEL_RUN9,0,0}, // S_SKEL_RUN8 + {SPR_SKEL,4,2,{(actionf_p1)A_Chase},S_SKEL_RUN10,0,0}, // S_SKEL_RUN9 + {SPR_SKEL,4,2,{(actionf_p1)A_Chase},S_SKEL_RUN11,0,0}, // S_SKEL_RUN10 + {SPR_SKEL,5,2,{(actionf_p1)A_Chase},S_SKEL_RUN12,0,0}, // S_SKEL_RUN11 + {SPR_SKEL,5,2,{(actionf_p1)A_Chase},S_SKEL_RUN1,0,0}, // S_SKEL_RUN12 + {SPR_SKEL,6,0,{(actionf_p1)A_FaceTarget},S_SKEL_FIST2,0,0}, // S_SKEL_FIST1 + {SPR_SKEL,6,6,{(actionf_p1)A_SkelWhoosh},S_SKEL_FIST3,0,0}, // S_SKEL_FIST2 + {SPR_SKEL,7,6,{(actionf_p1)A_FaceTarget},S_SKEL_FIST4,0,0}, // S_SKEL_FIST3 + {SPR_SKEL,8,6,{(actionf_p1)A_SkelFist},S_SKEL_RUN1,0,0}, // S_SKEL_FIST4 + {SPR_SKEL,32777,0,{(actionf_p1)A_FaceTarget},S_SKEL_MISS2,0,0}, // S_SKEL_MISS1 + {SPR_SKEL,32777,10,{(actionf_p1)A_FaceTarget},S_SKEL_MISS3,0,0}, // S_SKEL_MISS2 + {SPR_SKEL,10,10,{(actionf_p1)A_SkelMissile},S_SKEL_MISS4,0,0}, // S_SKEL_MISS3 + {SPR_SKEL,10,10,{(actionf_p1)A_FaceTarget},S_SKEL_RUN1,0,0}, // S_SKEL_MISS4 + {SPR_SKEL,11,5,{0},S_SKEL_PAIN2,0,0}, // S_SKEL_PAIN + {SPR_SKEL,11,5,{(actionf_p1)A_Pain},S_SKEL_RUN1,0,0}, // S_SKEL_PAIN2 + {SPR_SKEL,11,7,{0},S_SKEL_DIE2,0,0}, // S_SKEL_DIE1 + {SPR_SKEL,12,7,{0},S_SKEL_DIE3,0,0}, // S_SKEL_DIE2 + {SPR_SKEL,13,7,{(actionf_p1)A_Scream},S_SKEL_DIE4,0,0}, // S_SKEL_DIE3 + {SPR_SKEL,14,7,{(actionf_p1)A_Fall},S_SKEL_DIE5,0,0}, // S_SKEL_DIE4 + {SPR_SKEL,15,7,{0},S_SKEL_DIE6,0,0}, // S_SKEL_DIE5 + {SPR_SKEL,16,-1,{0},S_NULL,0,0}, // S_SKEL_DIE6 + {SPR_SKEL,16,5,{0},S_SKEL_RAISE2,0,0}, // S_SKEL_RAISE1 + {SPR_SKEL,15,5,{0},S_SKEL_RAISE3,0,0}, // S_SKEL_RAISE2 + {SPR_SKEL,14,5,{0},S_SKEL_RAISE4,0,0}, // S_SKEL_RAISE3 + {SPR_SKEL,13,5,{0},S_SKEL_RAISE5,0,0}, // S_SKEL_RAISE4 + {SPR_SKEL,12,5,{0},S_SKEL_RAISE6,0,0}, // S_SKEL_RAISE5 + {SPR_SKEL,11,5,{0},S_SKEL_RUN1,0,0}, // S_SKEL_RAISE6 + {SPR_MANF,32768,4,{0},S_FATSHOT2,0,0}, // S_FATSHOT1 + {SPR_MANF,32769,4,{0},S_FATSHOT1,0,0}, // S_FATSHOT2 + {SPR_MISL,32769,8,{0},S_FATSHOTX2,0,0}, // S_FATSHOTX1 + {SPR_MISL,32770,6,{0},S_FATSHOTX3,0,0}, // S_FATSHOTX2 + {SPR_MISL,32771,4,{0},S_NULL,0,0}, // S_FATSHOTX3 + {SPR_FATT,0,15,{(actionf_p1)A_Look},S_FATT_STND2,0,0}, // S_FATT_STND + {SPR_FATT,1,15,{(actionf_p1)A_Look},S_FATT_STND,0,0}, // S_FATT_STND2 + {SPR_FATT,0,4,{(actionf_p1)A_Chase},S_FATT_RUN2,0,0}, // S_FATT_RUN1 + {SPR_FATT,0,4,{(actionf_p1)A_Chase},S_FATT_RUN3,0,0}, // S_FATT_RUN2 + {SPR_FATT,1,4,{(actionf_p1)A_Chase},S_FATT_RUN4,0,0}, // S_FATT_RUN3 + {SPR_FATT,1,4,{(actionf_p1)A_Chase},S_FATT_RUN5,0,0}, // S_FATT_RUN4 + {SPR_FATT,2,4,{(actionf_p1)A_Chase},S_FATT_RUN6,0,0}, // S_FATT_RUN5 + {SPR_FATT,2,4,{(actionf_p1)A_Chase},S_FATT_RUN7,0,0}, // S_FATT_RUN6 + {SPR_FATT,3,4,{(actionf_p1)A_Chase},S_FATT_RUN8,0,0}, // S_FATT_RUN7 + {SPR_FATT,3,4,{(actionf_p1)A_Chase},S_FATT_RUN9,0,0}, // S_FATT_RUN8 + {SPR_FATT,4,4,{(actionf_p1)A_Chase},S_FATT_RUN10,0,0}, // S_FATT_RUN9 + {SPR_FATT,4,4,{(actionf_p1)A_Chase},S_FATT_RUN11,0,0}, // S_FATT_RUN10 + {SPR_FATT,5,4,{(actionf_p1)A_Chase},S_FATT_RUN12,0,0}, // S_FATT_RUN11 + {SPR_FATT,5,4,{(actionf_p1)A_Chase},S_FATT_RUN1,0,0}, // S_FATT_RUN12 + {SPR_FATT,6,20,{(actionf_p1)A_FatRaise},S_FATT_ATK2,0,0}, // S_FATT_ATK1 + {SPR_FATT,32775,10,{(actionf_p1)A_FatAttack1},S_FATT_ATK3,0,0}, // S_FATT_ATK2 + {SPR_FATT,8,5,{(actionf_p1)A_FaceTarget},S_FATT_ATK4,0,0}, // S_FATT_ATK3 + {SPR_FATT,6,5,{(actionf_p1)A_FaceTarget},S_FATT_ATK5,0,0}, // S_FATT_ATK4 + {SPR_FATT,32775,10,{(actionf_p1)A_FatAttack2},S_FATT_ATK6,0,0}, // S_FATT_ATK5 + {SPR_FATT,8,5,{(actionf_p1)A_FaceTarget},S_FATT_ATK7,0,0}, // S_FATT_ATK6 + {SPR_FATT,6,5,{(actionf_p1)A_FaceTarget},S_FATT_ATK8,0,0}, // S_FATT_ATK7 + {SPR_FATT,32775,10,{(actionf_p1)A_FatAttack3},S_FATT_ATK9,0,0}, // S_FATT_ATK8 + {SPR_FATT,8,5,{(actionf_p1)A_FaceTarget},S_FATT_ATK10,0,0}, // S_FATT_ATK9 + {SPR_FATT,6,5,{(actionf_p1)A_FaceTarget},S_FATT_RUN1,0,0}, // S_FATT_ATK10 + {SPR_FATT,9,3,{0},S_FATT_PAIN2,0,0}, // S_FATT_PAIN + {SPR_FATT,9,3,{(actionf_p1)A_Pain},S_FATT_RUN1,0,0}, // S_FATT_PAIN2 + {SPR_FATT,10,6,{0},S_FATT_DIE2,0,0}, // S_FATT_DIE1 + {SPR_FATT,11,6,{(actionf_p1)A_Scream},S_FATT_DIE3,0,0}, // S_FATT_DIE2 + {SPR_FATT,12,6,{(actionf_p1)A_Fall},S_FATT_DIE4,0,0}, // S_FATT_DIE3 + {SPR_FATT,13,6,{0},S_FATT_DIE5,0,0}, // S_FATT_DIE4 + {SPR_FATT,14,6,{0},S_FATT_DIE6,0,0}, // S_FATT_DIE5 + {SPR_FATT,15,6,{0},S_FATT_DIE7,0,0}, // S_FATT_DIE6 + {SPR_FATT,16,6,{0},S_FATT_DIE8,0,0}, // S_FATT_DIE7 + {SPR_FATT,17,6,{0},S_FATT_DIE9,0,0}, // S_FATT_DIE8 + {SPR_FATT,18,6,{0},S_FATT_DIE10,0,0}, // S_FATT_DIE9 + {SPR_FATT,19,-1,{(actionf_p1)A_BossDeath},S_NULL,0,0}, // S_FATT_DIE10 + {SPR_FATT,17,5,{0},S_FATT_RAISE2,0,0}, // S_FATT_RAISE1 + {SPR_FATT,16,5,{0},S_FATT_RAISE3,0,0}, // S_FATT_RAISE2 + {SPR_FATT,15,5,{0},S_FATT_RAISE4,0,0}, // S_FATT_RAISE3 + {SPR_FATT,14,5,{0},S_FATT_RAISE5,0,0}, // S_FATT_RAISE4 + {SPR_FATT,13,5,{0},S_FATT_RAISE6,0,0}, // S_FATT_RAISE5 + {SPR_FATT,12,5,{0},S_FATT_RAISE7,0,0}, // S_FATT_RAISE6 + {SPR_FATT,11,5,{0},S_FATT_RAISE8,0,0}, // S_FATT_RAISE7 + {SPR_FATT,10,5,{0},S_FATT_RUN1,0,0}, // S_FATT_RAISE8 + {SPR_CPOS,0,10,{(actionf_p1)A_Look},S_CPOS_STND2,0,0}, // S_CPOS_STND + {SPR_CPOS,1,10,{(actionf_p1)A_Look},S_CPOS_STND,0,0}, // S_CPOS_STND2 + {SPR_CPOS,0,3,{(actionf_p1)A_Chase},S_CPOS_RUN2,0,0}, // S_CPOS_RUN1 + {SPR_CPOS,0,3,{(actionf_p1)A_Chase},S_CPOS_RUN3,0,0}, // S_CPOS_RUN2 + {SPR_CPOS,1,3,{(actionf_p1)A_Chase},S_CPOS_RUN4,0,0}, // S_CPOS_RUN3 + {SPR_CPOS,1,3,{(actionf_p1)A_Chase},S_CPOS_RUN5,0,0}, // S_CPOS_RUN4 + {SPR_CPOS,2,3,{(actionf_p1)A_Chase},S_CPOS_RUN6,0,0}, // S_CPOS_RUN5 + {SPR_CPOS,2,3,{(actionf_p1)A_Chase},S_CPOS_RUN7,0,0}, // S_CPOS_RUN6 + {SPR_CPOS,3,3,{(actionf_p1)A_Chase},S_CPOS_RUN8,0,0}, // S_CPOS_RUN7 + {SPR_CPOS,3,3,{(actionf_p1)A_Chase},S_CPOS_RUN1,0,0}, // S_CPOS_RUN8 + {SPR_CPOS,4,10,{(actionf_p1)A_FaceTarget},S_CPOS_ATK2,0,0}, // S_CPOS_ATK1 + {SPR_CPOS,32773,4,{(actionf_p1)A_CPosAttack},S_CPOS_ATK3,0,0}, // S_CPOS_ATK2 + {SPR_CPOS,32772,4,{(actionf_p1)A_CPosAttack},S_CPOS_ATK4,0,0}, // S_CPOS_ATK3 + {SPR_CPOS,5,1,{(actionf_p1)A_CPosRefire},S_CPOS_ATK2,0,0}, // S_CPOS_ATK4 + {SPR_CPOS,6,3,{0},S_CPOS_PAIN2,0,0}, // S_CPOS_PAIN + {SPR_CPOS,6,3,{(actionf_p1)A_Pain},S_CPOS_RUN1,0,0}, // S_CPOS_PAIN2 + {SPR_CPOS,7,5,{0},S_CPOS_DIE2,0,0}, // S_CPOS_DIE1 + {SPR_CPOS,8,5,{(actionf_p1)A_Scream},S_CPOS_DIE3,0,0}, // S_CPOS_DIE2 + {SPR_CPOS,9,5,{(actionf_p1)A_Fall},S_CPOS_DIE4,0,0}, // S_CPOS_DIE3 + {SPR_CPOS,10,5,{0},S_CPOS_DIE5,0,0}, // S_CPOS_DIE4 + {SPR_CPOS,11,5,{0},S_CPOS_DIE6,0,0}, // S_CPOS_DIE5 + {SPR_CPOS,12,5,{0},S_CPOS_DIE7,0,0}, // S_CPOS_DIE6 + {SPR_CPOS,13,-1,{0},S_NULL,0,0}, // S_CPOS_DIE7 + {SPR_CPOS,14,5,{0},S_CPOS_XDIE2,0,0}, // S_CPOS_XDIE1 + {SPR_CPOS,15,5,{(actionf_p1)A_XScream},S_CPOS_XDIE3,0,0}, // S_CPOS_XDIE2 + {SPR_CPOS,16,5,{(actionf_p1)A_Fall},S_CPOS_XDIE4,0,0}, // S_CPOS_XDIE3 + {SPR_CPOS,17,5,{0},S_CPOS_XDIE5,0,0}, // S_CPOS_XDIE4 + {SPR_CPOS,18,5,{0},S_CPOS_XDIE6,0,0}, // S_CPOS_XDIE5 + {SPR_CPOS,19,-1,{0},S_NULL,0,0}, // S_CPOS_XDIE6 + {SPR_CPOS,13,5,{0},S_CPOS_RAISE2,0,0}, // S_CPOS_RAISE1 + {SPR_CPOS,12,5,{0},S_CPOS_RAISE3,0,0}, // S_CPOS_RAISE2 + {SPR_CPOS,11,5,{0},S_CPOS_RAISE4,0,0}, // S_CPOS_RAISE3 + {SPR_CPOS,10,5,{0},S_CPOS_RAISE5,0,0}, // S_CPOS_RAISE4 + {SPR_CPOS,9,5,{0},S_CPOS_RAISE6,0,0}, // S_CPOS_RAISE5 + {SPR_CPOS,8,5,{0},S_CPOS_RAISE7,0,0}, // S_CPOS_RAISE6 + {SPR_CPOS,7,5,{0},S_CPOS_RUN1,0,0}, // S_CPOS_RAISE7 + {SPR_TROO,0,10,{(actionf_p1)A_Look},S_TROO_STND2,0,0}, // S_TROO_STND + {SPR_TROO,1,10,{(actionf_p1)A_Look},S_TROO_STND,0,0}, // S_TROO_STND2 + {SPR_TROO,0,3,{(actionf_p1)A_Chase},S_TROO_RUN2,0,0}, // S_TROO_RUN1 + {SPR_TROO,0,3,{(actionf_p1)A_Chase},S_TROO_RUN3,0,0}, // S_TROO_RUN2 + {SPR_TROO,1,3,{(actionf_p1)A_Chase},S_TROO_RUN4,0,0}, // S_TROO_RUN3 + {SPR_TROO,1,3,{(actionf_p1)A_Chase},S_TROO_RUN5,0,0}, // S_TROO_RUN4 + {SPR_TROO,2,3,{(actionf_p1)A_Chase},S_TROO_RUN6,0,0}, // S_TROO_RUN5 + {SPR_TROO,2,3,{(actionf_p1)A_Chase},S_TROO_RUN7,0,0}, // S_TROO_RUN6 + {SPR_TROO,3,3,{(actionf_p1)A_Chase},S_TROO_RUN8,0,0}, // S_TROO_RUN7 + {SPR_TROO,3,3,{(actionf_p1)A_Chase},S_TROO_RUN1,0,0}, // S_TROO_RUN8 + {SPR_TROO,4,8,{(actionf_p1)A_FaceTarget},S_TROO_ATK2,0,0}, // S_TROO_ATK1 + {SPR_TROO,5,8,{(actionf_p1)A_FaceTarget},S_TROO_ATK3,0,0}, // S_TROO_ATK2 + {SPR_TROO,6,6,{(actionf_p1)A_TroopAttack},S_TROO_RUN1,0,0}, // S_TROO_ATK3 + {SPR_TROO,7,2,{0},S_TROO_PAIN2,0,0}, // S_TROO_PAIN + {SPR_TROO,7,2,{(actionf_p1)A_Pain},S_TROO_RUN1,0,0}, // S_TROO_PAIN2 + {SPR_TROO,8,8,{0},S_TROO_DIE2,0,0}, // S_TROO_DIE1 + {SPR_TROO,9,8,{(actionf_p1)A_Scream},S_TROO_DIE3,0,0}, // S_TROO_DIE2 + {SPR_TROO,10,6,{0},S_TROO_DIE4,0,0}, // S_TROO_DIE3 + {SPR_TROO,11,6,{(actionf_p1)A_Fall},S_TROO_DIE5,0,0}, // S_TROO_DIE4 + {SPR_TROO,12,-1,{0},S_NULL,0,0}, // S_TROO_DIE5 + {SPR_TROO,13,5,{0},S_TROO_XDIE2,0,0}, // S_TROO_XDIE1 + {SPR_TROO,14,5,{(actionf_p1)A_XScream},S_TROO_XDIE3,0,0}, // S_TROO_XDIE2 + {SPR_TROO,15,5,{0},S_TROO_XDIE4,0,0}, // S_TROO_XDIE3 + {SPR_TROO,16,5,{(actionf_p1)A_Fall},S_TROO_XDIE5,0,0}, // S_TROO_XDIE4 + {SPR_TROO,17,5,{0},S_TROO_XDIE6,0,0}, // S_TROO_XDIE5 + {SPR_TROO,18,5,{0},S_TROO_XDIE7,0,0}, // S_TROO_XDIE6 + {SPR_TROO,19,5,{0},S_TROO_XDIE8,0,0}, // S_TROO_XDIE7 + {SPR_TROO,20,-1,{0},S_NULL,0,0}, // S_TROO_XDIE8 + {SPR_TROO,12,8,{0},S_TROO_RAISE2,0,0}, // S_TROO_RAISE1 + {SPR_TROO,11,8,{0},S_TROO_RAISE3,0,0}, // S_TROO_RAISE2 + {SPR_TROO,10,6,{0},S_TROO_RAISE4,0,0}, // S_TROO_RAISE3 + {SPR_TROO,9,6,{0},S_TROO_RAISE5,0,0}, // S_TROO_RAISE4 + {SPR_TROO,8,6,{0},S_TROO_RUN1,0,0}, // S_TROO_RAISE5 + {SPR_SARG,0,10,{(actionf_p1)A_Look},S_SARG_STND2,0,0}, // S_SARG_STND + {SPR_SARG,1,10,{(actionf_p1)A_Look},S_SARG_STND,0,0}, // S_SARG_STND2 + {SPR_SARG,0,2,{(actionf_p1)A_Chase},S_SARG_RUN2,0,0}, // S_SARG_RUN1 + {SPR_SARG,0,2,{(actionf_p1)A_Chase},S_SARG_RUN3,0,0}, // S_SARG_RUN2 + {SPR_SARG,1,2,{(actionf_p1)A_Chase},S_SARG_RUN4,0,0}, // S_SARG_RUN3 + {SPR_SARG,1,2,{(actionf_p1)A_Chase},S_SARG_RUN5,0,0}, // S_SARG_RUN4 + {SPR_SARG,2,2,{(actionf_p1)A_Chase},S_SARG_RUN6,0,0}, // S_SARG_RUN5 + {SPR_SARG,2,2,{(actionf_p1)A_Chase},S_SARG_RUN7,0,0}, // S_SARG_RUN6 + {SPR_SARG,3,2,{(actionf_p1)A_Chase},S_SARG_RUN8,0,0}, // S_SARG_RUN7 + {SPR_SARG,3,2,{(actionf_p1)A_Chase},S_SARG_RUN1,0,0}, // S_SARG_RUN8 + {SPR_SARG,4,8,{(actionf_p1)A_FaceTarget},S_SARG_ATK2,0,0}, // S_SARG_ATK1 + {SPR_SARG,5,8,{(actionf_p1)A_FaceTarget},S_SARG_ATK3,0,0}, // S_SARG_ATK2 + {SPR_SARG,6,8,{(actionf_p1)A_SargAttack},S_SARG_RUN1,0,0}, // S_SARG_ATK3 + {SPR_SARG,7,2,{0},S_SARG_PAIN2,0,0}, // S_SARG_PAIN + {SPR_SARG,7,2,{(actionf_p1)A_Pain},S_SARG_RUN1,0,0}, // S_SARG_PAIN2 + {SPR_SARG,8,8,{0},S_SARG_DIE2,0,0}, // S_SARG_DIE1 + {SPR_SARG,9,8,{(actionf_p1)A_Scream},S_SARG_DIE3,0,0}, // S_SARG_DIE2 + {SPR_SARG,10,4,{0},S_SARG_DIE4,0,0}, // S_SARG_DIE3 + {SPR_SARG,11,4,{(actionf_p1)A_Fall},S_SARG_DIE5,0,0}, // S_SARG_DIE4 + {SPR_SARG,12,4,{0},S_SARG_DIE6,0,0}, // S_SARG_DIE5 + {SPR_SARG,13,-1,{0},S_NULL,0,0}, // S_SARG_DIE6 + {SPR_SARG,13,5,{0},S_SARG_RAISE2,0,0}, // S_SARG_RAISE1 + {SPR_SARG,12,5,{0},S_SARG_RAISE3,0,0}, // S_SARG_RAISE2 + {SPR_SARG,11,5,{0},S_SARG_RAISE4,0,0}, // S_SARG_RAISE3 + {SPR_SARG,10,5,{0},S_SARG_RAISE5,0,0}, // S_SARG_RAISE4 + {SPR_SARG,9,5,{0},S_SARG_RAISE6,0,0}, // S_SARG_RAISE5 + {SPR_SARG,8,5,{0},S_SARG_RUN1,0,0}, // S_SARG_RAISE6 + {SPR_HEAD,0,10,{(actionf_p1)A_Look},S_HEAD_STND,0,0}, // S_HEAD_STND + {SPR_HEAD,0,3,{(actionf_p1)A_Chase},S_HEAD_RUN1,0,0}, // S_HEAD_RUN1 + {SPR_HEAD,1,5,{(actionf_p1)A_FaceTarget},S_HEAD_ATK2,0,0}, // S_HEAD_ATK1 + {SPR_HEAD,2,5,{(actionf_p1)A_FaceTarget},S_HEAD_ATK3,0,0}, // S_HEAD_ATK2 + {SPR_HEAD,32771,5,{(actionf_p1)A_HeadAttack},S_HEAD_RUN1,0,0}, // S_HEAD_ATK3 + {SPR_HEAD,4,3,{0},S_HEAD_PAIN2,0,0}, // S_HEAD_PAIN + {SPR_HEAD,4,3,{(actionf_p1)A_Pain},S_HEAD_PAIN3,0,0}, // S_HEAD_PAIN2 + {SPR_HEAD,5,6,{0},S_HEAD_RUN1,0,0}, // S_HEAD_PAIN3 + {SPR_HEAD,6,8,{0},S_HEAD_DIE2,0,0}, // S_HEAD_DIE1 + {SPR_HEAD,7,8,{(actionf_p1)A_Scream},S_HEAD_DIE3,0,0}, // S_HEAD_DIE2 + {SPR_HEAD,8,8,{0},S_HEAD_DIE4,0,0}, // S_HEAD_DIE3 + {SPR_HEAD,9,8,{0},S_HEAD_DIE5,0,0}, // S_HEAD_DIE4 + {SPR_HEAD,10,8,{(actionf_p1)A_Fall},S_HEAD_DIE6,0,0}, // S_HEAD_DIE5 + {SPR_HEAD,11,-1,{0},S_NULL,0,0}, // S_HEAD_DIE6 + {SPR_HEAD,11,8,{0},S_HEAD_RAISE2,0,0}, // S_HEAD_RAISE1 + {SPR_HEAD,10,8,{0},S_HEAD_RAISE3,0,0}, // S_HEAD_RAISE2 + {SPR_HEAD,9,8,{0},S_HEAD_RAISE4,0,0}, // S_HEAD_RAISE3 + {SPR_HEAD,8,8,{0},S_HEAD_RAISE5,0,0}, // S_HEAD_RAISE4 + {SPR_HEAD,7,8,{0},S_HEAD_RAISE6,0,0}, // S_HEAD_RAISE5 + {SPR_HEAD,6,8,{0},S_HEAD_RUN1,0,0}, // S_HEAD_RAISE6 + {SPR_BAL7,32768,4,{0},S_BRBALL2,0,0}, // S_BRBALL1 + {SPR_BAL7,32769,4,{0},S_BRBALL1,0,0}, // S_BRBALL2 + {SPR_BAL7,32770,6,{0},S_BRBALLX2,0,0}, // S_BRBALLX1 + {SPR_BAL7,32771,6,{0},S_BRBALLX3,0,0}, // S_BRBALLX2 + {SPR_BAL7,32772,6,{0},S_NULL,0,0}, // S_BRBALLX3 + {SPR_BOSS,0,10,{(actionf_p1)A_Look},S_BOSS_STND2,0,0}, // S_BOSS_STND + {SPR_BOSS,1,10,{(actionf_p1)A_Look},S_BOSS_STND,0,0}, // S_BOSS_STND2 + {SPR_BOSS,0,3,{(actionf_p1)A_Chase},S_BOSS_RUN2,0,0}, // S_BOSS_RUN1 + {SPR_BOSS,0,3,{(actionf_p1)A_Chase},S_BOSS_RUN3,0,0}, // S_BOSS_RUN2 + {SPR_BOSS,1,3,{(actionf_p1)A_Chase},S_BOSS_RUN4,0,0}, // S_BOSS_RUN3 + {SPR_BOSS,1,3,{(actionf_p1)A_Chase},S_BOSS_RUN5,0,0}, // S_BOSS_RUN4 + {SPR_BOSS,2,3,{(actionf_p1)A_Chase},S_BOSS_RUN6,0,0}, // S_BOSS_RUN5 + {SPR_BOSS,2,3,{(actionf_p1)A_Chase},S_BOSS_RUN7,0,0}, // S_BOSS_RUN6 + {SPR_BOSS,3,3,{(actionf_p1)A_Chase},S_BOSS_RUN8,0,0}, // S_BOSS_RUN7 + {SPR_BOSS,3,3,{(actionf_p1)A_Chase},S_BOSS_RUN1,0,0}, // S_BOSS_RUN8 + {SPR_BOSS,4,8,{(actionf_p1)A_FaceTarget},S_BOSS_ATK2,0,0}, // S_BOSS_ATK1 + {SPR_BOSS,5,8,{(actionf_p1)A_FaceTarget},S_BOSS_ATK3,0,0}, // S_BOSS_ATK2 + {SPR_BOSS,6,8,{(actionf_p1)A_BruisAttack},S_BOSS_RUN1,0,0}, // S_BOSS_ATK3 + {SPR_BOSS,7,2,{0},S_BOSS_PAIN2,0,0}, // S_BOSS_PAIN + {SPR_BOSS,7,2,{(actionf_p1)A_Pain},S_BOSS_RUN1,0,0}, // S_BOSS_PAIN2 + {SPR_BOSS,8,8,{0},S_BOSS_DIE2,0,0}, // S_BOSS_DIE1 + {SPR_BOSS,9,8,{(actionf_p1)A_Scream},S_BOSS_DIE3,0,0}, // S_BOSS_DIE2 + {SPR_BOSS,10,8,{0},S_BOSS_DIE4,0,0}, // S_BOSS_DIE3 + {SPR_BOSS,11,8,{(actionf_p1)A_Fall},S_BOSS_DIE5,0,0}, // S_BOSS_DIE4 + {SPR_BOSS,12,8,{0},S_BOSS_DIE6,0,0}, // S_BOSS_DIE5 + {SPR_BOSS,13,8,{0},S_BOSS_DIE7,0,0}, // S_BOSS_DIE6 + {SPR_BOSS,14,-1,{(actionf_p1)A_BossDeath},S_NULL,0,0}, // S_BOSS_DIE7 + {SPR_BOSS,14,8,{0},S_BOSS_RAISE2,0,0}, // S_BOSS_RAISE1 + {SPR_BOSS,13,8,{0},S_BOSS_RAISE3,0,0}, // S_BOSS_RAISE2 + {SPR_BOSS,12,8,{0},S_BOSS_RAISE4,0,0}, // S_BOSS_RAISE3 + {SPR_BOSS,11,8,{0},S_BOSS_RAISE5,0,0}, // S_BOSS_RAISE4 + {SPR_BOSS,10,8,{0},S_BOSS_RAISE6,0,0}, // S_BOSS_RAISE5 + {SPR_BOSS,9,8,{0},S_BOSS_RAISE7,0,0}, // S_BOSS_RAISE6 + {SPR_BOSS,8,8,{0},S_BOSS_RUN1,0,0}, // S_BOSS_RAISE7 + {SPR_BOS2,0,10,{(actionf_p1)A_Look},S_BOS2_STND2,0,0}, // S_BOS2_STND + {SPR_BOS2,1,10,{(actionf_p1)A_Look},S_BOS2_STND,0,0}, // S_BOS2_STND2 + {SPR_BOS2,0,3,{(actionf_p1)A_Chase},S_BOS2_RUN2,0,0}, // S_BOS2_RUN1 + {SPR_BOS2,0,3,{(actionf_p1)A_Chase},S_BOS2_RUN3,0,0}, // S_BOS2_RUN2 + {SPR_BOS2,1,3,{(actionf_p1)A_Chase},S_BOS2_RUN4,0,0}, // S_BOS2_RUN3 + {SPR_BOS2,1,3,{(actionf_p1)A_Chase},S_BOS2_RUN5,0,0}, // S_BOS2_RUN4 + {SPR_BOS2,2,3,{(actionf_p1)A_Chase},S_BOS2_RUN6,0,0}, // S_BOS2_RUN5 + {SPR_BOS2,2,3,{(actionf_p1)A_Chase},S_BOS2_RUN7,0,0}, // S_BOS2_RUN6 + {SPR_BOS2,3,3,{(actionf_p1)A_Chase},S_BOS2_RUN8,0,0}, // S_BOS2_RUN7 + {SPR_BOS2,3,3,{(actionf_p1)A_Chase},S_BOS2_RUN1,0,0}, // S_BOS2_RUN8 + {SPR_BOS2,4,8,{(actionf_p1)A_FaceTarget},S_BOS2_ATK2,0,0}, // S_BOS2_ATK1 + {SPR_BOS2,5,8,{(actionf_p1)A_FaceTarget},S_BOS2_ATK3,0,0}, // S_BOS2_ATK2 + {SPR_BOS2,6,8,{(actionf_p1)A_BruisAttack},S_BOS2_RUN1,0,0}, // S_BOS2_ATK3 + {SPR_BOS2,7,2,{0},S_BOS2_PAIN2,0,0}, // S_BOS2_PAIN + {SPR_BOS2,7,2,{(actionf_p1)A_Pain},S_BOS2_RUN1,0,0}, // S_BOS2_PAIN2 + {SPR_BOS2,8,8,{0},S_BOS2_DIE2,0,0}, // S_BOS2_DIE1 + {SPR_BOS2,9,8,{(actionf_p1)A_Scream},S_BOS2_DIE3,0,0}, // S_BOS2_DIE2 + {SPR_BOS2,10,8,{0},S_BOS2_DIE4,0,0}, // S_BOS2_DIE3 + {SPR_BOS2,11,8,{(actionf_p1)A_Fall},S_BOS2_DIE5,0,0}, // S_BOS2_DIE4 + {SPR_BOS2,12,8,{0},S_BOS2_DIE6,0,0}, // S_BOS2_DIE5 + {SPR_BOS2,13,8,{0},S_BOS2_DIE7,0,0}, // S_BOS2_DIE6 + {SPR_BOS2,14,-1,{0},S_NULL,0,0}, // S_BOS2_DIE7 + {SPR_BOS2,14,8,{0},S_BOS2_RAISE2,0,0}, // S_BOS2_RAISE1 + {SPR_BOS2,13,8,{0},S_BOS2_RAISE3,0,0}, // S_BOS2_RAISE2 + {SPR_BOS2,12,8,{0},S_BOS2_RAISE4,0,0}, // S_BOS2_RAISE3 + {SPR_BOS2,11,8,{0},S_BOS2_RAISE5,0,0}, // S_BOS2_RAISE4 + {SPR_BOS2,10,8,{0},S_BOS2_RAISE6,0,0}, // S_BOS2_RAISE5 + {SPR_BOS2,9,8,{0},S_BOS2_RAISE7,0,0}, // S_BOS2_RAISE6 + {SPR_BOS2,8,8,{0},S_BOS2_RUN1,0,0}, // S_BOS2_RAISE7 + {SPR_SKUL,32768,10,{(actionf_p1)A_Look},S_SKULL_STND2,0,0}, // S_SKULL_STND + {SPR_SKUL,32769,10,{(actionf_p1)A_Look},S_SKULL_STND,0,0}, // S_SKULL_STND2 + {SPR_SKUL,32768,6,{(actionf_p1)A_Chase},S_SKULL_RUN2,0,0}, // S_SKULL_RUN1 + {SPR_SKUL,32769,6,{(actionf_p1)A_Chase},S_SKULL_RUN1,0,0}, // S_SKULL_RUN2 + {SPR_SKUL,32770,10,{(actionf_p1)A_FaceTarget},S_SKULL_ATK2,0,0}, // S_SKULL_ATK1 + {SPR_SKUL,32771,4,{(actionf_p1)A_SkullAttack},S_SKULL_ATK3,0,0}, // S_SKULL_ATK2 + {SPR_SKUL,32770,4,{0},S_SKULL_ATK4,0,0}, // S_SKULL_ATK3 + {SPR_SKUL,32771,4,{0},S_SKULL_ATK3,0,0}, // S_SKULL_ATK4 + {SPR_SKUL,32772,3,{0},S_SKULL_PAIN2,0,0}, // S_SKULL_PAIN + {SPR_SKUL,32772,3,{(actionf_p1)A_Pain},S_SKULL_RUN1,0,0}, // S_SKULL_PAIN2 + {SPR_SKUL,32773,6,{0},S_SKULL_DIE2,0,0}, // S_SKULL_DIE1 + {SPR_SKUL,32774,6,{(actionf_p1)A_Scream},S_SKULL_DIE3,0,0}, // S_SKULL_DIE2 + {SPR_SKUL,32775,6,{0},S_SKULL_DIE4,0,0}, // S_SKULL_DIE3 + {SPR_SKUL,32776,6,{(actionf_p1)A_Fall},S_SKULL_DIE5,0,0}, // S_SKULL_DIE4 + {SPR_SKUL,9,6,{0},S_SKULL_DIE6,0,0}, // S_SKULL_DIE5 + {SPR_SKUL,10,6,{0},S_NULL,0,0}, // S_SKULL_DIE6 + {SPR_SPID,0,10,{(actionf_p1)A_Look},S_SPID_STND2,0,0}, // S_SPID_STND + {SPR_SPID,1,10,{(actionf_p1)A_Look},S_SPID_STND,0,0}, // S_SPID_STND2 + {SPR_SPID,0,3,{(actionf_p1)A_Metal},S_SPID_RUN2,0,0}, // S_SPID_RUN1 + {SPR_SPID,0,3,{(actionf_p1)A_Chase},S_SPID_RUN3,0,0}, // S_SPID_RUN2 + {SPR_SPID,1,3,{(actionf_p1)A_Chase},S_SPID_RUN4,0,0}, // S_SPID_RUN3 + {SPR_SPID,1,3,{(actionf_p1)A_Chase},S_SPID_RUN5,0,0}, // S_SPID_RUN4 + {SPR_SPID,2,3,{(actionf_p1)A_Metal},S_SPID_RUN6,0,0}, // S_SPID_RUN5 + {SPR_SPID,2,3,{(actionf_p1)A_Chase},S_SPID_RUN7,0,0}, // S_SPID_RUN6 + {SPR_SPID,3,3,{(actionf_p1)A_Chase},S_SPID_RUN8,0,0}, // S_SPID_RUN7 + {SPR_SPID,3,3,{(actionf_p1)A_Chase},S_SPID_RUN9,0,0}, // S_SPID_RUN8 + {SPR_SPID,4,3,{(actionf_p1)A_Metal},S_SPID_RUN10,0,0}, // S_SPID_RUN9 + {SPR_SPID,4,3,{(actionf_p1)A_Chase},S_SPID_RUN11,0,0}, // S_SPID_RUN10 + {SPR_SPID,5,3,{(actionf_p1)A_Chase},S_SPID_RUN12,0,0}, // S_SPID_RUN11 + {SPR_SPID,5,3,{(actionf_p1)A_Chase},S_SPID_RUN1,0,0}, // S_SPID_RUN12 + {SPR_SPID,32768,20,{(actionf_p1)A_FaceTarget},S_SPID_ATK2,0,0}, // S_SPID_ATK1 + {SPR_SPID,32774,4,{(actionf_p1)A_SPosAttack},S_SPID_ATK3,0,0}, // S_SPID_ATK2 + {SPR_SPID,32775,4,{(actionf_p1)A_SPosAttack},S_SPID_ATK4,0,0}, // S_SPID_ATK3 + {SPR_SPID,32775,1,{(actionf_p1)A_SpidRefire},S_SPID_ATK2,0,0}, // S_SPID_ATK4 + {SPR_SPID,8,3,{0},S_SPID_PAIN2,0,0}, // S_SPID_PAIN + {SPR_SPID,8,3,{(actionf_p1)A_Pain},S_SPID_RUN1,0,0}, // S_SPID_PAIN2 + {SPR_SPID,9,20,{(actionf_p1)A_Scream},S_SPID_DIE2,0,0}, // S_SPID_DIE1 + {SPR_SPID,10,10,{(actionf_p1)A_Fall},S_SPID_DIE3,0,0}, // S_SPID_DIE2 + {SPR_SPID,11,10,{0},S_SPID_DIE4,0,0}, // S_SPID_DIE3 + {SPR_SPID,12,10,{0},S_SPID_DIE5,0,0}, // S_SPID_DIE4 + {SPR_SPID,13,10,{0},S_SPID_DIE6,0,0}, // S_SPID_DIE5 + {SPR_SPID,14,10,{0},S_SPID_DIE7,0,0}, // S_SPID_DIE6 + {SPR_SPID,15,10,{0},S_SPID_DIE8,0,0}, // S_SPID_DIE7 + {SPR_SPID,16,10,{0},S_SPID_DIE9,0,0}, // S_SPID_DIE8 + {SPR_SPID,17,10,{0},S_SPID_DIE10,0,0}, // S_SPID_DIE9 + {SPR_SPID,18,30,{0},S_SPID_DIE11,0,0}, // S_SPID_DIE10 + {SPR_SPID,18,-1,{(actionf_p1)A_BossDeath},S_NULL,0,0}, // S_SPID_DIE11 + {SPR_BSPI,0,10,{(actionf_p1)A_Look},S_BSPI_STND2,0,0}, // S_BSPI_STND + {SPR_BSPI,1,10,{(actionf_p1)A_Look},S_BSPI_STND,0,0}, // S_BSPI_STND2 + {SPR_BSPI,0,20,{0},S_BSPI_RUN1,0,0}, // S_BSPI_SIGHT + {SPR_BSPI,0,3,{(actionf_p1)A_BabyMetal},S_BSPI_RUN2,0,0}, // S_BSPI_RUN1 + {SPR_BSPI,0,3,{(actionf_p1)A_Chase},S_BSPI_RUN3,0,0}, // S_BSPI_RUN2 + {SPR_BSPI,1,3,{(actionf_p1)A_Chase},S_BSPI_RUN4,0,0}, // S_BSPI_RUN3 + {SPR_BSPI,1,3,{(actionf_p1)A_Chase},S_BSPI_RUN5,0,0}, // S_BSPI_RUN4 + {SPR_BSPI,2,3,{(actionf_p1)A_Chase},S_BSPI_RUN6,0,0}, // S_BSPI_RUN5 + {SPR_BSPI,2,3,{(actionf_p1)A_Chase},S_BSPI_RUN7,0,0}, // S_BSPI_RUN6 + {SPR_BSPI,3,3,{(actionf_p1)A_BabyMetal},S_BSPI_RUN8,0,0}, // S_BSPI_RUN7 + {SPR_BSPI,3,3,{(actionf_p1)A_Chase},S_BSPI_RUN9,0,0}, // S_BSPI_RUN8 + {SPR_BSPI,4,3,{(actionf_p1)A_Chase},S_BSPI_RUN10,0,0}, // S_BSPI_RUN9 + {SPR_BSPI,4,3,{(actionf_p1)A_Chase},S_BSPI_RUN11,0,0}, // S_BSPI_RUN10 + {SPR_BSPI,5,3,{(actionf_p1)A_Chase},S_BSPI_RUN12,0,0}, // S_BSPI_RUN11 + {SPR_BSPI,5,3,{(actionf_p1)A_Chase},S_BSPI_RUN1,0,0}, // S_BSPI_RUN12 + {SPR_BSPI,32768,20,{(actionf_p1)A_FaceTarget},S_BSPI_ATK2,0,0}, // S_BSPI_ATK1 + {SPR_BSPI,32774,4,{(actionf_p1)A_BspiAttack},S_BSPI_ATK3,0,0}, // S_BSPI_ATK2 + {SPR_BSPI,32775,4,{0},S_BSPI_ATK4,0,0}, // S_BSPI_ATK3 + {SPR_BSPI,32775,1,{(actionf_p1)A_SpidRefire},S_BSPI_ATK2,0,0}, // S_BSPI_ATK4 + {SPR_BSPI,8,3,{0},S_BSPI_PAIN2,0,0}, // S_BSPI_PAIN + {SPR_BSPI,8,3,{(actionf_p1)A_Pain},S_BSPI_RUN1,0,0}, // S_BSPI_PAIN2 + {SPR_BSPI,9,20,{(actionf_p1)A_Scream},S_BSPI_DIE2,0,0}, // S_BSPI_DIE1 + {SPR_BSPI,10,7,{(actionf_p1)A_Fall},S_BSPI_DIE3,0,0}, // S_BSPI_DIE2 + {SPR_BSPI,11,7,{0},S_BSPI_DIE4,0,0}, // S_BSPI_DIE3 + {SPR_BSPI,12,7,{0},S_BSPI_DIE5,0,0}, // S_BSPI_DIE4 + {SPR_BSPI,13,7,{0},S_BSPI_DIE6,0,0}, // S_BSPI_DIE5 + {SPR_BSPI,14,7,{0},S_BSPI_DIE7,0,0}, // S_BSPI_DIE6 + {SPR_BSPI,15,-1,{(actionf_p1)A_BossDeath},S_NULL,0,0}, // S_BSPI_DIE7 + {SPR_BSPI,15,5,{0},S_BSPI_RAISE2,0,0}, // S_BSPI_RAISE1 + {SPR_BSPI,14,5,{0},S_BSPI_RAISE3,0,0}, // S_BSPI_RAISE2 + {SPR_BSPI,13,5,{0},S_BSPI_RAISE4,0,0}, // S_BSPI_RAISE3 + {SPR_BSPI,12,5,{0},S_BSPI_RAISE5,0,0}, // S_BSPI_RAISE4 + {SPR_BSPI,11,5,{0},S_BSPI_RAISE6,0,0}, // S_BSPI_RAISE5 + {SPR_BSPI,10,5,{0},S_BSPI_RAISE7,0,0}, // S_BSPI_RAISE6 + {SPR_BSPI,9,5,{0},S_BSPI_RUN1,0,0}, // S_BSPI_RAISE7 + {SPR_APLS,32768,5,{0},S_ARACH_PLAZ2,0,0}, // S_ARACH_PLAZ + {SPR_APLS,32769,5,{0},S_ARACH_PLAZ,0,0}, // S_ARACH_PLAZ2 + {SPR_APBX,32768,5,{0},S_ARACH_PLEX2,0,0}, // S_ARACH_PLEX + {SPR_APBX,32769,5,{0},S_ARACH_PLEX3,0,0}, // S_ARACH_PLEX2 + {SPR_APBX,32770,5,{0},S_ARACH_PLEX4,0,0}, // S_ARACH_PLEX3 + {SPR_APBX,32771,5,{0},S_ARACH_PLEX5,0,0}, // S_ARACH_PLEX4 + {SPR_APBX,32772,5,{0},S_NULL,0,0}, // S_ARACH_PLEX5 + {SPR_CYBR,0,10,{(actionf_p1)A_Look},S_CYBER_STND2,0,0}, // S_CYBER_STND + {SPR_CYBR,1,10,{(actionf_p1)A_Look},S_CYBER_STND,0,0}, // S_CYBER_STND2 + {SPR_CYBR,0,3,{(actionf_p1)A_Hoof},S_CYBER_RUN2,0,0}, // S_CYBER_RUN1 + {SPR_CYBR,0,3,{(actionf_p1)A_Chase},S_CYBER_RUN3,0,0}, // S_CYBER_RUN2 + {SPR_CYBR,1,3,{(actionf_p1)A_Chase},S_CYBER_RUN4,0,0}, // S_CYBER_RUN3 + {SPR_CYBR,1,3,{(actionf_p1)A_Chase},S_CYBER_RUN5,0,0}, // S_CYBER_RUN4 + {SPR_CYBR,2,3,{(actionf_p1)A_Chase},S_CYBER_RUN6,0,0}, // S_CYBER_RUN5 + {SPR_CYBR,2,3,{(actionf_p1)A_Chase},S_CYBER_RUN7,0,0}, // S_CYBER_RUN6 + {SPR_CYBR,3,3,{(actionf_p1)A_Metal},S_CYBER_RUN8,0,0}, // S_CYBER_RUN7 + {SPR_CYBR,3,3,{(actionf_p1)A_Chase},S_CYBER_RUN1,0,0}, // S_CYBER_RUN8 + {SPR_CYBR,4,6,{(actionf_p1)A_FaceTarget},S_CYBER_ATK2,0,0}, // S_CYBER_ATK1 + {SPR_CYBR,5,12,{(actionf_p1)A_CyberAttack},S_CYBER_ATK3,0,0}, // S_CYBER_ATK2 + {SPR_CYBR,4,12,{(actionf_p1)A_FaceTarget},S_CYBER_ATK4,0,0}, // S_CYBER_ATK3 + {SPR_CYBR,5,12,{(actionf_p1)A_CyberAttack},S_CYBER_ATK5,0,0}, // S_CYBER_ATK4 + {SPR_CYBR,4,12,{(actionf_p1)A_FaceTarget},S_CYBER_ATK6,0,0}, // S_CYBER_ATK5 + {SPR_CYBR,5,12,{(actionf_p1)A_CyberAttack},S_CYBER_RUN1,0,0}, // S_CYBER_ATK6 + {SPR_CYBR,6,10,{(actionf_p1)A_Pain},S_CYBER_RUN1,0,0}, // S_CYBER_PAIN + {SPR_CYBR,7,10,{0},S_CYBER_DIE2,0,0}, // S_CYBER_DIE1 + {SPR_CYBR,8,10,{(actionf_p1)A_Scream},S_CYBER_DIE3,0,0}, // S_CYBER_DIE2 + {SPR_CYBR,9,10,{0},S_CYBER_DIE4,0,0}, // S_CYBER_DIE3 + {SPR_CYBR,10,10,{0},S_CYBER_DIE5,0,0}, // S_CYBER_DIE4 + {SPR_CYBR,11,10,{0},S_CYBER_DIE6,0,0}, // S_CYBER_DIE5 + {SPR_CYBR,12,10,{(actionf_p1)A_Fall},S_CYBER_DIE7,0,0}, // S_CYBER_DIE6 + {SPR_CYBR,13,10,{0},S_CYBER_DIE8,0,0}, // S_CYBER_DIE7 + {SPR_CYBR,14,10,{0},S_CYBER_DIE9,0,0}, // S_CYBER_DIE8 + {SPR_CYBR,15,30,{0},S_CYBER_DIE10,0,0}, // S_CYBER_DIE9 + {SPR_CYBR,15,-1,{(actionf_p1)A_BossDeath},S_NULL,0,0}, // S_CYBER_DIE10 + {SPR_PAIN,0,10,{(actionf_p1)A_Look},S_PAIN_STND,0,0}, // S_PAIN_STND + {SPR_PAIN,0,3,{(actionf_p1)A_Chase},S_PAIN_RUN2,0,0}, // S_PAIN_RUN1 + {SPR_PAIN,0,3,{(actionf_p1)A_Chase},S_PAIN_RUN3,0,0}, // S_PAIN_RUN2 + {SPR_PAIN,1,3,{(actionf_p1)A_Chase},S_PAIN_RUN4,0,0}, // S_PAIN_RUN3 + {SPR_PAIN,1,3,{(actionf_p1)A_Chase},S_PAIN_RUN5,0,0}, // S_PAIN_RUN4 + {SPR_PAIN,2,3,{(actionf_p1)A_Chase},S_PAIN_RUN6,0,0}, // S_PAIN_RUN5 + {SPR_PAIN,2,3,{(actionf_p1)A_Chase},S_PAIN_RUN1,0,0}, // S_PAIN_RUN6 + {SPR_PAIN,3,5,{(actionf_p1)A_FaceTarget},S_PAIN_ATK2,0,0}, // S_PAIN_ATK1 + {SPR_PAIN,4,5,{(actionf_p1)A_FaceTarget},S_PAIN_ATK3,0,0}, // S_PAIN_ATK2 + {SPR_PAIN,32773,5,{(actionf_p1)A_FaceTarget},S_PAIN_ATK4,0,0}, // S_PAIN_ATK3 + {SPR_PAIN,32773,0,{(actionf_p1)A_PainAttack},S_PAIN_RUN1,0,0}, // S_PAIN_ATK4 + {SPR_PAIN,6,6,{0},S_PAIN_PAIN2,0,0}, // S_PAIN_PAIN + {SPR_PAIN,6,6,{(actionf_p1)A_Pain},S_PAIN_RUN1,0,0}, // S_PAIN_PAIN2 + {SPR_PAIN,32775,8,{0},S_PAIN_DIE2,0,0}, // S_PAIN_DIE1 + {SPR_PAIN,32776,8,{(actionf_p1)A_Scream},S_PAIN_DIE3,0,0}, // S_PAIN_DIE2 + {SPR_PAIN,32777,8,{0},S_PAIN_DIE4,0,0}, // S_PAIN_DIE3 + {SPR_PAIN,32778,8,{0},S_PAIN_DIE5,0,0}, // S_PAIN_DIE4 + {SPR_PAIN,32779,8,{(actionf_p1)A_PainDie},S_PAIN_DIE6,0,0}, // S_PAIN_DIE5 + {SPR_PAIN,32780,8,{0},S_NULL,0,0}, // S_PAIN_DIE6 + {SPR_PAIN,12,8,{0},S_PAIN_RAISE2,0,0}, // S_PAIN_RAISE1 + {SPR_PAIN,11,8,{0},S_PAIN_RAISE3,0,0}, // S_PAIN_RAISE2 + {SPR_PAIN,10,8,{0},S_PAIN_RAISE4,0,0}, // S_PAIN_RAISE3 + {SPR_PAIN,9,8,{0},S_PAIN_RAISE5,0,0}, // S_PAIN_RAISE4 + {SPR_PAIN,8,8,{0},S_PAIN_RAISE6,0,0}, // S_PAIN_RAISE5 + {SPR_PAIN,7,8,{0},S_PAIN_RUN1,0,0}, // S_PAIN_RAISE6 + {SPR_SSWV,0,10,{(actionf_p1)A_Look},S_SSWV_STND2,0,0}, // S_SSWV_STND + {SPR_SSWV,1,10,{(actionf_p1)A_Look},S_SSWV_STND,0,0}, // S_SSWV_STND2 + {SPR_SSWV,0,3,{(actionf_p1)A_Chase},S_SSWV_RUN2,0,0}, // S_SSWV_RUN1 + {SPR_SSWV,0,3,{(actionf_p1)A_Chase},S_SSWV_RUN3,0,0}, // S_SSWV_RUN2 + {SPR_SSWV,1,3,{(actionf_p1)A_Chase},S_SSWV_RUN4,0,0}, // S_SSWV_RUN3 + {SPR_SSWV,1,3,{(actionf_p1)A_Chase},S_SSWV_RUN5,0,0}, // S_SSWV_RUN4 + {SPR_SSWV,2,3,{(actionf_p1)A_Chase},S_SSWV_RUN6,0,0}, // S_SSWV_RUN5 + {SPR_SSWV,2,3,{(actionf_p1)A_Chase},S_SSWV_RUN7,0,0}, // S_SSWV_RUN6 + {SPR_SSWV,3,3,{(actionf_p1)A_Chase},S_SSWV_RUN8,0,0}, // S_SSWV_RUN7 + {SPR_SSWV,3,3,{(actionf_p1)A_Chase},S_SSWV_RUN1,0,0}, // S_SSWV_RUN8 + {SPR_SSWV,4,10,{(actionf_p1)A_FaceTarget},S_SSWV_ATK2,0,0}, // S_SSWV_ATK1 + {SPR_SSWV,5,10,{(actionf_p1)A_FaceTarget},S_SSWV_ATK3,0,0}, // S_SSWV_ATK2 + {SPR_SSWV,32774,4,{(actionf_p1)A_CPosAttack},S_SSWV_ATK4,0,0}, // S_SSWV_ATK3 + {SPR_SSWV,5,6,{(actionf_p1)A_FaceTarget},S_SSWV_ATK5,0,0}, // S_SSWV_ATK4 + {SPR_SSWV,32774,4,{(actionf_p1)A_CPosAttack},S_SSWV_ATK6,0,0}, // S_SSWV_ATK5 + {SPR_SSWV,5,1,{(actionf_p1)A_CPosRefire},S_SSWV_ATK2,0,0}, // S_SSWV_ATK6 + {SPR_SSWV,7,3,{0},S_SSWV_PAIN2,0,0}, // S_SSWV_PAIN + {SPR_SSWV,7,3,{(actionf_p1)A_Pain},S_SSWV_RUN1,0,0}, // S_SSWV_PAIN2 + {SPR_SSWV,8,5,{0},S_SSWV_DIE2,0,0}, // S_SSWV_DIE1 + {SPR_SSWV,9,5,{(actionf_p1)A_Scream},S_SSWV_DIE3,0,0}, // S_SSWV_DIE2 + {SPR_SSWV,10,5,{(actionf_p1)A_Fall},S_SSWV_DIE4,0,0}, // S_SSWV_DIE3 + {SPR_SSWV,11,5,{0},S_SSWV_DIE5,0,0}, // S_SSWV_DIE4 + {SPR_SSWV,12,-1,{0},S_NULL,0,0}, // S_SSWV_DIE5 + {SPR_SSWV,13,5,{0},S_SSWV_XDIE2,0,0}, // S_SSWV_XDIE1 + {SPR_SSWV,14,5,{(actionf_p1)A_XScream},S_SSWV_XDIE3,0,0}, // S_SSWV_XDIE2 + {SPR_SSWV,15,5,{(actionf_p1)A_Fall},S_SSWV_XDIE4,0,0}, // S_SSWV_XDIE3 + {SPR_SSWV,16,5,{0},S_SSWV_XDIE5,0,0}, // S_SSWV_XDIE4 + {SPR_SSWV,17,5,{0},S_SSWV_XDIE6,0,0}, // S_SSWV_XDIE5 + {SPR_SSWV,18,5,{0},S_SSWV_XDIE7,0,0}, // S_SSWV_XDIE6 + {SPR_SSWV,19,5,{0},S_SSWV_XDIE8,0,0}, // S_SSWV_XDIE7 + {SPR_SSWV,20,5,{0},S_SSWV_XDIE9,0,0}, // S_SSWV_XDIE8 + {SPR_SSWV,21,-1,{0},S_NULL,0,0}, // S_SSWV_XDIE9 + {SPR_SSWV,12,5,{0},S_SSWV_RAISE2,0,0}, // S_SSWV_RAISE1 + {SPR_SSWV,11,5,{0},S_SSWV_RAISE3,0,0}, // S_SSWV_RAISE2 + {SPR_SSWV,10,5,{0},S_SSWV_RAISE4,0,0}, // S_SSWV_RAISE3 + {SPR_SSWV,9,5,{0},S_SSWV_RAISE5,0,0}, // S_SSWV_RAISE4 + {SPR_SSWV,8,5,{0},S_SSWV_RUN1,0,0}, // S_SSWV_RAISE5 + {SPR_KEEN,0,-1,{0},S_KEENSTND,0,0}, // S_KEENSTND + {SPR_KEEN,0,6,{0},S_COMMKEEN2,0,0}, // S_COMMKEEN + {SPR_KEEN,1,6,{0},S_COMMKEEN3,0,0}, // S_COMMKEEN2 + {SPR_KEEN,2,6,{(actionf_p1)A_Scream},S_COMMKEEN4,0,0}, // S_COMMKEEN3 + {SPR_KEEN,3,6,{0},S_COMMKEEN5,0,0}, // S_COMMKEEN4 + {SPR_KEEN,4,6,{0},S_COMMKEEN6,0,0}, // S_COMMKEEN5 + {SPR_KEEN,5,6,{0},S_COMMKEEN7,0,0}, // S_COMMKEEN6 + {SPR_KEEN,6,6,{0},S_COMMKEEN8,0,0}, // S_COMMKEEN7 + {SPR_KEEN,7,6,{0},S_COMMKEEN9,0,0}, // S_COMMKEEN8 + {SPR_KEEN,8,6,{0},S_COMMKEEN10,0,0}, // S_COMMKEEN9 + {SPR_KEEN,9,6,{0},S_COMMKEEN11,0,0}, // S_COMMKEEN10 + {SPR_KEEN,10,6,{(actionf_p1)A_KeenDie},S_COMMKEEN12,0,0},// S_COMMKEEN11 + {SPR_KEEN,11,-1,{0},S_NULL,0,0}, // S_COMMKEEN12 + {SPR_KEEN,12,4,{0},S_KEENPAIN2,0,0}, // S_KEENPAIN + {SPR_KEEN,12,8,{(actionf_p1)A_Pain},S_KEENSTND,0,0}, // S_KEENPAIN2 + {SPR_BBRN,0,-1,{0},S_NULL,0,0}, // S_BRAIN + {SPR_BBRN,1,36,{(actionf_p1)A_BrainPain},S_BRAIN,0,0}, // S_BRAIN_PAIN + {SPR_BBRN,0,100,{(actionf_p1)A_BrainScream},S_BRAIN_DIE2,0,0}, // S_BRAIN_DIE1 + {SPR_BBRN,0,10,{0},S_BRAIN_DIE3,0,0}, // S_BRAIN_DIE2 + {SPR_BBRN,0,10,{0},S_BRAIN_DIE4,0,0}, // S_BRAIN_DIE3 + {SPR_BBRN,0,-1,{(actionf_p1)A_BrainDie},S_NULL,0,0}, // S_BRAIN_DIE4 + {SPR_SSWV,0,10,{(actionf_p1)A_Look},S_BRAINEYE,0,0}, // S_BRAINEYE + {SPR_SSWV,0,181,{(actionf_p1)A_BrainAwake},S_BRAINEYE1,0,0}, // S_BRAINEYESEE + {SPR_SSWV,0,150,{(actionf_p1)A_BrainSpit},S_BRAINEYE1,0,0}, // S_BRAINEYE1 + {SPR_BOSF,32768,3,{(actionf_p1)A_SpawnSound},S_SPAWN2,0,0}, // S_SPAWN1 + {SPR_BOSF,32769,3,{(actionf_p1)A_SpawnFly},S_SPAWN3,0,0}, // S_SPAWN2 + {SPR_BOSF,32770,3,{(actionf_p1)A_SpawnFly},S_SPAWN4,0,0}, // S_SPAWN3 + {SPR_BOSF,32771,3,{(actionf_p1)A_SpawnFly},S_SPAWN1,0,0}, // S_SPAWN4 + {SPR_FIRE,32768,4,{(actionf_p1)A_Fire},S_SPAWNFIRE2,0,0}, // S_SPAWNFIRE1 + {SPR_FIRE,32769,4,{(actionf_p1)A_Fire},S_SPAWNFIRE3,0,0}, // S_SPAWNFIRE2 + {SPR_FIRE,32770,4,{(actionf_p1)A_Fire},S_SPAWNFIRE4,0,0}, // S_SPAWNFIRE3 + {SPR_FIRE,32771,4,{(actionf_p1)A_Fire},S_SPAWNFIRE5,0,0}, // S_SPAWNFIRE4 + {SPR_FIRE,32772,4,{(actionf_p1)A_Fire},S_SPAWNFIRE6,0,0}, // S_SPAWNFIRE5 + {SPR_FIRE,32773,4,{(actionf_p1)A_Fire},S_SPAWNFIRE7,0,0}, // S_SPAWNFIRE6 + {SPR_FIRE,32774,4,{(actionf_p1)A_Fire},S_SPAWNFIRE8,0,0}, // S_SPAWNFIRE7 + {SPR_FIRE,32775,4,{(actionf_p1)A_Fire},S_NULL,0,0}, // S_SPAWNFIRE8 + {SPR_MISL,32769,10,{0},S_BRAINEXPLODE2,0,0}, // S_BRAINEXPLODE1 + {SPR_MISL,32770,10,{0},S_BRAINEXPLODE3,0,0}, // S_BRAINEXPLODE2 + {SPR_MISL,32771,10,{(actionf_p1)A_BrainExplode},S_NULL,0,0}, // S_BRAINEXPLODE3 + {SPR_ARM1,0,6,{0},S_ARM1A,0,0}, // S_ARM1 + {SPR_ARM1,32769,7,{0},S_ARM1,0,0}, // S_ARM1A + {SPR_ARM2,0,6,{0},S_ARM2A,0,0}, // S_ARM2 + {SPR_ARM2,32769,6,{0},S_ARM2,0,0}, // S_ARM2A + {SPR_BAR1,0,6,{0},S_BAR2,0,0}, // S_BAR1 + {SPR_BAR1,1,6,{0},S_BAR1,0,0}, // S_BAR2 + {SPR_BEXP,32768,5,{0},S_BEXP2,0,0}, // S_BEXP + {SPR_BEXP,32769,5,{(actionf_p1)A_Scream},S_BEXP3,0,0}, // S_BEXP2 + {SPR_BEXP,32770,5,{0},S_BEXP4,0,0}, // S_BEXP3 + {SPR_BEXP,32771,10,{(actionf_p1)A_Explode},S_BEXP5,0,0}, // S_BEXP4 + {SPR_BEXP,32772,10,{0},S_NULL,0,0}, // S_BEXP5 + {SPR_FCAN,32768,4,{0},S_BBAR2,0,0}, // S_BBAR1 + {SPR_FCAN,32769,4,{0},S_BBAR3,0,0}, // S_BBAR2 + {SPR_FCAN,32770,4,{0},S_BBAR1,0,0}, // S_BBAR3 + {SPR_BON1,0,6,{0},S_BON1A,0,0}, // S_BON1 + {SPR_BON1,1,6,{0},S_BON1B,0,0}, // S_BON1A + {SPR_BON1,2,6,{0},S_BON1C,0,0}, // S_BON1B + {SPR_BON1,3,6,{0},S_BON1D,0,0}, // S_BON1C + {SPR_BON1,2,6,{0},S_BON1E,0,0}, // S_BON1D + {SPR_BON1,1,6,{0},S_BON1,0,0}, // S_BON1E + {SPR_BON2,0,6,{0},S_BON2A,0,0}, // S_BON2 + {SPR_BON2,1,6,{0},S_BON2B,0,0}, // S_BON2A + {SPR_BON2,2,6,{0},S_BON2C,0,0}, // S_BON2B + {SPR_BON2,3,6,{0},S_BON2D,0,0}, // S_BON2C + {SPR_BON2,2,6,{0},S_BON2E,0,0}, // S_BON2D + {SPR_BON2,1,6,{0},S_BON2,0,0}, // S_BON2E + {SPR_BKEY,0,10,{0},S_BKEY2,0,0}, // S_BKEY + {SPR_BKEY,32769,10,{0},S_BKEY,0,0}, // S_BKEY2 + {SPR_RKEY,0,10,{0},S_RKEY2,0,0}, // S_RKEY + {SPR_RKEY,32769,10,{0},S_RKEY,0,0}, // S_RKEY2 + {SPR_YKEY,0,10,{0},S_YKEY2,0,0}, // S_YKEY + {SPR_YKEY,32769,10,{0},S_YKEY,0,0}, // S_YKEY2 + {SPR_BSKU,0,10,{0},S_BSKULL2,0,0}, // S_BSKULL + {SPR_BSKU,32769,10,{0},S_BSKULL,0,0}, // S_BSKULL2 + {SPR_RSKU,0,10,{0},S_RSKULL2,0,0}, // S_RSKULL + {SPR_RSKU,32769,10,{0},S_RSKULL,0,0}, // S_RSKULL2 + {SPR_YSKU,0,10,{0},S_YSKULL2,0,0}, // S_YSKULL + {SPR_YSKU,32769,10,{0},S_YSKULL,0,0}, // S_YSKULL2 + {SPR_STIM,0,-1,{0},S_NULL,0,0}, // S_STIM + {SPR_MEDI,0,-1,{0},S_NULL,0,0}, // S_MEDI + {SPR_SOUL,32768,6,{0},S_SOUL2,0,0}, // S_SOUL + {SPR_SOUL,32769,6,{0},S_SOUL3,0,0}, // S_SOUL2 + {SPR_SOUL,32770,6,{0},S_SOUL4,0,0}, // S_SOUL3 + {SPR_SOUL,32771,6,{0},S_SOUL5,0,0}, // S_SOUL4 + {SPR_SOUL,32770,6,{0},S_SOUL6,0,0}, // S_SOUL5 + {SPR_SOUL,32769,6,{0},S_SOUL,0,0}, // S_SOUL6 + {SPR_PINV,32768,6,{0},S_PINV2,0,0}, // S_PINV + {SPR_PINV,32769,6,{0},S_PINV3,0,0}, // S_PINV2 + {SPR_PINV,32770,6,{0},S_PINV4,0,0}, // S_PINV3 + {SPR_PINV,32771,6,{0},S_PINV,0,0}, // S_PINV4 + {SPR_PSTR,32768,-1,{0},S_NULL,0,0}, // S_PSTR + {SPR_PINS,32768,6,{0},S_PINS2,0,0}, // S_PINS + {SPR_PINS,32769,6,{0},S_PINS3,0,0}, // S_PINS2 + {SPR_PINS,32770,6,{0},S_PINS4,0,0}, // S_PINS3 + {SPR_PINS,32771,6,{0},S_PINS,0,0}, // S_PINS4 + {SPR_MEGA,32768,6,{0},S_MEGA2,0,0}, // S_MEGA + {SPR_MEGA,32769,6,{0},S_MEGA3,0,0}, // S_MEGA2 + {SPR_MEGA,32770,6,{0},S_MEGA4,0,0}, // S_MEGA3 + {SPR_MEGA,32771,6,{0},S_MEGA,0,0}, // S_MEGA4 + {SPR_SUIT,32768,-1,{0},S_NULL,0,0}, // S_SUIT + {SPR_PMAP,32768,6,{0},S_PMAP2,0,0}, // S_PMAP + {SPR_PMAP,32769,6,{0},S_PMAP3,0,0}, // S_PMAP2 + {SPR_PMAP,32770,6,{0},S_PMAP4,0,0}, // S_PMAP3 + {SPR_PMAP,32771,6,{0},S_PMAP5,0,0}, // S_PMAP4 + {SPR_PMAP,32770,6,{0},S_PMAP6,0,0}, // S_PMAP5 + {SPR_PMAP,32769,6,{0},S_PMAP,0,0}, // S_PMAP6 + {SPR_PVIS,32768,6,{0},S_PVIS2,0,0}, // S_PVIS + {SPR_PVIS,1,6,{0},S_PVIS,0,0}, // S_PVIS2 + {SPR_CLIP,0,-1,{0},S_NULL,0,0}, // S_CLIP + {SPR_AMMO,0,-1,{0},S_NULL,0,0}, // S_AMMO + {SPR_ROCK,0,-1,{0},S_NULL,0,0}, // S_ROCK + {SPR_BROK,0,-1,{0},S_NULL,0,0}, // S_BROK + {SPR_CELL,0,-1,{0},S_NULL,0,0}, // S_CELL + {SPR_CELP,0,-1,{0},S_NULL,0,0}, // S_CELP + {SPR_SHEL,0,-1,{0},S_NULL,0,0}, // S_SHEL + {SPR_SBOX,0,-1,{0},S_NULL,0,0}, // S_SBOX + {SPR_BPAK,0,-1,{0},S_NULL,0,0}, // S_BPAK + {SPR_BFUG,0,-1,{0},S_NULL,0,0}, // S_BFUG + {SPR_MGUN,0,-1,{0},S_NULL,0,0}, // S_MGUN + {SPR_CSAW,0,-1,{0},S_NULL,0,0}, // S_CSAW + {SPR_LAUN,0,-1,{0},S_NULL,0,0}, // S_LAUN + {SPR_PLAS,0,-1,{0},S_NULL,0,0}, // S_PLAS + {SPR_SHOT,0,-1,{0},S_NULL,0,0}, // S_SHOT + {SPR_SGN2,0,-1,{0},S_NULL,0,0}, // S_SHOT2 + {SPR_COLU,32768,-1,{0},S_NULL,0,0}, // S_COLU + {SPR_SMT2,0,-1,{0},S_NULL,0,0}, // S_STALAG + {SPR_GOR1,0,10,{0},S_BLOODYTWITCH2,0,0}, // S_BLOODYTWITCH + {SPR_GOR1,1,15,{0},S_BLOODYTWITCH3,0,0}, // S_BLOODYTWITCH2 + {SPR_GOR1,2,8,{0},S_BLOODYTWITCH4,0,0}, // S_BLOODYTWITCH3 + {SPR_GOR1,1,6,{0},S_BLOODYTWITCH,0,0}, // S_BLOODYTWITCH4 + {SPR_PLAY,13,-1,{0},S_NULL,0,0}, // S_DEADTORSO + {SPR_PLAY,18,-1,{0},S_NULL,0,0}, // S_DEADBOTTOM + {SPR_POL2,0,-1,{0},S_NULL,0,0}, // S_HEADSONSTICK + {SPR_POL5,0,-1,{0},S_NULL,0,0}, // S_GIBS + {SPR_POL4,0,-1,{0},S_NULL,0,0}, // S_HEADONASTICK + {SPR_POL3,32768,6,{0},S_HEADCANDLES2,0,0}, // S_HEADCANDLES + {SPR_POL3,32769,6,{0},S_HEADCANDLES,0,0}, // S_HEADCANDLES2 + {SPR_POL1,0,-1,{0},S_NULL,0,0}, // S_DEADSTICK + {SPR_POL6,0,6,{0},S_LIVESTICK2,0,0}, // S_LIVESTICK + {SPR_POL6,1,8,{0},S_LIVESTICK,0,0}, // S_LIVESTICK2 + {SPR_GOR2,0,-1,{0},S_NULL,0,0}, // S_MEAT2 + {SPR_GOR3,0,-1,{0},S_NULL,0,0}, // S_MEAT3 + {SPR_GOR4,0,-1,{0},S_NULL,0,0}, // S_MEAT4 + {SPR_GOR5,0,-1,{0},S_NULL,0,0}, // S_MEAT5 + {SPR_SMIT,0,-1,{0},S_NULL,0,0}, // S_STALAGTITE + {SPR_COL1,0,-1,{0},S_NULL,0,0}, // S_TALLGRNCOL + {SPR_COL2,0,-1,{0},S_NULL,0,0}, // S_SHRTGRNCOL + {SPR_COL3,0,-1,{0},S_NULL,0,0}, // S_TALLREDCOL + {SPR_COL4,0,-1,{0},S_NULL,0,0}, // S_SHRTREDCOL + {SPR_CAND,32768,-1,{0},S_NULL,0,0}, // S_CANDLESTIK + {SPR_CBRA,32768,-1,{0},S_NULL,0,0}, // S_CANDELABRA + {SPR_COL6,0,-1,{0},S_NULL,0,0}, // S_SKULLCOL + {SPR_TRE1,0,-1,{0},S_NULL,0,0}, // S_TORCHTREE + {SPR_TRE2,0,-1,{0},S_NULL,0,0}, // S_BIGTREE + {SPR_ELEC,0,-1,{0},S_NULL,0,0}, // S_TECHPILLAR + {SPR_CEYE,32768,6,{0},S_EVILEYE2,0,0}, // S_EVILEYE + {SPR_CEYE,32769,6,{0},S_EVILEYE3,0,0}, // S_EVILEYE2 + {SPR_CEYE,32770,6,{0},S_EVILEYE4,0,0}, // S_EVILEYE3 + {SPR_CEYE,32769,6,{0},S_EVILEYE,0,0}, // S_EVILEYE4 + {SPR_FSKU,32768,6,{0},S_FLOATSKULL2,0,0}, // S_FLOATSKULL + {SPR_FSKU,32769,6,{0},S_FLOATSKULL3,0,0}, // S_FLOATSKULL2 + {SPR_FSKU,32770,6,{0},S_FLOATSKULL,0,0}, // S_FLOATSKULL3 + {SPR_COL5,0,14,{0},S_HEARTCOL2,0,0}, // S_HEARTCOL + {SPR_COL5,1,14,{0},S_HEARTCOL,0,0}, // S_HEARTCOL2 + {SPR_TBLU,32768,4,{0},S_BLUETORCH2,0,0}, // S_BLUETORCH + {SPR_TBLU,32769,4,{0},S_BLUETORCH3,0,0}, // S_BLUETORCH2 + {SPR_TBLU,32770,4,{0},S_BLUETORCH4,0,0}, // S_BLUETORCH3 + {SPR_TBLU,32771,4,{0},S_BLUETORCH,0,0}, // S_BLUETORCH4 + {SPR_TGRN,32768,4,{0},S_GREENTORCH2,0,0}, // S_GREENTORCH + {SPR_TGRN,32769,4,{0},S_GREENTORCH3,0,0}, // S_GREENTORCH2 + {SPR_TGRN,32770,4,{0},S_GREENTORCH4,0,0}, // S_GREENTORCH3 + {SPR_TGRN,32771,4,{0},S_GREENTORCH,0,0}, // S_GREENTORCH4 + {SPR_TRED,32768,4,{0},S_REDTORCH2,0,0}, // S_REDTORCH + {SPR_TRED,32769,4,{0},S_REDTORCH3,0,0}, // S_REDTORCH2 + {SPR_TRED,32770,4,{0},S_REDTORCH4,0,0}, // S_REDTORCH3 + {SPR_TRED,32771,4,{0},S_REDTORCH,0,0}, // S_REDTORCH4 + {SPR_SMBT,32768,4,{0},S_BTORCHSHRT2,0,0}, // S_BTORCHSHRT + {SPR_SMBT,32769,4,{0},S_BTORCHSHRT3,0,0}, // S_BTORCHSHRT2 + {SPR_SMBT,32770,4,{0},S_BTORCHSHRT4,0,0}, // S_BTORCHSHRT3 + {SPR_SMBT,32771,4,{0},S_BTORCHSHRT,0,0}, // S_BTORCHSHRT4 + {SPR_SMGT,32768,4,{0},S_GTORCHSHRT2,0,0}, // S_GTORCHSHRT + {SPR_SMGT,32769,4,{0},S_GTORCHSHRT3,0,0}, // S_GTORCHSHRT2 + {SPR_SMGT,32770,4,{0},S_GTORCHSHRT4,0,0}, // S_GTORCHSHRT3 + {SPR_SMGT,32771,4,{0},S_GTORCHSHRT,0,0}, // S_GTORCHSHRT4 + {SPR_SMRT,32768,4,{0},S_RTORCHSHRT2,0,0}, // S_RTORCHSHRT + {SPR_SMRT,32769,4,{0},S_RTORCHSHRT3,0,0}, // S_RTORCHSHRT2 + {SPR_SMRT,32770,4,{0},S_RTORCHSHRT4,0,0}, // S_RTORCHSHRT3 + {SPR_SMRT,32771,4,{0},S_RTORCHSHRT,0,0}, // S_RTORCHSHRT4 + {SPR_HDB1,0,-1,{0},S_NULL,0,0}, // S_HANGNOGUTS + {SPR_HDB2,0,-1,{0},S_NULL,0,0}, // S_HANGBNOBRAIN + {SPR_HDB3,0,-1,{0},S_NULL,0,0}, // S_HANGTLOOKDN + {SPR_HDB4,0,-1,{0},S_NULL,0,0}, // S_HANGTSKULL + {SPR_HDB5,0,-1,{0},S_NULL,0,0}, // S_HANGTLOOKUP + {SPR_HDB6,0,-1,{0},S_NULL,0,0}, // S_HANGTNOBRAIN + {SPR_POB1,0,-1,{0},S_NULL,0,0}, // S_COLONGIBS + {SPR_POB2,0,-1,{0},S_NULL,0,0}, // S_SMALLPOOL + {SPR_BRS1,0,-1,{0},S_NULL,0,0}, // S_BRAINSTEM + {SPR_TLMP,32768,4,{0},S_TECHLAMP2,0,0}, // S_TECHLAMP + {SPR_TLMP,32769,4,{0},S_TECHLAMP3,0,0}, // S_TECHLAMP2 + {SPR_TLMP,32770,4,{0},S_TECHLAMP4,0,0}, // S_TECHLAMP3 + {SPR_TLMP,32771,4,{0},S_TECHLAMP,0,0}, // S_TECHLAMP4 + {SPR_TLP2,32768,4,{0},S_TECH2LAMP2,0,0}, // S_TECH2LAMP + {SPR_TLP2,32769,4,{0},S_TECH2LAMP3,0,0}, // S_TECH2LAMP2 + {SPR_TLP2,32770,4,{0},S_TECH2LAMP4,0,0}, // S_TECH2LAMP3 + {SPR_TLP2,32771,4,{0},S_TECH2LAMP,0,0} // S_TECH2LAMP4 +}; + + +mobjinfo_t mobjinfo[NUMMOBJTYPES] = { + + { // MT_PLAYER + -1, // doomednum + S_PLAY, // spawnstate + 100, // spawnhealth + S_PLAY_RUN1, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_PLAY_PAIN, // painstate + 255, // painchance + sfx_plpain, // painsound + S_NULL, // meleestate + S_PLAY_ATK1, // missilestate + S_PLAY_DIE1, // deathstate + S_PLAY_XDIE1, // xdeathstate + sfx_pldeth, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SHOOTABLE | MF_DROPOFF | MF_PICKUP | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_POSSESSED + 3004, // doomednum + S_POSS_STND, // spawnstate + 20, // spawnhealth + S_POSS_RUN1, // seestate + sfx_posit1, // seesound + 8, // reactiontime + sfx_pistol, // attacksound + S_POSS_PAIN, // painstate + 200, // painchance + sfx_popain, // painsound + 0, // meleestate + S_POSS_ATK1, // missilestate + S_POSS_DIE1, // deathstate + S_POSS_XDIE1, // xdeathstate + sfx_podth1, // deathsound + 8, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_POSS_RAISE1 // raisestate + }, + + { // MT_SHOTGUY + 9, // doomednum + S_SPOS_STND, // spawnstate + 30, // spawnhealth + S_SPOS_RUN1, // seestate + sfx_posit2, // seesound + 8, // reactiontime + 0, // attacksound + S_SPOS_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_SPOS_ATK1, // missilestate + S_SPOS_DIE1, // deathstate + S_SPOS_XDIE1, // xdeathstate + sfx_podth2, // deathsound + 8, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_SPOS_RAISE1 // raisestate + }, + + { // MT_VILE + 64, // doomednum + S_VILE_STND, // spawnstate + 700, // spawnhealth + S_VILE_RUN1, // seestate + sfx_vilsit, // seesound + 8, // reactiontime + 0, // attacksound + S_VILE_PAIN, // painstate + 10, // painchance + sfx_vipain, // painsound + 0, // meleestate + S_VILE_ATK1, // missilestate + S_VILE_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_vildth, // deathsound + 15, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 500, // mass + 0, // damage + sfx_vilact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_FIRE + -1, // doomednum + S_FIRE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_UNDEAD + 66, // doomednum + S_SKEL_STND, // spawnstate + 300, // spawnhealth + S_SKEL_RUN1, // seestate + sfx_skesit, // seesound + 8, // reactiontime + 0, // attacksound + S_SKEL_PAIN, // painstate + 100, // painchance + sfx_popain, // painsound + S_SKEL_FIST1, // meleestate + S_SKEL_MISS1, // missilestate + S_SKEL_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_skedth, // deathsound + 10, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 500, // mass + 0, // damage + sfx_skeact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_SKEL_RAISE1 // raisestate + }, + + { // MT_TRACER + -1, // doomednum + S_TRACER, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_skeatk, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_TRACEEXP1, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 10 * FRACUNIT, // speed + 11 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 10, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SMOKE + -1, // doomednum + S_SMOKE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_FATSO + 67, // doomednum + S_FATT_STND, // spawnstate + 600, // spawnhealth + S_FATT_RUN1, // seestate + sfx_mansit, // seesound + 8, // reactiontime + 0, // attacksound + S_FATT_PAIN, // painstate + 80, // painchance + sfx_mnpain, // painsound + 0, // meleestate + S_FATT_ATK1, // missilestate + S_FATT_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_mandth, // deathsound + 8, // speed + 48 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_FATT_RAISE1 // raisestate + }, + + { // MT_FATSHOT + -1, // doomednum + S_FATSHOT1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_FATSHOTX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 20 * FRACUNIT, // speed + 6 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_CHAINGUY + 65, // doomednum + S_CPOS_STND, // spawnstate + 70, // spawnhealth + S_CPOS_RUN1, // seestate + sfx_posit2, // seesound + 8, // reactiontime + 0, // attacksound + S_CPOS_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_CPOS_ATK1, // missilestate + S_CPOS_DIE1, // deathstate + S_CPOS_XDIE1, // xdeathstate + sfx_podth2, // deathsound + 8, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_CPOS_RAISE1 // raisestate + }, + + { // MT_TROOP + 3001, // doomednum + S_TROO_STND, // spawnstate + 60, // spawnhealth + S_TROO_RUN1, // seestate + sfx_bgsit1, // seesound + 8, // reactiontime + 0, // attacksound + S_TROO_PAIN, // painstate + 200, // painchance + sfx_popain, // painsound + S_TROO_ATK1, // meleestate + S_TROO_ATK1, // missilestate + S_TROO_DIE1, // deathstate + S_TROO_XDIE1, // xdeathstate + sfx_bgdth1, // deathsound + 8, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_bgact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_TROO_RAISE1 // raisestate + }, + + { // MT_SERGEANT + 3002, // doomednum + S_SARG_STND, // spawnstate + 150, // spawnhealth + S_SARG_RUN1, // seestate + sfx_sgtsit, // seesound + 8, // reactiontime + sfx_sgtatk, // attacksound + S_SARG_PAIN, // painstate + 180, // painchance + sfx_dmpain, // painsound + S_SARG_ATK1, // meleestate + 0, // missilestate + S_SARG_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_sgtdth, // deathsound + 10, // speed + 30 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_SARG_RAISE1 // raisestate + }, + + { // MT_SHADOWS + 58, // doomednum + S_SARG_STND, // spawnstate + 150, // spawnhealth + S_SARG_RUN1, // seestate + sfx_sgtsit, // seesound + 8, // reactiontime + sfx_sgtatk, // attacksound + S_SARG_PAIN, // painstate + 180, // painchance + sfx_dmpain, // painsound + S_SARG_ATK1, // meleestate + 0, // missilestate + S_SARG_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_sgtdth, // deathsound + 10, // speed + 30 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_SHADOW | MF_COUNTKILL, // flags + S_SARG_RAISE1 // raisestate + }, + + { // MT_HEAD + 3005, // doomednum + S_HEAD_STND, // spawnstate + 400, // spawnhealth + S_HEAD_RUN1, // seestate + sfx_cacsit, // seesound + 8, // reactiontime + 0, // attacksound + S_HEAD_PAIN, // painstate + 128, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_HEAD_ATK1, // missilestate + S_HEAD_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_cacdth, // deathsound + 8, // speed + 31 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY | MF_COUNTKILL, // flags + S_HEAD_RAISE1 // raisestate + }, + + { // MT_BRUISER + 3003, // doomednum + S_BOSS_STND, // spawnstate + 1000, // spawnhealth + S_BOSS_RUN1, // seestate + sfx_brssit, // seesound + 8, // reactiontime + 0, // attacksound + S_BOSS_PAIN, // painstate + 50, // painchance + sfx_dmpain, // painsound + S_BOSS_ATK1, // meleestate + S_BOSS_ATK1, // missilestate + S_BOSS_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_brsdth, // deathsound + 8, // speed + 24 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_BOSS_RAISE1 // raisestate + }, + + { // MT_BRUISERSHOT + -1, // doomednum + S_BRBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BRBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 15 * FRACUNIT, // speed + 6 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_KNIGHT + 69, // doomednum + S_BOS2_STND, // spawnstate + 500, // spawnhealth + S_BOS2_RUN1, // seestate + sfx_kntsit, // seesound + 8, // reactiontime + 0, // attacksound + S_BOS2_PAIN, // painstate + 50, // painchance + sfx_dmpain, // painsound + S_BOS2_ATK1, // meleestate + S_BOS2_ATK1, // missilestate + S_BOS2_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_kntdth, // deathsound + 8, // speed + 24 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_BOS2_RAISE1 // raisestate + }, + + { // MT_SKULL + 3006, // doomednum + S_SKULL_STND, // spawnstate + 100, // spawnhealth + S_SKULL_RUN1, // seestate + 0, // seesound + 8, // reactiontime + sfx_sklatk, // attacksound + S_SKULL_PAIN, // painstate + 256, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_SKULL_ATK1, // missilestate + S_SKULL_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 8, // speed + 16 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 50, // mass + 3, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SPIDER + 7, // doomednum + S_SPID_STND, // spawnstate + 3000, // spawnhealth + S_SPID_RUN1, // seestate + sfx_spisit, // seesound + 8, // reactiontime + sfx_shotgn, // attacksound + S_SPID_PAIN, // painstate + 40, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_SPID_ATK1, // missilestate + S_SPID_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_spidth, // deathsound + 12, // speed + 128 * FRACUNIT, // radius + 100 * FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_BABY + 68, // doomednum + S_BSPI_STND, // spawnstate + 500, // spawnhealth + S_BSPI_SIGHT, // seestate + sfx_bspsit, // seesound + 8, // reactiontime + 0, // attacksound + S_BSPI_PAIN, // painstate + 128, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_BSPI_ATK1, // missilestate + S_BSPI_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_bspdth, // deathsound + 12, // speed + 64 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 600, // mass + 0, // damage + sfx_bspact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_BSPI_RAISE1 // raisestate + }, + + { // MT_CYBORG + 16, // doomednum + S_CYBER_STND, // spawnstate + 4000, // spawnhealth + S_CYBER_RUN1, // seestate + sfx_cybsit, // seesound + 8, // reactiontime + 0, // attacksound + S_CYBER_PAIN, // painstate + 20, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_CYBER_ATK1, // missilestate + S_CYBER_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_cybdth, // deathsound + 16, // speed + 40 * FRACUNIT, // radius + 110 * FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_PAIN + 71, // doomednum + S_PAIN_STND, // spawnstate + 400, // spawnhealth + S_PAIN_RUN1, // seestate + sfx_pesit, // seesound + 8, // reactiontime + 0, // attacksound + S_PAIN_PAIN, // painstate + 128, // painchance + sfx_pepain, // painsound + 0, // meleestate + S_PAIN_ATK1, // missilestate + S_PAIN_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_pedth, // deathsound + 8, // speed + 31 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY | MF_COUNTKILL, // flags + S_PAIN_RAISE1 // raisestate + }, + + { // MT_WOLFSS + 84, // doomednum + S_SSWV_STND, // spawnstate + 50, // spawnhealth + S_SSWV_RUN1, // seestate + sfx_sssit, // seesound + 8, // reactiontime + 0, // attacksound + S_SSWV_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_SSWV_ATK1, // missilestate + S_SSWV_DIE1, // deathstate + S_SSWV_XDIE1, // xdeathstate + sfx_ssdth, // deathsound + 8, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_SSWV_RAISE1 // raisestate + }, + + { // MT_KEEN + 72, // doomednum + S_KEENSTND, // spawnstate + 100, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_KEENPAIN, // painstate + 256, // painchance + sfx_keenpn, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_COMMKEEN, // deathstate + S_NULL, // xdeathstate + sfx_keendt, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 72 * FRACUNIT, // height + 10000000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_BOSSBRAIN + 88, // doomednum + S_BRAIN, // spawnstate + 250, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_BRAIN_PAIN, // painstate + 255, // painchance + sfx_bospn, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BRAIN_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_bosdth, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 10000000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SHOOTABLE, // flags + S_NULL // raisestate + }, + + { // MT_BOSSSPIT + 89, // doomednum + S_BRAINEYE, // spawnstate + 1000, // spawnhealth + S_BRAINEYESEE, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 32 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_BOSSTARGET + 87, // doomednum + S_NULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 32 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_SPAWNSHOT + -1, // doomednum + S_SPAWN1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_bospit, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10 * FRACUNIT, // speed + 6 * FRACUNIT, // radius + 32 * FRACUNIT, // height + 100, // mass + 3, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY | MF_NOCLIP, // flags + S_NULL // raisestate + }, + + { // MT_SPAWNFIRE + -1, // doomednum + S_SPAWNFIRE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_BARREL + 2035, // doomednum + S_BAR1, // spawnstate + 20, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BEXP, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 0, // speed + 10 * FRACUNIT, // radius + 42 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD, // flags + S_NULL // raisestate + }, + + { // MT_TROOPSHOT + -1, // doomednum + S_TBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_TBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10 * FRACUNIT, // speed + 6 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 3, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_HEADSHOT + -1, // doomednum + S_RBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_RBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10 * FRACUNIT, // speed + 6 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_ROCKET + -1, // doomednum + S_ROCKET, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_rlaunc, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_EXPLODE1, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 20 * FRACUNIT, // speed + 11 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 20, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_PLASMA + -1, // doomednum + S_PLASBALL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_PLASEXP, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25 * FRACUNIT, // speed + 13 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_BFG + -1, // doomednum + S_BFGSHOT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + 0, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BFGLAND, // deathstate + S_NULL, // xdeathstate + sfx_rxplod, // deathsound + 25 * FRACUNIT, // speed + 13 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 100, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_ARACHPLAZ + -1, // doomednum + S_ARACH_PLAZ, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_ARACH_PLEX, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25 * FRACUNIT, // speed + 13 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_PUFF + -1, // doomednum + S_PUFF1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_BLOOD + -1, // doomednum + S_BLOOD1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_TFOG + -1, // doomednum + S_TFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_IFOG + -1, // doomednum + S_IFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_TELEPORTMAN + 14, // doomednum + S_NULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_EXTRABFG + -1, // doomednum + S_BFGEXP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC0 + 2018, // doomednum + S_ARM1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC1 + 2019, // doomednum + S_ARM2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC2 + 2014, // doomednum + S_BON1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC3 + 2015, // doomednum + S_BON2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC4 + 5, // doomednum + S_BKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC5 + 13, // doomednum + S_RKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC6 + 6, // doomednum + S_YKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC7 + 39, // doomednum + S_YSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC8 + 38, // doomednum + S_RSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC9 + 40, // doomednum + S_BSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC10 + 2011, // doomednum + S_STIM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC11 + 2012, // doomednum + S_MEDI, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC12 + 2013, // doomednum + S_SOUL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_INV + 2022, // doomednum + S_PINV, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC13 + 2023, // doomednum + S_PSTR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_INS + 2024, // doomednum + S_PINS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC14 + 2025, // doomednum + S_SUIT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC15 + 2026, // doomednum + S_PMAP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC16 + 2045, // doomednum + S_PVIS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MEGA + 83, // doomednum + S_MEGA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_CLIP + 2007, // doomednum + S_CLIP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC17 + 2048, // doomednum + S_AMMO, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC18 + 2010, // doomednum + S_ROCK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC19 + 2046, // doomednum + S_BROK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC20 + 2047, // doomednum + S_CELL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC21 + 17, // doomednum + S_CELP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC22 + 2008, // doomednum + S_SHEL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC23 + 2049, // doomednum + S_SBOX, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC24 + 8, // doomednum + S_BPAK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC25 + 2006, // doomednum + S_BFUG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_CHAINGUN + 2002, // doomednum + S_MGUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC26 + 2005, // doomednum + S_CSAW, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC27 + 2003, // doomednum + S_LAUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC28 + 2004, // doomednum + S_PLAS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_SHOTGUN + 2001, // doomednum + S_SHOT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_SUPERSHOTGUN + 82, // doomednum + S_SHOT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC29 + 85, // doomednum + S_TECHLAMP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC30 + 86, // doomednum + S_TECH2LAMP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC31 + 2028, // doomednum + S_COLU, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC32 + 30, // doomednum + S_TALLGRNCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC33 + 31, // doomednum + S_SHRTGRNCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC34 + 32, // doomednum + S_TALLREDCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC35 + 33, // doomednum + S_SHRTREDCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC36 + 37, // doomednum + S_SKULLCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC37 + 36, // doomednum + S_HEARTCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC38 + 41, // doomednum + S_EVILEYE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC39 + 42, // doomednum + S_FLOATSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC40 + 43, // doomednum + S_TORCHTREE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC41 + 44, // doomednum + S_BLUETORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC42 + 45, // doomednum + S_GREENTORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC43 + 46, // doomednum + S_REDTORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC44 + 55, // doomednum + S_BTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC45 + 56, // doomednum + S_GTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC46 + 57, // doomednum + S_RTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC47 + 47, // doomednum + S_STALAGTITE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC48 + 48, // doomednum + S_TECHPILLAR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC49 + 34, // doomednum + S_CANDLESTIK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC50 + 35, // doomednum + S_CANDELABRA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC51 + 49, // doomednum + S_BLOODYTWITCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 68 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC52 + 50, // doomednum + S_MEAT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 84 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC53 + 51, // doomednum + S_MEAT3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 84 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC54 + 52, // doomednum + S_MEAT4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 68 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC55 + 53, // doomednum + S_MEAT5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 52 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC56 + 59, // doomednum + S_MEAT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 84 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC57 + 60, // doomednum + S_MEAT4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 68 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC58 + 61, // doomednum + S_MEAT3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 52 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC59 + 62, // doomednum + S_MEAT5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 52 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC60 + 63, // doomednum + S_BLOODYTWITCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 68 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC61 + 22, // doomednum + S_HEAD_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC62 + 15, // doomednum + S_PLAY_DIE7, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC63 + 18, // doomednum + S_POSS_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC64 + 21, // doomednum + S_SARG_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC65 + 23, // doomednum + S_SKULL_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC66 + 20, // doomednum + S_TROO_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC67 + 19, // doomednum + S_SPOS_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC68 + 10, // doomednum + S_PLAY_XDIE9, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC69 + 12, // doomednum + S_PLAY_XDIE9, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC70 + 28, // doomednum + S_HEADSONSTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC71 + 24, // doomednum + S_GIBS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC72 + 27, // doomednum + S_HEADONASTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC73 + 29, // doomednum + S_HEADCANDLES, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC74 + 25, // doomednum + S_DEADSTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC75 + 26, // doomednum + S_LIVESTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC76 + 54, // doomednum + S_BIGTREE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 32 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC77 + 70, // doomednum + S_BBAR1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC78 + 73, // doomednum + S_HANGNOGUTS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 88 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC79 + 74, // doomednum + S_HANGBNOBRAIN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 88 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC80 + 75, // doomednum + S_HANGTLOOKDN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC81 + 76, // doomednum + S_HANGTSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC82 + 77, // doomednum + S_HANGTLOOKUP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC83 + 78, // doomednum + S_HANGTNOBRAIN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC84 + 79, // doomednum + S_COLONGIBS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_MISC85 + 80, // doomednum + S_SMALLPOOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_MISC86 + 81, // doomednum + S_BRAINSTEM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + } +}; +int myargc; +char** myargv; + + +// +// M_CheckParm +// Checks for the given parameter +// in the program's command line arguments. +// Returns the argument number (1 to argc-1) +// or 0 if not present +int M_CheckParm(char* check) +{ + int i; + + for (i = 1; i < myargc; i++) + { + if (!doom_strcasecmp(check, myargv[i])) + return i; + } + + return 0; +} +void M_ClearBox(fixed_t* box) +{ + box[BOXTOP] = box[BOXRIGHT] = DOOM_MININT; + box[BOXBOTTOM] = box[BOXLEFT] = DOOM_MAXINT; +} + + +void M_AddToBox(fixed_t* box, fixed_t x, fixed_t y) +{ + if (x < box[BOXLEFT]) + box[BOXLEFT] = x; + else if (x > box[BOXRIGHT]) + box[BOXRIGHT] = x; + if (y < box[BOXBOTTOM]) + box[BOXBOTTOM] = y; + else if (y > box[BOXTOP]) + box[BOXTOP] = y; +} +static int firsttime = 1; +static unsigned char cheat_xlate_table[256]; + + +// +// Called in st_stuff module, which handles the input. +// Returns a 1 if the cheat was successful, 0 if failed. +// +int cht_CheckCheat(cheatseq_t* cht, char key) +{ + int i; + int rc = 0; + + if (firsttime) + { + firsttime = 0; + for (i = 0; i < 256; i++) cheat_xlate_table[i] = SCRAMBLE(i); + } + + if (!cht->p) + cht->p = cht->sequence; // initialize if first time + + if (*cht->p == 0) + *(cht->p++) = key; + else if + (cheat_xlate_table[(unsigned char)key] == *cht->p) cht->p++; + else + cht->p = cht->sequence; + + if (*cht->p == 1) + cht->p++; + else if (*cht->p == 0xff) // end of sequence character + { + cht->p = cht->sequence; + rc = 1; + } + + return rc; +} + + +void cht_GetParam(cheatseq_t* cht, char* buffer) +{ + unsigned char* p, c; + + p = cht->sequence; + while (*(p++) != 1); + + do + { + c = *p; + *(buffer++) = c; + *(p++) = 0; + } while (c && *p != 0xff); + + if (*p == 0xff) + *buffer = 0; +} +fixed_t FixedMul(fixed_t a, fixed_t b) +{ + return ((long long)a * (long long)b) >> FRACBITS; +} + + +// +// FixedDiv, C version. +// +fixed_t FixedDiv(fixed_t a, fixed_t b) +{ + if ((doom_abs(a) >> 14) >= doom_abs(b)) + return (a ^ b) < 0 ? DOOM_MININT : DOOM_MAXINT; + return FixedDiv2(a, b); +} + + +fixed_t FixedDiv2(fixed_t a, fixed_t b) +{ + double c; + + c = ((double)a) / ((double)b) * FRACUNIT; + + if (c >= 2147483648.0 || c < -2147483648.0) + I_Error("Error: FixedDiv: divide by zero"); + return (fixed_t)c; +} +#define SAVESTRINGSIZE 24 +#define SKULLXOFF -32 +#define LINEHEIGHT 16 + + +// +// MENU TYPEDEFS +// +typedef struct +{ + // 0 = no cursor here, 1 = ok, 2 = arrows ok + short status; + + char name[10]; + + // choice = menu item #. + // if status = 2, + // choice=0:leftarrow,1:rightarrow + void (*routine)(int choice); + + // hotkey in menu + char alphaKey; +} menuitem_t; + + +typedef struct menu_s +{ + short numitems; // # of menu items + struct menu_s* prevMenu; // previous menu + menuitem_t* menuitems; // menu items + void (*routine)(); // draw routine + short x; + short y; // x,y of menu + short lastOn; // last item user was on in menu +} menu_t; + + +typedef struct +{ + char* lump; + int x, w; + int offx; + int offy; +} menu_custom_text_seg_t; + + +typedef struct +{ + char* name; + menu_custom_text_seg_t segs[16]; +} menu_custom_text_t; + + +extern int doom_flags; + +extern patch_t* hu_font[HU_FONTSIZE]; +extern doom_boolean message_dontfuckwithme; + +extern doom_boolean chat_on; // in heads-up code + +extern int mousemove; + + +// +// defaulted values +// +int mouseSensitivity; // has default + +// Show messages has default, 0 = off, 1 = on +int showMessages; + + +// Blocky mode, has default, 0 = high, 1 = normal +int detailLevel; +int screenblocks; // has default + +// temp for screenblocks (0-9) +int screenSize; + +// -1 = no quicksave slot picked! +int quickSaveSlot; + +// 1 = message to be printed +int messageToPrint; +// ...and here is the message string! +char* messageString; + +// message x & y +int messx; +int messy; +int messageLastMenuActive; + +// timed message = no input from user +doom_boolean messageNeedsInput; + +void (*messageRoutine)(int response); + +char gammamsg[5][26] = +{ + GAMMALVL0, + GAMMALVL1, + GAMMALVL2, + GAMMALVL3, + GAMMALVL4 +}; + +// we are going to be entering a savegame string +int saveStringEnter; +int saveSlot; // which slot to save in +int saveCharIndex; // which char we're editing +// old save description before edit +char saveOldString[SAVESTRINGSIZE]; + +doom_boolean inhelpscreens; +doom_boolean menuactive; + +extern doom_boolean sendpause; +char savegamestrings[10][SAVESTRINGSIZE]; + +char endstring[160]; + +short itemOn; // menu item skull is on +short skullAnimCounter; // skull animation counter +short whichSkull; // which skull to draw + +// graphic name of skulls +// warning: initializer-string for array of chars is too long +char skullName[2][/*8*/9] = { "M_SKULL1","M_SKULL2" }; + +// current menudef +menu_t* currentMenu; + +// We create new menu text by cutting into existing graphics and pasting them to create the new text. +// This way we don't ship code with embeded graphics that come from WAD files. +menu_custom_text_t menu_custom_texts[] = +{ + {"TXT_MMOV", { + {"M_MSENS", 0, 74, 0, 0}, // Mouse + {"M_MSENS", 0, 31, 83, 0}, // Mo + {"M_MSENS", 160, 14, 83 + 31, 0}, // v + {"M_MSENS", 60, 14, 83 + 31 + 14, 0}, // e + {"M_DETAIL", 169, 5, 83 + 31 + 14 + 14, 0}, // : + {0} + }}, + {"TXT_MOPT", { + {"M_MSENS", 0, 74, 0, 0}, // Mouse + {"M_OPTION", 0, 92, 74 + 9, 0}, // Options + {0} + }}, + {"TXT_CROS", { + {"M_SKILL", 0, 16, 0, 0}, // C + {"M_DETAIL", 14, 15, 16, 0}, // r + {"M_SKILL", 46, 30, 16 + 15, 0}, // os + {"M_SKILL", 62, 14, 16 + 15 + 30, 0}, // s + {"M_SKILL", 16, 15, 16 + 15 + 30 + 14, 0}, // h + {"M_DETAIL", 140, 19, 16 + 15 + 30 + 14 + 15, 0}, // ai + {"M_DETAIL", 14, 15, 16 + 15 + 30 + 14 + 15 + 19, 0}, // r + {"M_DETAIL", 169, 5, 16 + 15 + 30 + 14 + 15 + 19 + 15, 0}, // : + {0} + }}, + {"TXT_ARUN", { + {"M_SGTTL", 90, 17, 0, 0}, // A + {"M_GDLOW", 0, 10, 17, 3}, // l + {"M_GDLOW", 26, 16, 17 + 10, 3}, // + {"M_DISP", 57, 30, 17 + 10 + 16, 0}, // ay + {"M_RDTHIS", 99, 14, 17 + 10 + 16 + 30, 0}, // s + {"M_RDTHIS", 0, 16, 17 + 10 + 16 + 30 + 14 + 7, 0}, // R + {"M_SFXVOL", 90, 15, 17 + 10 + 16 + 30 + 14 + 7 + 16, 0}, // u + {"M_OPTION", 62, 15, 17 + 10 + 16 + 30 + 14 + 7 + 16 + 15, 0}, // n + {"M_DETAIL", 169, 5, 17 + 10 + 16 + 30 + 14 + 7 + 16 + 15 + 15, 0}, // : + {0} + }}, +}; + +const int custom_texts_count = sizeof(menu_custom_texts) / sizeof(menu_custom_text_t); + +char tempstring[80]; +int epi; +char detailNames[2][9] = { "M_GDHIGH","M_GDLOW" }; +char msgNames[2][9] = { "M_MSGOFF","M_MSGON" }; + +int quitsounds[8] = +{ + sfx_pldeth, + sfx_dmpain, + sfx_popain, + sfx_slop, + sfx_telept, + sfx_posit1, + sfx_posit3, + sfx_sgtatk +}; + +int quitsounds2[8] = +{ + sfx_vilact, + sfx_getpow, + sfx_boscub, + sfx_slop, + sfx_skeswg, + sfx_kntdth, + sfx_bspact, + sfx_sgtatk +}; + + +// +// PROTOTYPES +// +void M_NewGame(int choice); +void M_Episode(int choice); +void M_ChooseSkill(int choice); +void M_LoadGame(int choice); +void M_SaveGame(int choice); +void M_Options(int choice); +void M_EndGame(int choice); +void M_ReadThis(int choice); +void M_ReadThis2(int choice); +void M_QuitDOOM(int choice); + +void M_ChangeMessages(int choice); +void M_SfxVol(int choice); +void M_MusicVol(int choice); +void M_ChangeDetail(int choice); +void M_MouseOptions(int choice); +void M_SizeDisplay(int choice); +void M_StartGame(int choice); +void M_Sound(int choice); +void M_ChangeCrosshair(int choice); +void M_ChangeAlwaysRun(int choice); + +void M_MouseMove(int choice); +void M_ChangeSensitivity(int choice); + +void M_FinishReadThis(int choice); +void M_LoadSelect(int choice); +void M_SaveSelect(int choice); +void M_ReadSaveStrings(void); +void M_QuickSave(void); +void M_QuickLoad(void); + +void M_DrawMainMenu(void); +void M_DrawReadThis1(void); +void M_DrawReadThis2(void); +void M_DrawNewGame(void); +void M_DrawEpisode(void); +void M_DrawOptions(void); +void M_DrawSound(void); +void M_DrawLoad(void); +void M_DrawSave(void); + +void M_DrawSaveLoadBorder(int x, int y); +void M_SetupNextMenu(menu_t* menudef); +void M_DrawThermo(int x, int y, int thermWidth, int thermDot); +void M_DrawEmptyCell(menu_t* menu, int item); +void M_DrawSelCell(menu_t* menu, int item); +void M_WriteText(int x, int y, char* string); +int M_StringWidth(char* string); +int M_StringHeight(char* string); +void M_StartControlPanel(void); +void M_StartMessage(char* string, void* routine, doom_boolean input); +void M_StopMessage(void); +void M_ClearMenus(void); +void M_DrawMouseOptions(void); + + +// +// DOOM MENU +// +enum +{ + newgame = 0, + options, + loadgame, + savegame, + readthis, + quitdoom, + main_end +} main_e; + +menuitem_t MainMenu[] = +{ + {1,"M_NGAME",M_NewGame,'n'}, + {1,"M_OPTION",M_Options,'o'}, + {1,"M_LOADG",M_LoadGame,'l'}, + {1,"M_SAVEG",M_SaveGame,'s'}, + // Another hickup with Special edition. + {1,"M_RDTHIS",M_ReadThis,'r'}, + {1,"M_QUITG",M_QuitDOOM,'q'} +}; + +menu_t MainDef = +{ + main_end, + 0, + MainMenu, + M_DrawMainMenu, + 97,64, + 0 +}; + + +// +// EPISODE SELECT +// +enum +{ + ep1, + ep2, + ep3, + ep4, + ep_end +} episodes_e; + +menuitem_t EpisodeMenu[] = +{ + {1,"M_EPI1", M_Episode,'k'}, + {1,"M_EPI2", M_Episode,'t'}, + {1,"M_EPI3", M_Episode,'i'}, + {1,"M_EPI4", M_Episode,'t'} +}; + +menu_t EpiDef = +{ + ep_end, // # of menu items + &MainDef, // previous menu + EpisodeMenu, // menuitem_t -> + M_DrawEpisode, // drawing routine -> + 48,63, // x,y + ep1 // lastOn +}; + + +// +// NEW GAME +// +enum +{ + killthings, + toorough, + hurtme, + violence, + nightmare, + newg_end +} newgame_e; + +menuitem_t NewGameMenu[] = +{ + {1,"M_JKILL", M_ChooseSkill, 'i'}, + {1,"M_ROUGH", M_ChooseSkill, 'h'}, + {1,"M_HURT", M_ChooseSkill, 'h'}, + {1,"M_ULTRA", M_ChooseSkill, 'u'}, + {1,"M_NMARE", M_ChooseSkill, 'n'} +}; + +menu_t NewDef = +{ + newg_end, // # of menu items + &EpiDef, // previous menu + NewGameMenu, // menuitem_t -> + M_DrawNewGame, // drawing routine -> + 48,63, // x,y + hurtme // lastOn +}; + + +// +// OPTIONS MENU +// +menuitem_t* OptionsMenu; + +enum +{ + endgame, + messages, + crosshair_opt, + always_run_opt, + //detail, // Details do nothing? + scrnsize, + option_empty1, + mouseoptions, + soundvol, + opt_end +} options_e; + +menuitem_t OptionsMenuFull[] = +{ + {1,"M_ENDGAM", M_EndGame,'e'}, + {1,"M_MESSG", M_ChangeMessages,'m'}, + {1,"TXT_CROS", M_ChangeCrosshair,'c'}, + {1,"TXT_ARUN", M_ChangeAlwaysRun,'r'}, + //{1,"M_DETAIL", M_ChangeDetail,'g'}, // Details do nothing? + {2,"M_SCRNSZ", M_SizeDisplay,'s'}, + {-1,"",0}, + {1,"TXT_MOPT", M_MouseOptions,'f'}, + {1,"M_SVOL", M_Sound,'s'} +}; + +menu_t OptionsDef = +{ + opt_end, + &MainDef, + OptionsMenuFull, + M_DrawOptions, + 60,37, + 0 +}; + + +enum +{ + endgame_no_mouse, + messages_no_mouse, + crosshair_opt_no_mouse, + always_run_opt_no_mouse, + //detail_no_mouse, // Details do nothing? + scrnsize_no_mouse, + option_empty1_no_mouse, + soundvol_no_mouse, + opt_end_no_mouse +} options_e_no_mouse; + +menuitem_t OptionsMenuNoMouse[] = +{ + {1,"M_ENDGAM", M_EndGame,'e'}, + {1,"M_MESSG", M_ChangeMessages,'m'}, + {1,"TXT_CROS", M_ChangeCrosshair,'c'}, + {1,"TXT_ARUN", M_ChangeAlwaysRun,'r'}, + //{1,"M_DETAIL", M_ChangeDetail,'g'}, // Details do nothing? + {2,"M_SCRNSZ", M_SizeDisplay,'s'}, + {-1,"",0}, + {1,"M_SVOL", M_Sound,'s'} +}; + +menu_t OptionsNoMouseDef = +{ + opt_end_no_mouse, + &MainDef, + OptionsMenuNoMouse, + M_DrawOptions, + 60,37, + 0 +}; + + +enum +{ + endgame_no_sound, + messages_no_sound, + crosshair_opt_no_sound, + always_run_opt_no_sound, + //detail_no_sound, // Details do nothing? + scrnsize_no_sound, + option_empty1_no_sound, + mouseoptions_no_sound, + opt_end_no_sound +} options_e_no_sound; + +menuitem_t OptionsMenuNoSound[] = +{ + {1,"M_ENDGAM", M_EndGame,'e'}, + {1,"M_MESSG", M_ChangeMessages,'m'}, + {1,"TXT_CROS", M_ChangeCrosshair,'c'}, + {1,"TXT_ARUN", M_ChangeAlwaysRun,'r'}, + //{1,"M_DETAIL", M_ChangeDetail,'g'}, // Details do nothing? + {2,"M_SCRNSZ", M_SizeDisplay,'s'}, + {-1,"",0}, + {1,"TXT_MOPT", M_MouseOptions,'f'} +}; + +menu_t OptionsNoSoundDef = +{ + opt_end_no_sound, + &MainDef, + OptionsMenuNoSound, + M_DrawOptions, + 60,37, + 0 +}; + + +enum +{ + endgame_no_sound_no_mouse, + messages_no_sound_no_mouse, + crosshair_opt_no_sound_no_mouse, + always_run_top_no_sound_no_mouse, + //detail_no_sound_no_mouse, // Details do nothing? + scrnsize_no_sound_no_mouse, + option_empty1_no_sound_no_mouse, + opt_end_no_sound_no_mouse +} options_e_no_sound_no_mouse; + +menuitem_t OptionsMenuNoSoundNoMouse[] = +{ + {1,"M_ENDGAM", M_EndGame,'e'}, + {1,"M_MESSG", M_ChangeMessages,'m'}, + {1,"TXT_CROS", M_ChangeCrosshair,'c'}, + {1,"TXT_ARUN", M_ChangeAlwaysRun,'r'}, + //{1,"M_DETAIL", M_ChangeDetail,'g'}, // Details do nothing? + {2,"M_SCRNSZ", M_SizeDisplay,'s'}, + {-1,"",0} +}; + +menu_t OptionsNoSoundNoMouseDef = +{ + opt_end_no_sound_no_mouse, + &MainDef, + OptionsMenuNoSoundNoMouse, + M_DrawOptions, + 60,37, + 0 +}; + + +// +// MOUSE OPTIONS +// +enum +{ + mousemov, + mousesens, + mouse_option_empty1, + mouse_opt_end +} mouseoptions_e; + +menuitem_t MouseOptionsMenu[] = +{ + {1,"TXT_MMOV", M_MouseMove,'f'}, + {2,"M_MSENS", M_ChangeSensitivity,'m'}, + {-1,"",0}, +}; + +menu_t MouseOptionsDef = +{ + mouse_opt_end, + &OptionsDef, + MouseOptionsMenu, + M_DrawMouseOptions, + 60,70, + 0 +}; + + +// +// Read This! MENU 1 & 2 +// +enum +{ + rdthsempty1, + read1_end +} read_e; + +menuitem_t ReadMenu1[] = +{ + {1,"",M_ReadThis2,0} +}; + +menu_t ReadDef1 = +{ + read1_end, + &MainDef, + ReadMenu1, + M_DrawReadThis1, + 280,185, + 0 +}; + +enum +{ + rdthsempty2, + read2_end +} read_e2; + +menuitem_t ReadMenu2[] = +{ + {1,"",M_FinishReadThis,0} +}; + +menu_t ReadDef2 = +{ + read2_end, + &ReadDef1, + ReadMenu2, + M_DrawReadThis2, + 330,175, + 0 +}; + +// +// SOUND VOLUME MENU +// +menuitem_t* SoundMenu; + +enum +{ + sfx_vol, + sfx_empty1, + music_vol, + sfx_empty2, + sound_end +} sound_e; + +menuitem_t SoundMenuFull[] = +{ + {2,"M_SFXVOL",M_SfxVol,'s'}, + {-1,"",0}, + {2,"M_MUSVOL",M_MusicVol,'m'}, + {-1,"",0} +}; + +menu_t SoundDef = +{ + sound_end, + &OptionsDef, + SoundMenuFull, + M_DrawSound, + 80,64, + 0 +}; + + +enum +{ + music_vol_no_sfx, + sfx_empty2_no_sfx, + sound_end_no_sfx +} sound_e_no_sfx; + +menuitem_t SoundMenuNoSFX[] = +{ + {2,"M_MUSVOL",M_MusicVol,'m'}, + {-1,"",0} +}; + +menu_t SoundNoSFXDef = +{ + sound_end_no_sfx, + &OptionsDef, + SoundMenuNoSFX, + M_DrawSound, + 80,64, + 0 +}; + + +enum +{ + sfx_vol_no_music, + sfx_empty1_no_music, + sound_end_no_music +} sound_e_no_music; + +menuitem_t SoundMenuNoMusic[] = +{ + {2,"M_SFXVOL",M_SfxVol,'s'}, + {-1,"",0} +}; + +menu_t SoundNoMusicDef = +{ + sound_end_no_music, + &OptionsDef, + SoundMenuNoMusic, + M_DrawSound, + 80,64, + 0 +}; + + +// +// LOAD GAME MENU +// +enum +{ + load1, + load2, + load3, + load4, + load5, + load6, + load_end +} load_e; + +menuitem_t LoadMenu[] = +{ + {1,"", M_LoadSelect,'1'}, + {1,"", M_LoadSelect,'2'}, + {1,"", M_LoadSelect,'3'}, + {1,"", M_LoadSelect,'4'}, + {1,"", M_LoadSelect,'5'}, + {1,"", M_LoadSelect,'6'} +}; + +menu_t LoadDef = +{ + load_end, + &MainDef, + LoadMenu, + M_DrawLoad, + 80,54, + 0 +}; + +// +// SAVE GAME MENU +// +menuitem_t SaveMenu[] = +{ + {1,"", M_SaveSelect,'1'}, + {1,"", M_SaveSelect,'2'}, + {1,"", M_SaveSelect,'3'}, + {1,"", M_SaveSelect,'4'}, + {1,"", M_SaveSelect,'5'}, + {1,"", M_SaveSelect,'6'} +}; + +menu_t SaveDef = +{ + load_end, + &MainDef, + SaveMenu, + M_DrawSave, + 80,54, + 0 +}; + + +// +// M_DrawCustomMenuText +// Draw several segments of patches to make up new text +// +void M_DrawCustomMenuText(char* name, int x, int y) +{ + for (int i = 0; i < custom_texts_count; ++i) + { + menu_custom_text_t* custom_text = menu_custom_texts + i; + if (doom_strcmp(custom_text->name, name) == 0) + { + menu_custom_text_seg_t* seg = custom_text->segs; + while (seg->lump) + { + void* lump = W_CacheLumpName(seg->lump, PU_CACHE); + V_DrawPatchRectDirect(x + seg->offx, y, 0, lump, seg->x, seg->w); + ++seg; + } + break; + } + } +} + + +// +// M_ReadSaveStrings +// read the strings from the savegame files +// +void M_ReadSaveStrings(void) +{ + void* handle; + int count; + int i; + char name[256]; + + for (i = 0; i < load_end; i++) + { +#if 0 + if (M_CheckParm("-cdrom")) + //doom_sprintf(name, "c:\\doomdata\\" SAVEGAMENAME "%d.dsg", i); + else +#endif + { + //doom_sprintf(name, SAVEGAMENAME"%d.dsg", i); + doom_strcpy(name, SAVEGAMENAME); + doom_concat(name, doom_itoa(i, 10)); + doom_concat(name, ".dsg"); + } + + handle = doom_open(name, "r"); + if (handle == 0) + { + doom_strcpy(&savegamestrings[i][0], EMPTYSTRING); + LoadMenu[i].status = 0; + continue; + } + count = doom_read(handle, &savegamestrings[i], SAVESTRINGSIZE); + doom_close(handle); + LoadMenu[i].status = 1; + } +} + + +// +// M_LoadGame & Cie. +// +void M_DrawLoad(void) +{ + int i; + + V_DrawPatchDirect(72, 28, 0, W_CacheLumpName("M_LOADG", PU_CACHE)); + for (i = 0; i < load_end; i++) + { + M_DrawSaveLoadBorder(LoadDef.x, LoadDef.y + LINEHEIGHT * i); + M_WriteText(LoadDef.x, LoadDef.y + LINEHEIGHT * i, savegamestrings[i]); + } +} + + +// +// Draw border for the savegame description +// +void M_DrawSaveLoadBorder(int x, int y) +{ + int i; + + V_DrawPatchDirect(x - 8, y + 7, 0, W_CacheLumpName("M_LSLEFT", PU_CACHE)); + + for (i = 0; i < 24; i++) + { + V_DrawPatchDirect(x, y + 7, 0, W_CacheLumpName("M_LSCNTR", PU_CACHE)); + x += 8; + } + + V_DrawPatchDirect(x, y + 7, 0, W_CacheLumpName("M_LSRGHT", PU_CACHE)); +} + + +// +// User wants to load this game +// +void M_LoadSelect(int choice) +{ + char name[256]; + +#if 0 + if (M_CheckParm("-cdrom")) + //doom_sprintf(name, "c:\\doomdata\\"SAVEGAMENAME"%d.dsg", choice); + else +#endif + { + //doom_sprintf(name, SAVEGAMENAME"%d.dsg", choice); + doom_strcpy(name, SAVEGAMENAME); + doom_concat(name, doom_itoa(choice, 10)); + doom_concat(name, ".dsg"); + } + G_LoadGame(name); + M_ClearMenus(); +} + + +// +// Selected from DOOM menu +// +void M_LoadGame(int choice) +{ + if (netgame) + { + M_StartMessage(LOADNET, 0, false); + return; + } + + M_SetupNextMenu(&LoadDef); + M_ReadSaveStrings(); +} + + +// +// M_SaveGame & Cie. +// +void M_DrawSave(void) +{ + int i; + + V_DrawPatchDirect(72, 28, 0, W_CacheLumpName("M_SAVEG", PU_CACHE)); + for (i = 0; i < load_end; i++) + { + M_DrawSaveLoadBorder(LoadDef.x, LoadDef.y + LINEHEIGHT * i); + M_WriteText(LoadDef.x, LoadDef.y + LINEHEIGHT * i, savegamestrings[i]); + } + + if (saveStringEnter) + { + i = M_StringWidth(savegamestrings[saveSlot]); + M_WriteText(LoadDef.x + i, LoadDef.y + LINEHEIGHT * saveSlot, "_"); + } +} + + +// +// M_Responder calls this when user is finished +// +void M_DoSave(int slot) +{ + G_SaveGame(slot, savegamestrings[slot]); + M_ClearMenus(); + + // PICK QUICKSAVE SLOT YET? + if (quickSaveSlot == -2) + quickSaveSlot = slot; +} + + +// +// User wants to save. Start string input for M_Responder +// +void M_SaveSelect(int choice) +{ + // we are going to be intercepting all chars + saveStringEnter = 1; + + saveSlot = choice; + doom_strcpy(saveOldString, savegamestrings[choice]); + if (!doom_strcmp(savegamestrings[choice], EMPTYSTRING)) + savegamestrings[choice][0] = 0; + saveCharIndex = (int)doom_strlen(savegamestrings[choice]); +} + + +// +// Selected from DOOM menu +// +void M_SaveGame(int choice) +{ + if (!usergame) + { + M_StartMessage(SAVEDEAD, 0, false); + return; + } + + if (gamestate != GS_LEVEL) + return; + + M_SetupNextMenu(&SaveDef); + M_ReadSaveStrings(); +} + + +// +// M_QuickSave +// +void M_QuickSaveResponse(int ch) +{ + if (ch == 'y') + { + M_DoSave(quickSaveSlot); + S_StartSound(0, sfx_swtchx); + } +} + + +void M_QuickSave(void) +{ + if (!usergame) + { + S_StartSound(0, sfx_oof); + return; + } + + if (gamestate != GS_LEVEL) + return; + + if (quickSaveSlot < 0) + { + M_StartControlPanel(); + M_ReadSaveStrings(); + M_SetupNextMenu(&SaveDef); + quickSaveSlot = -2; // means to pick a slot now + return; + } + //doom_sprintf(tempstring, QSPROMPT, savegamestrings[quickSaveSlot]); + doom_strcpy(tempstring, QSPROMPT_1); + doom_concat(tempstring, savegamestrings[quickSaveSlot]); + doom_strcpy(tempstring, QSPROMPT_2); + M_StartMessage(tempstring, M_QuickSaveResponse, true); +} + + +// +// M_QuickLoad +// +void M_QuickLoadResponse(int ch) +{ + if (ch == 'y') + { + M_LoadSelect(quickSaveSlot); + S_StartSound(0, sfx_swtchx); + } +} + + +void M_QuickLoad(void) +{ + if (netgame) + { + M_StartMessage(QLOADNET, 0, false); + return; + } + + if (quickSaveSlot < 0) + { + M_StartMessage(QSAVESPOT, 0, false); + return; + } + //doom_sprintf(tempstring, QLPROMPT, savegamestrings[quickSaveSlot]); + doom_strcpy(tempstring, QLPROMPT_1); + doom_concat(tempstring, savegamestrings[quickSaveSlot]); + doom_strcpy(tempstring, QLPROMPT_2); + M_StartMessage(tempstring, M_QuickLoadResponse, true); +} + + +// +// Read This Menus +// Had a "quick hack to fix romero bug" +// +void M_DrawReadThis1(void) +{ + inhelpscreens = true; + switch (gamemode) + { + case commercial: + V_DrawPatchDirect(0, 0, 0, W_CacheLumpName("HELP", PU_CACHE)); + break; + case shareware: + case registered: + case retail: + V_DrawPatchDirect(0, 0, 0, W_CacheLumpName("HELP1", PU_CACHE)); + break; + default: + break; + } + return; +} + + +// +// Read This Menus - optional second page. +// +void M_DrawReadThis2(void) +{ + inhelpscreens = true; + switch (gamemode) + { + case retail: + case commercial: + // This hack keeps us from having to change menus. + V_DrawPatchDirect(0, 0, 0, W_CacheLumpName("CREDIT", PU_CACHE)); + break; + case shareware: + case registered: + V_DrawPatchDirect(0, 0, 0, W_CacheLumpName("HELP2", PU_CACHE)); + break; + default: + break; + } + return; +} + + +// +// Change Sfx & Music volumes +// +void M_DrawSound(void) +{ + V_DrawPatchDirect(60, 38, 0, W_CacheLumpName("M_SVOL", PU_CACHE)); + + if (!(doom_flags & DOOM_FLAG_HIDE_SOUND_OPTIONS)) + { + int offset = (doom_flags & DOOM_FLAG_HIDE_MUSIC_OPTIONS) ? sfx_vol_no_music : sfx_vol; + M_DrawThermo(SoundDef.x, SoundDef.y + LINEHEIGHT * (offset + 1), + 16, snd_SfxVolume); + } + + if (!(doom_flags & DOOM_FLAG_HIDE_MUSIC_OPTIONS)) + { + int offset = (doom_flags & DOOM_FLAG_HIDE_SOUND_OPTIONS) ? music_vol_no_sfx : music_vol; + M_DrawThermo(SoundDef.x, SoundDef.y + LINEHEIGHT * (offset + 1), + 16, snd_MusicVolume); + } +} + + +void M_Sound(int choice) +{ + M_SetupNextMenu(&SoundDef); +} + + +void M_MouseOptions(int choice) +{ + M_SetupNextMenu(&MouseOptionsDef); +} + + +void M_SfxVol(int choice) +{ + switch (choice) + { + case 0: + if (snd_SfxVolume) + snd_SfxVolume--; + break; + case 1: + if (snd_SfxVolume < 15) + snd_SfxVolume++; + break; + } + + S_SetSfxVolume(snd_SfxVolume /* *8 */); +} + + +void M_MusicVol(int choice) +{ + switch (choice) + { + case 0: + if (snd_MusicVolume) + snd_MusicVolume--; + break; + case 1: + if (snd_MusicVolume < 15) + snd_MusicVolume++; + break; + } + + S_SetMusicVolume(snd_MusicVolume /* *8 */); +} + + +// +// M_DrawMainMenu +// +void M_DrawMainMenu(void) +{ + V_DrawPatchDirect(94, 2, 0, W_CacheLumpName("M_DOOM", PU_CACHE)); +} + + +// +// M_NewGame +// +void M_DrawNewGame(void) +{ + V_DrawPatchDirect(96, 14, 0, W_CacheLumpName("M_NEWG", PU_CACHE)); + V_DrawPatchDirect(54, 38, 0, W_CacheLumpName("M_SKILL", PU_CACHE)); +} + + +void M_NewGame(int choice) +{ + if (netgame && !demoplayback) + { + M_StartMessage(NEWGAME, 0, false); + return; + } + + if (gamemode == commercial) + M_SetupNextMenu(&NewDef); + else + M_SetupNextMenu(&EpiDef); +} + + +// +// M_Episode +// +void M_DrawEpisode(void) +{ + V_DrawPatchDirect(54, 38, 0, W_CacheLumpName("M_EPISOD", PU_CACHE)); +} + +void M_VerifyNightmare(int ch) +{ + if (ch != 'y') + return; + + G_DeferedInitNew(nightmare, epi + 1, 1); + M_ClearMenus(); +} + +void M_ChooseSkill(int choice) +{ + if (choice == nightmare) + { + M_StartMessage(NIGHTMARE, M_VerifyNightmare, true); + return; + } + + G_DeferedInitNew(choice, epi + 1, 1); + M_ClearMenus(); +} + +void M_Episode(int choice) +{ + if ((gamemode == shareware) + && choice) + { + M_StartMessage(SWSTRING, 0, false); + M_SetupNextMenu(&ReadDef1); + return; + } + + // Yet another hack... + if ((gamemode == registered) + && (choice > 2)) + { + doom_print( + "M_Episode: 4th episode requires UltimateDOOM\n"); + choice = 0; + } + + epi = choice; + M_SetupNextMenu(&NewDef); +} + + +// +// M_Options +// +void M_DrawOptions(void) +{ + V_DrawPatchDirect(108, 15, 0, W_CacheLumpName("M_OPTTTL", PU_CACHE)); + + //V_DrawPatchDirect (OptionsDef.x + 175,OptionsDef.y+LINEHEIGHT*detail,0, + // W_CacheLumpName(detailNames[detailLevel],PU_CACHE)); // Details do nothing? + + V_DrawPatchDirect(OptionsDef.x + 120, OptionsDef.y + LINEHEIGHT * messages, 0, + W_CacheLumpName(msgNames[showMessages], PU_CACHE)); + + extern int crosshair; + V_DrawPatchDirect(OptionsDef.x + 131, OptionsDef.y + LINEHEIGHT * crosshair_opt, 0, + W_CacheLumpName(msgNames[crosshair], PU_CACHE)); + + extern int always_run; + V_DrawPatchDirect(OptionsDef.x + 147, OptionsDef.y + LINEHEIGHT * always_run_opt, 0, + W_CacheLumpName(msgNames[always_run], PU_CACHE)); + + M_DrawThermo(OptionsDef.x, OptionsDef.y + LINEHEIGHT * (scrnsize + 1), + 9, screenSize); +} + + +void M_DrawMouseOptions(void) +{ + M_DrawCustomMenuText("TXT_MOPT", 74, 45); + + V_DrawPatchDirect(MouseOptionsDef.x + 149, MouseOptionsDef.y + LINEHEIGHT * mousemov, 0, + W_CacheLumpName(msgNames[mousemove], PU_CACHE)); + + M_DrawThermo(MouseOptionsDef.x, MouseOptionsDef.y + LINEHEIGHT * (mousesens + 1), + 10, mouseSensitivity); +} + + +void M_Options(int choice) +{ + M_SetupNextMenu(&OptionsDef); +} + + +// +// Toggle messages on/off +// +void M_ChangeMessages(int choice) +{ + // warning: unused parameter `int choice' + choice = 0; + showMessages = 1 - showMessages; + + if (!showMessages) + players[consoleplayer].message = MSGOFF; + else + players[consoleplayer].message = MSGON; + + message_dontfuckwithme = true; +} + + +// +// Toggle crosshair on/off +// +void M_ChangeCrosshair(int choice) +{ + extern int crosshair; + + // warning: unused parameter `int choice' + choice = 0; + crosshair = 1 - crosshair; + + if (!crosshair) + players[consoleplayer].message = CROSSOFF; + else + players[consoleplayer].message = CROSSON; + + message_dontfuckwithme = true; +} + + +// +// Toggle always-run on/off +// +void M_ChangeAlwaysRun(int choice) +{ + extern int always_run; + + // warning: unused parameter `int choice' + choice = 0; + always_run = 1 - always_run; + + if (!always_run) + players[consoleplayer].message = ALWAYSRUNOFF; + else + players[consoleplayer].message = ALWAYSRUNON; + + message_dontfuckwithme = true; +} + + +// +// M_EndGame +// +void M_EndGameResponse(int ch) +{ + if (ch != 'y') + return; + + currentMenu->lastOn = itemOn; + M_ClearMenus(); + D_StartTitle(); +} + + +void M_EndGame(int choice) +{ + choice = 0; + if (!usergame) + { + S_StartSound(0, sfx_oof); + return; + } + + if (netgame) + { + M_StartMessage(NETEND, 0, false); + return; + } + + M_StartMessage(ENDGAME, M_EndGameResponse, true); +} + + +// +// M_ReadThis +// +void M_ReadThis(int choice) +{ + choice = 0; + M_SetupNextMenu(&ReadDef1); +} + + +void M_ReadThis2(int choice) +{ + choice = 0; + M_SetupNextMenu(&ReadDef2); +} + + +void M_FinishReadThis(int choice) +{ + choice = 0; + M_SetupNextMenu(&MainDef); +} + + +// +// M_QuitDOOM +// +void M_QuitResponse(int ch) +{ + if (ch != 'y') + return; + if (!netgame) + { + if (gamemode == commercial) + S_StartSound(0, quitsounds2[(gametic >> 2) & 7]); + else + S_StartSound(0, quitsounds[(gametic >> 2) & 7]); + I_WaitVBL(105); + } + I_Quit(); +} + + +void M_QuitDOOM(int choice) +{ + // We pick index 0 which is language sensitive, + // or one at random, between 1 and maximum number. + if (language != english) + { + //doom_sprintf(endstring, "%s\n\n"DOSY, endmsg[0]); + doom_strcpy(endstring, endmsg[0]); + doom_concat(endstring, "\n\n" DOSY); + } + else + { + //doom_sprintf(endstring, "%s\n\n" DOSY, endmsg[gametic % (NUM_QUITMESSAGES - 2) + 1]); + doom_strcpy(endstring, endmsg[gametic % (NUM_QUITMESSAGES - 2) + 1]); + doom_concat(endstring, "\n\n" DOSY); + } + + M_StartMessage(endstring, M_QuitResponse, true); +} + + +void M_ChangeSensitivity(int choice) +{ + switch (choice) + { + case 0: + if (mouseSensitivity) + mouseSensitivity--; + break; + case 1: + if (mouseSensitivity < 9) + mouseSensitivity++; + break; + } +} + + +void M_MouseMove(int choice) +{ + choice = 0; + mousemove = 1 - mousemove; + + return; +} + + +void M_ChangeDetail(int choice) +{ + choice = 0; + detailLevel = 1 - detailLevel; + + // FIXME - does not work. Remove anyway? + doom_print("M_ChangeDetail: low detail mode n.a.\n"); +} + + +void M_SizeDisplay(int choice) +{ + switch (choice) + { + case 0: + if (screenSize > 0) + { + screenblocks--; + screenSize--; + } + break; + case 1: + if (screenSize < 8) + { + screenblocks++; + screenSize++; + } + break; + } + + R_SetViewSize(screenblocks, detailLevel); +} + + +// +// Menu Functions +// +void M_DrawThermo(int x, int y, int thermWidth, int thermDot) +{ + int xx; + int i; + + xx = x; + V_DrawPatchDirect(xx, y, 0, W_CacheLumpName("M_THERML", PU_CACHE)); + xx += 8; + for (i = 0; i < thermWidth; i++) + { + V_DrawPatchDirect(xx, y, 0, W_CacheLumpName("M_THERMM", PU_CACHE)); + xx += 8; + } + V_DrawPatchDirect(xx, y, 0, W_CacheLumpName("M_THERMR", PU_CACHE)); + + V_DrawPatchDirect((x + 8) + thermDot * 8, y, + 0, W_CacheLumpName("M_THERMO", PU_CACHE)); +} + + +void M_DrawEmptyCell(menu_t* menu, int item) +{ + V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1, 0, + W_CacheLumpName("M_CELL1", PU_CACHE)); +} + + +void M_DrawSelCell(menu_t* menu, int item) +{ + V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1, 0, + W_CacheLumpName("M_CELL2", PU_CACHE)); +} + + +void M_StartMessage(char* string, void* routine, doom_boolean input) +{ + messageLastMenuActive = menuactive; + messageToPrint = 1; + messageString = string; + messageRoutine = routine; + messageNeedsInput = input; + menuactive = true; + return; +} + + +void M_StopMessage(void) +{ + menuactive = messageLastMenuActive; + messageToPrint = 0; +} + + +// +// Find string width from hu_font chars +// +int M_StringWidth(char* string) +{ + int i; + int w = 0; + int c; + + for (i = 0; i < doom_strlen(string); i++) + { + c = doom_toupper(string[i]) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + w += 4; + else + w += SHORT(hu_font[c]->width); + } + + return w; +} + + +// +// Find string height from hu_font chars +// +int M_StringHeight(char* string) +{ + int i; + int h; + int height = SHORT(hu_font[0]->height); + + h = height; + for (i = 0; i < doom_strlen(string); i++) + if (string[i] == '\n') + h += height; + + return h; +} + + +// +// Write a string using the hu_font +// +void M_WriteText(int x, int y, char* string) +{ + int w; + char* ch; + int c; + int cx; + int cy; + + ch = string; + cx = x; + cy = y; + + while (1) + { + c = *ch++; + if (!c) + break; + if (c == '\n') + { + cx = x; + cy += 12; + continue; + } + + c = doom_toupper(c) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + if (cx + w > SCREENWIDTH) + break; + V_DrawPatchDirect(cx, cy, 0, hu_font[c]); + cx += w; + } +} + + +// +// CONTROL PANEL +// + +// +// M_Responder +// +doom_boolean M_Responder(event_t* ev) +{ + int ch; + int i; + static int joywait = 0; + static int mousewait = 0; + static int mousey = 0; + static int lasty = 0; + static int mousex = 0; + static int lastx = 0; + + ch = -1; + + if (ev->type == ev_joystick && joywait < I_GetTime()) + { + if (ev->data3 == -1) + { + ch = KEY_UPARROW; + joywait = I_GetTime() + 5; + } + else if (ev->data3 == 1) + { + ch = KEY_DOWNARROW; + joywait = I_GetTime() + 5; + } + + if (ev->data2 == -1) + { + ch = KEY_LEFTARROW; + joywait = I_GetTime() + 2; + } + else if (ev->data2 == 1) + { + ch = KEY_RIGHTARROW; + joywait = I_GetTime() + 2; + } + + if (ev->data1 & 1) + { + ch = KEY_ENTER; + joywait = I_GetTime() + 5; + } + if (ev->data1 & 2) + { + ch = KEY_BACKSPACE; + joywait = I_GetTime() + 5; + } + } + else + { + if (ev->type == ev_mouse && mousewait < I_GetTime()) + { + mousey += ev->data3; + if (mousey < lasty - 30) + { + ch = KEY_DOWNARROW; + mousewait = I_GetTime() + 5; + mousey = lasty -= 30; + } + else if (mousey > lasty + 30) + { + ch = KEY_UPARROW; + mousewait = I_GetTime() + 5; + mousey = lasty += 30; + } + + mousex += ev->data2; + if (mousex < lastx - 30) + { + ch = KEY_LEFTARROW; + mousewait = I_GetTime() + 5; + mousex = lastx -= 30; + } + else if (mousex > lastx + 30) + { + ch = KEY_RIGHTARROW; + mousewait = I_GetTime() + 5; + mousex = lastx += 30; + } + + if (ev->data1 & 1) + { + ch = KEY_ENTER; + mousewait = I_GetTime() + 15; + } + + if (ev->data1 & 2) + { + ch = KEY_BACKSPACE; + mousewait = I_GetTime() + 15; + } + } + else + if (ev->type == ev_keydown) + { + ch = ev->data1; + } + } + + if (ch == -1) + return false; + + + // Save Game string input + if (saveStringEnter) + { + switch (ch) + { + case KEY_BACKSPACE: + if (saveCharIndex > 0) + { + saveCharIndex--; + savegamestrings[saveSlot][saveCharIndex] = 0; + } + break; + + case KEY_ESCAPE: + saveStringEnter = 0; + doom_strcpy(&savegamestrings[saveSlot][0], saveOldString); + break; + + case KEY_ENTER: + saveStringEnter = 0; + if (savegamestrings[saveSlot][0]) + M_DoSave(saveSlot); + break; + + default: + ch = doom_toupper(ch); + if (ch != 32) + if (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE) + break; + if (ch >= 32 && ch <= 127 && + saveCharIndex < SAVESTRINGSIZE - 1 && + M_StringWidth(savegamestrings[saveSlot]) < + (SAVESTRINGSIZE - 2) * 8) + { + savegamestrings[saveSlot][saveCharIndex++] = ch; + savegamestrings[saveSlot][saveCharIndex] = 0; + } + break; + } + return true; + } + + // Take care of any messages that need input + if (messageToPrint) + { + if (messageNeedsInput == true && + !(ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE)) + return false; + + menuactive = messageLastMenuActive; + messageToPrint = 0; + if (messageRoutine) + messageRoutine(ch); + + menuactive = false; + S_StartSound(0, sfx_swtchx); + return true; + } + + if (devparm && ch == KEY_F1) + { + G_ScreenShot(); + return true; + } + + + // F-Keys + if (!menuactive) + switch (ch) + { + case KEY_MINUS: // Screen size down + if (automapactive || chat_on) + return false; + M_SizeDisplay(0); + S_StartSound(0, sfx_stnmov); + return true; + + case KEY_EQUALS: // Screen size up + if (automapactive || chat_on) + return false; + M_SizeDisplay(1); + S_StartSound(0, sfx_stnmov); + return true; + + case KEY_F1: // Help key + M_StartControlPanel(); + + if (gamemode == retail) + currentMenu = &ReadDef2; + else + currentMenu = &ReadDef1; + + itemOn = 0; + S_StartSound(0, sfx_swtchn); + return true; + + case KEY_F2: // Save + M_StartControlPanel(); + S_StartSound(0, sfx_swtchn); + M_SaveGame(0); + return true; + + case KEY_F3: // Load + M_StartControlPanel(); + S_StartSound(0, sfx_swtchn); + M_LoadGame(0); + return true; + + case KEY_F4: // Sound Volume + M_StartControlPanel(); + currentMenu = &SoundDef; + itemOn = sfx_vol; + S_StartSound(0, sfx_swtchn); + return true; + + // case KEY_F5: // Detail toggle + // M_ChangeDetail(0); + // S_StartSound(0, sfx_swtchn); + // return true; + + case KEY_F5: // Crosshair toggle + M_ChangeCrosshair(0); + S_StartSound(0, sfx_swtchn); + return true; + + case KEY_F6: // Quicksave + S_StartSound(0, sfx_swtchn); + M_QuickSave(); + return true; + + case KEY_F7: // End game + S_StartSound(0, sfx_swtchn); + M_EndGame(0); + return true; + + case KEY_F8: // Toggle messages + M_ChangeMessages(0); + S_StartSound(0, sfx_swtchn); + return true; + + case KEY_F9: // Quickload + S_StartSound(0, sfx_swtchn); + M_QuickLoad(); + return true; + + case KEY_F10: // Quit DOOM + S_StartSound(0, sfx_swtchn); + M_QuitDOOM(0); + return true; + + case KEY_F11: // gamma toggle + usegamma++; + if (usegamma > 4) + usegamma = 0; + players[consoleplayer].message = gammamsg[usegamma]; + I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); + return true; + + } + + + // Pop-up menu? + if (!menuactive) + { + if (ch == KEY_ESCAPE) + { + M_StartControlPanel(); + S_StartSound(0, sfx_swtchn); + return true; + } + return false; + } + + + // Keys usable within menu + switch (ch) + { + case KEY_DOWNARROW: + do + { + if (itemOn + 1 > currentMenu->numitems - 1) + itemOn = 0; + else itemOn++; + S_StartSound(0, sfx_pstop); + } while (currentMenu->menuitems[itemOn].status == -1); + return true; + + case KEY_UPARROW: + do + { + if (!itemOn) + itemOn = currentMenu->numitems - 1; + else itemOn--; + S_StartSound(0, sfx_pstop); + } while (currentMenu->menuitems[itemOn].status == -1); + return true; + + case KEY_LEFTARROW: + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status == 2) + { + S_StartSound(0, sfx_stnmov); + currentMenu->menuitems[itemOn].routine(0); + } + return true; + + case KEY_RIGHTARROW: + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status == 2) + { + S_StartSound(0, sfx_stnmov); + currentMenu->menuitems[itemOn].routine(1); + } + return true; + + case KEY_ENTER: + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status) + { + currentMenu->lastOn = itemOn; + if (currentMenu->menuitems[itemOn].status == 2) + { + currentMenu->menuitems[itemOn].routine(1); // right arrow + S_StartSound(0, sfx_stnmov); + } + else + { + currentMenu->menuitems[itemOn].routine(itemOn); + S_StartSound(0, sfx_pistol); + } + } + return true; + + case KEY_ESCAPE: + currentMenu->lastOn = itemOn; + M_ClearMenus(); + S_StartSound(0, sfx_swtchx); + return true; + + case KEY_BACKSPACE: + currentMenu->lastOn = itemOn; + if (currentMenu->prevMenu) + { + currentMenu = currentMenu->prevMenu; + itemOn = currentMenu->lastOn; + S_StartSound(0, sfx_swtchn); + } + return true; + + default: + for (i = itemOn + 1; i < currentMenu->numitems; i++) + if (currentMenu->menuitems[i].alphaKey == ch) + { + itemOn = i; + S_StartSound(0, sfx_pstop); + return true; + } + for (i = 0; i <= itemOn; i++) + if (currentMenu->menuitems[i].alphaKey == ch) + { + itemOn = i; + S_StartSound(0, sfx_pstop); + return true; + } + break; + + } + + return false; +} + + +// +// M_StartControlPanel +// +void M_StartControlPanel(void) +{ + // intro might call this repeatedly + if (menuactive) + return; + + menuactive = 1; + currentMenu = &MainDef; // JDC + itemOn = currentMenu->lastOn; // JDC +} + + +// +// M_Drawer +// Called after the view has been rendered, +// but before it has been blitted. +// +void M_Drawer(void) +{ + static short x; + static short y; + short i; + short max; + char string[40]; + int start; + + inhelpscreens = false; + + + // Horiz. & Vertically center string and print it. + if (messageToPrint) + { + start = 0; + y = 100 - M_StringHeight(messageString) / 2; + while (*(messageString + start)) + { + for (i = 0; i < doom_strlen(messageString + start); i++) + if (*(messageString + start + i) == '\n') + { + doom_memset(string, 0, 40); + doom_strncpy(string, messageString + start, i); + start += i + 1; + break; + } + + if (i == doom_strlen(messageString + start)) + { + doom_strcpy(string, messageString + start); + start += i; + } + + x = 160 - M_StringWidth(string) / 2; + M_WriteText(x, y, string); + y += SHORT(hu_font[0]->height); + } + return; + } + + if (!menuactive) + return; + + // Darken background so the menu is more readable. + if (doom_flags & DOOM_FLAG_MENU_DARKEN_BG) + { + extern byte* screens[5]; + extern unsigned char screen_palette[256 * 3]; + extern lighttable_t* colormaps; + for (int j = 0, len = SCREENWIDTH * SCREENHEIGHT; j < len; ++j) + { + byte color = screens[0][j]; + color = colormaps[color + (20 * 256)]; + screens[0][j] = color; + } + } + + if (currentMenu->routine) + currentMenu->routine(); // call Draw routine + + // DRAW MENU + x = currentMenu->x; + y = currentMenu->y; + max = currentMenu->numitems; + + for (i = 0; i < max; i++) + { + menuitem_t* menuitem = currentMenu->menuitems + i; + if (menuitem->name[0]) + { + if (doom_strncmp(menuitem->name, "TXT_", 4) == 0) + { + M_DrawCustomMenuText(menuitem->name, x, y); + } + else + { + V_DrawPatchDirect(x, y, 0, W_CacheLumpName(menuitem->name, PU_CACHE)); + } + } + y += LINEHEIGHT; + } + + + // DRAW SKULL + V_DrawPatchDirect(x + SKULLXOFF, currentMenu->y - 5 + itemOn * LINEHEIGHT, 0, + W_CacheLumpName(skullName[whichSkull], PU_CACHE)); + +} + + +// +// M_ClearMenus +// +void M_ClearMenus(void) +{ + menuactive = 0; +} + + +// +// M_SetupNextMenu +// +void M_SetupNextMenu(menu_t* menudef) +{ + currentMenu = menudef; + itemOn = currentMenu->lastOn; +} + + +// +// M_Ticker +// +void M_Ticker(void) +{ + if (--skullAnimCounter <= 0) + { + whichSkull ^= 1; + skullAnimCounter = 8; + } +} + + +// +// M_Init +// +void M_Init(void) +{ + doom_boolean hide_mouse = (doom_flags & DOOM_FLAG_HIDE_MOUSE_OPTIONS) ? true : false; + doom_boolean hide_sound = ((doom_flags & DOOM_FLAG_HIDE_MUSIC_OPTIONS) && (doom_flags & DOOM_FLAG_HIDE_SOUND_OPTIONS)) ? true : false; + + OptionsMenu = OptionsMenuFull; + if (hide_mouse && !hide_sound) + { + OptionsMenu = OptionsMenuNoMouse; + doom_memcpy(&OptionsDef, &OptionsNoMouseDef, sizeof(OptionsDef)); + } + else if (!hide_mouse && hide_sound) + { + OptionsMenu = OptionsMenuNoSound; + doom_memcpy(&OptionsDef, &OptionsNoSoundDef, sizeof(OptionsDef)); + } + else if (hide_mouse && hide_sound) + { + OptionsMenu = OptionsMenuNoSoundNoMouse; + doom_memcpy(&OptionsDef, &OptionsNoSoundNoMouseDef, sizeof(OptionsDef)); + } + + SoundMenu = SoundMenuFull; + if (doom_flags & DOOM_FLAG_HIDE_MUSIC_OPTIONS) + { + SoundMenu = SoundMenuNoMusic; + doom_memcpy(&SoundDef, &SoundNoMusicDef, sizeof(SoundDef)); + } + else if (doom_flags & DOOM_FLAG_HIDE_SOUND_OPTIONS) + { + SoundMenu = SoundMenuNoSFX; + doom_memcpy(&SoundDef, &SoundNoSFXDef, sizeof(SoundDef)); + } + + currentMenu = &MainDef; + menuactive = 0; + itemOn = currentMenu->lastOn; + whichSkull = 0; + skullAnimCounter = 10; + screenSize = screenblocks - 3; + messageToPrint = 0; + messageString = 0; + messageLastMenuActive = menuactive; + quickSaveSlot = -1; + + // Here we could catch other version dependencies, + // like HELP1/2, and four episodes. + + switch (gamemode) + { + case commercial: + // This is used because DOOM 2 had only one HELP + // page. I use CREDIT as second page now, but + // kept this hack for educational purposes. + MainMenu[readthis] = MainMenu[quitdoom]; + MainDef.numitems--; + MainDef.y += 8; + NewDef.prevMenu = &MainDef; + ReadDef1.routine = M_DrawReadThis1; + ReadDef1.x = 330; + ReadDef1.y = 165; + ReadMenu1[0].routine = M_FinishReadThis; + break; + case shareware: + // Episode 2 and 3 are handled, + // branching to an ad screen. + case registered: + // We need to remove the fourth episode. + EpiDef.numitems--; + break; + case retail: + // We are fine. + default: + break; + } +} +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#define STRING_VALUE 0xFFFF + + +// +// SCREEN SHOTS +// +typedef struct +{ + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + + unsigned short xmin; + unsigned short ymin; + unsigned short xmax; + unsigned short ymax; + + unsigned short hres; + unsigned short vres; + + unsigned char palette[48]; + + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + + char filler[58]; + unsigned char data; // unbounded +} pcx_t; + + +// +// M_DrawText +// Returns the final X coordinate +// HU_Init must have been called to init the font +// +extern patch_t* hu_font[HU_FONTSIZE]; + + +// +// DEFAULTS +// +extern int key_right; +extern int key_left; +extern int key_up; +extern int key_down; + +extern int key_strafeleft; +extern int key_straferight; + +extern int key_fire; +extern int key_use; +extern int key_strafe; +extern int key_speed; + +extern int mousebfire; +extern int mousebstrafe; +extern int mousebforward; +extern int mousemove; + +extern int joybfire; +extern int joybstrafe; +extern int joybuse; +extern int joybspeed; + +extern int viewwidth; +extern int viewheight; + +extern int mouseSensitivity; +extern int showMessages; + +extern int detailLevel; + +extern int screenblocks; + +extern int showMessages; + +// machine-independent sound params +extern int numChannels; + +extern char* chat_macros[]; + +extern byte scantokey[128]; + + +int usemouse; +int usejoystick; +int crosshair; +int always_run; + + +default_t defaults[] = +{ + {"mouse_sensitivity",&mouseSensitivity, 5}, + {"sfx_volume",&snd_SfxVolume, 8}, + {"music_volume",&snd_MusicVolume, 8}, + {"show_messages",&showMessages, 1}, + + {"key_right",&key_right, KEY_RIGHTARROW}, + {"key_left",&key_left, KEY_LEFTARROW}, + {"key_up",&key_up, KEY_UPARROW}, + {"key_down",&key_down, KEY_DOWNARROW}, + {"key_strafeleft",&key_strafeleft, ','}, + {"key_straferight",&key_straferight, '.'}, + + {"key_fire",&key_fire, KEY_RCTRL}, + {"key_use",&key_use, ' '}, + {"key_strafe",&key_strafe, KEY_RALT}, + {"key_speed",&key_speed, KEY_RSHIFT}, + + {"use_mouse",&usemouse, 1}, + {"mouseb_fire",&mousebfire,0}, + {"mouseb_strafe",&mousebstrafe,1}, + {"mouseb_forward",&mousebforward,2}, + {"mouse_move",&mousemove,0}, + + {"use_joystick",&usejoystick, 0}, + {"joyb_fire",&joybfire,0}, + {"joyb_strafe",&joybstrafe,1}, + {"joyb_use",&joybuse,3}, + {"joyb_speed",&joybspeed,2}, + + {"screenblocks",&screenblocks, 9}, + {"detaillevel",&detailLevel, 0}, + {"crosshair",&crosshair, 0}, + {"always_run",&always_run, 0}, + + {"snd_channels",&numChannels, 3}, + + {"usegamma",&usegamma, 0}, + + {"chatmacro0", 0, STRING_VALUE, 0, 0, &chat_macros[0], HUSTR_CHATMACRO0 }, + {"chatmacro1", 0, STRING_VALUE, 0, 0, &chat_macros[1], HUSTR_CHATMACRO1 }, + {"chatmacro2", 0, STRING_VALUE, 0, 0, &chat_macros[2], HUSTR_CHATMACRO2 }, + {"chatmacro3", 0, STRING_VALUE, 0, 0, &chat_macros[3], HUSTR_CHATMACRO3 }, + {"chatmacro4", 0, STRING_VALUE, 0, 0, &chat_macros[4], HUSTR_CHATMACRO4 }, + {"chatmacro5", 0, STRING_VALUE, 0, 0, &chat_macros[5], HUSTR_CHATMACRO5 }, + {"chatmacro6", 0, STRING_VALUE, 0, 0, &chat_macros[6], HUSTR_CHATMACRO6 }, + {"chatmacro7", 0, STRING_VALUE, 0, 0, &chat_macros[7], HUSTR_CHATMACRO7 }, + {"chatmacro8", 0, STRING_VALUE, 0, 0, &chat_macros[8], HUSTR_CHATMACRO8 }, + {"chatmacro9", 0, STRING_VALUE, 0, 0, &chat_macros[9], HUSTR_CHATMACRO9 } +}; + + +int numdefaults = sizeof(defaults) / sizeof(default_t);; +char* defaultfile; + + +int M_DrawText(int x, int y, doom_boolean direct, char* string) +{ + int c; + int w; + + while (*string) + { + c = doom_toupper(*string) - HU_FONTSTART; + string++; + if (c < 0 || c> HU_FONTSIZE) + { + x += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + if (x + w > SCREENWIDTH) + break; + if (direct) + V_DrawPatchDirect(x, y, 0, hu_font[c]); + else + V_DrawPatch(x, y, 0, hu_font[c]); + x += w; + } + + return x; +} + + +// +// M_WriteFile +// +doom_boolean M_WriteFile(char const* name, void* source, int length) +{ + void* handle; + int count; + + handle = doom_open(name, "wb"); + + if (handle == 0) + return false; + + count = doom_write(handle, source, length); + doom_close(handle); + + if (count < length) + return false; + + return true; +} + + +// +// M_ReadFile +// +int M_ReadFile(char const* name, byte** buffer) +{ + void* handle; + int count, length; + byte* buf; + + handle = doom_open(name, "rb"); + if (handle == 0) + { + //I_Error("Error: Couldn't read file %s", name); + + doom_strcpy(error_buf, "Error: Couldn't read file "); + doom_concat(error_buf, name); + I_Error(error_buf); + } + doom_seek(handle, 0, DOOM_SEEK_END); + length = doom_tell(handle); + doom_seek(handle, 0, DOOM_SEEK_SET); + buf = Z_Malloc(length, PU_STATIC, 0); + count = doom_read(handle, buf, length); + doom_close(handle); + + if (count < length) + { + //I_Error("Error: Couldn't read file %s", name); + + doom_strcpy(error_buf, "Error: Couldn't read file "); + doom_concat(error_buf, name); + I_Error(error_buf); + } + + *buffer = buf; + return length; +} + + +// +// M_SaveDefaults +// +void M_SaveDefaults(void) +{ + int i; + int v; + void* f; + + f = doom_open(defaultfile, "w"); + if (!f) + return; // can't write the file, but don't complain + + for (i = 0; i < numdefaults; i++) + { + if (defaults[i].defaultvalue > -0xfff + && defaults[i].defaultvalue < 0xfff) + { + v = *defaults[i].location; + //fprintf(f, "%s\t\t%i\n", defaults[i].name, v); + doom_fprint(f, defaults[i].name); + doom_fprint(f, "\t\t"); + doom_fprint(f, doom_itoa(v, 10)); + doom_fprint(f, "\n"); + } + else + { + //fprintf(f, "%s\t\t\"%s\"\n", defaults[i].name, + // *(char**)(defaults[i].text_location)); + doom_fprint(f, defaults[i].name); + doom_fprint(f, "\t\t\""); + doom_fprint(f, *(char**)(defaults[i].text_location)); + doom_fprint(f, "\"\n"); + } + } + + doom_close(f); +} + + +// +// M_LoadDefaults +// +void M_LoadDefaults(void) +{ + int i; + int len; + void* f; + char def[80]; + char strparm[100]; + char* newstring; + int parm; + doom_boolean isstring; + + // set everything to base values + // numdefaults = sizeof(defaults)/sizeof(defaults[0]); + for (i = 0; i < numdefaults; i++) + { + if (defaults[i].defaultvalue == 0xFFFF) + *defaults[i].text_location = defaults[i].default_text_value; + else + *defaults[i].location = (int)defaults[i].defaultvalue; + } + + // check for a custom default file + i = M_CheckParm("-config"); + if (i && i < myargc - 1) + { + defaultfile = myargv[i + 1]; + //doom_print(" default file: %s\n", defaultfile); + doom_print(" default file: "); + doom_print(defaultfile); + doom_print("\n"); + } + else + defaultfile = basedefault; + + // read the file in, overriding any set defaults + f = doom_open(defaultfile, "r"); + if (f) + { + while (!doom_eof(f)) + { + // def + int arg_read = 0; + char c; + for (i = 0; i < 79; ++i) + { + doom_read(f, &c, 1); + if (c == ' ' || c == '\n' || c == '\t') + { + if (i > 0) arg_read++; + break; + } + def[i] = c; + } + def[i] = '\0'; + + // Ignore spaces + if (c != '\n') + { + while (1) + { + doom_read(f, &c, 1); + if (c != ' ' && c != '\t') break; + } + + // strparam + i = 0; + if (c != '\n') + { + for (; i < 260;) + { + strparm[i++] = c; + doom_read(f, &c, 1); + if (c == '\n') + { + if (i > 0) arg_read++; + break; + } + } + } + strparm[i] = '\0'; + } + + isstring = false; + //if (fscanf(f, "%79s %[^\n]\n", def, strparm) == 2) + if (arg_read == 2) + { + if (strparm[0] == '"') + { + // get a string default + isstring = true; + len = (int)doom_strlen(strparm); + newstring = (char*)doom_malloc(len); + strparm[len - 1] = 0; + doom_strcpy(newstring, strparm + 1); + } + else if (strparm[0] == '0' && strparm[1] == 'x') + { + //sscanf(strparm + 2, "%x", &parm); + parm = doom_atox(strparm + 2); + } + else + { + //sscanf(strparm, "%i", &parm); + parm = doom_atoi(strparm); + } + for (i = 0; i < numdefaults; i++) + if (!doom_strcmp(def, defaults[i].name)) + { + if (!isstring) + *defaults[i].location = parm; + else + *defaults[i].text_location = newstring; + break; + } + } + } + + doom_close(f); + } +} + + +// +// WritePCXfile +// +void WritePCXfile(char* filename, byte* data, int width, int height, byte* palette) +{ + int i; + int length; + pcx_t* pcx; + byte* pack; + + pcx = Z_Malloc(width * height * 2 + 1000, PU_STATIC, 0); + + pcx->manufacturer = 0x0a; // PCX id + pcx->version = 5; // 256 color + pcx->encoding = 1; // uncompressed + pcx->bits_per_pixel = 8; // 256 color + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = SHORT(width - 1); + pcx->ymax = SHORT(height - 1); + pcx->hres = SHORT(width); + pcx->vres = SHORT(height); + doom_memset(pcx->palette, 0, sizeof(pcx->palette)); + pcx->color_planes = 1; // chunky image + pcx->bytes_per_line = SHORT(width); + pcx->palette_type = SHORT(2); // not a grey scale + doom_memset(pcx->filler, 0, sizeof(pcx->filler)); + + // pack the image + pack = &pcx->data; + + for (i = 0; i < width * height; i++) + { + if ((*data & 0xc0) != 0xc0) + *pack++ = *data++; + else + { + *pack++ = 0xc1; + *pack++ = *data++; + } + } + + // write the palette + *pack++ = 0x0c; // palette ID byte + for (i = 0; i < 768; i++) + *pack++ = *palette++; + + // write output file + length = (int)(pack - (byte*)pcx); + M_WriteFile(filename, pcx, length); + + Z_Free(pcx); +} + + +// +// M_ScreenShot +// +void M_ScreenShot(void) +{ + int i; + byte* linear; + char lbmname[12]; + void* f; + + // munge planar buffer to linear + linear = screens[2]; + I_ReadScreen(linear); + + // find a file name to save it to + doom_strcpy(lbmname, "DOOM00.pcx"); + + for (i = 0; i <= 99; i++) + { + lbmname[4] = i / 10 + '0'; + lbmname[5] = i % 10 + '0'; + if ((f = doom_open(lbmname, "wb")) == 0) + break; // file doesn't exist + doom_close(f); + } + if (i == 100) + I_Error("Error: M_ScreenShot: Couldn't create a PCX"); + + // save the pcx file + WritePCXfile(lbmname, linear, + SCREENWIDTH, SCREENHEIGHT, + W_CacheLumpName("PLAYPAL", PU_CACHE)); + + players[consoleplayer].message = "screen shot"; +} +unsigned char rndtable[256] = { + 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 , + 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 , + 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 , + 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 , + 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 , + 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 , + 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 , + 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 , + 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 , + 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 , + 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 , + 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 , + 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 , + 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 , + 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 , + 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 , + 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 , + 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 , + 120, 163, 236, 249 +}; + + +int rndindex = 0; +int prndindex = 0; + + +// Which one is deterministic? +int P_Random(void) +{ + prndindex = (prndindex + 1) & 0xff; + return rndtable[prndindex]; +} + + +int M_Random(void) +{ + rndindex = (rndindex + 1) & 0xff; + return rndtable[rndindex]; +} + + +void M_ClearRandom(void) +{ + rndindex = prndindex = 0; +} +#ifdef __BIG_ENDIAN__ + + +// Swap 16bit, that is, MSB and LSB byte. +unsigned short SwapSHORT(unsigned short x) +{ + // No masking with 0xFF should be necessary. + return (x >> 8) | (x << 8); +} + + +// Swapping 32bit. +unsigned long SwapLONG(unsigned long x) +{ + return + (x >> 24) + | ((x >> 8) & 0xff00) + | ((x << 8) & 0xff0000) + | (x << 24); +} + + +#endif +ceiling_t* activeceilings[MAXCEILINGS]; + + +// +// T_MoveCeiling +// +void T_MoveCeiling(ceiling_t* ceiling) +{ + result_e res; + + switch (ceiling->direction) + { + case 0: + // IN STASIS + break; + case 1: + // UP + res = T_MovePlane(ceiling->sector, + ceiling->speed, + ceiling->topheight, + false, 1, ceiling->direction); + + if (!(leveltime & 7)) + { + switch (ceiling->type) + { + case silentCrushAndRaise: + break; + default: + S_StartSound((mobj_t*)&ceiling->sector->soundorg, + sfx_stnmov); + // ? + break; + } + } + + if (res == pastdest) + { + switch (ceiling->type) + { + case raiseToHighest: + P_RemoveActiveCeiling(ceiling); + break; + + case silentCrushAndRaise: + S_StartSound((mobj_t*)&ceiling->sector->soundorg, + sfx_pstop); + case fastCrushAndRaise: + case crushAndRaise: + ceiling->direction = -1; + break; + + default: + break; + } + + } + break; + + case -1: + // DOWN + res = T_MovePlane(ceiling->sector, + ceiling->speed, + ceiling->bottomheight, + ceiling->crush, 1, ceiling->direction); + + if (!(leveltime & 7)) + { + switch (ceiling->type) + { + case silentCrushAndRaise: break; + default: + S_StartSound((mobj_t*)&ceiling->sector->soundorg, + sfx_stnmov); + } + } + + if (res == pastdest) + { + switch (ceiling->type) + { + case silentCrushAndRaise: + S_StartSound((mobj_t*)&ceiling->sector->soundorg, + sfx_pstop); + case crushAndRaise: + ceiling->speed = CEILSPEED; + case fastCrushAndRaise: + ceiling->direction = 1; + break; + + case lowerAndCrush: + case lowerToFloor: + P_RemoveActiveCeiling(ceiling); + break; + + default: + break; + } + } + else // ( res != pastdest ) + { + if (res == crushed) + { + switch (ceiling->type) + { + case silentCrushAndRaise: + case crushAndRaise: + case lowerAndCrush: + ceiling->speed = CEILSPEED / 8; + break; + + default: + break; + } + } + } + break; + } +} + + +// +// EV_DoCeiling +// Move a ceiling up/down and all around! +// +int EV_DoCeiling(line_t* line, ceiling_e type) +{ + int secnum; + int rtn; + sector_t* sec; + ceiling_t* ceiling; + + secnum = -1; + rtn = 0; + + // Reactivate in-stasis ceilings...for certain types. + switch (type) + { + case fastCrushAndRaise: + case silentCrushAndRaise: + case crushAndRaise: + P_ActivateInStasisCeiling(line); + default: + break; + } + + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) + continue; + + // new door thinker + rtn = 1; + ceiling = Z_Malloc(sizeof(*ceiling), PU_LEVSPEC, 0); + P_AddThinker(&ceiling->thinker); + sec->specialdata = ceiling; + ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; + ceiling->sector = sec; + ceiling->crush = false; + + switch (type) + { + case fastCrushAndRaise: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8 * FRACUNIT); + ceiling->direction = -1; + ceiling->speed = CEILSPEED * 2; + break; + + case silentCrushAndRaise: + case crushAndRaise: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + case lowerAndCrush: + case lowerToFloor: + ceiling->bottomheight = sec->floorheight; + if (type != lowerToFloor) + ceiling->bottomheight += 8 * FRACUNIT; + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + case raiseToHighest: + ceiling->topheight = P_FindHighestCeilingSurrounding(sec); + ceiling->direction = 1; + ceiling->speed = CEILSPEED; + break; + } + + ceiling->tag = sec->tag; + ceiling->type = type; + P_AddActiveCeiling(ceiling); + } + + return rtn; +} + + +// +// Add an active ceiling +// +void P_AddActiveCeiling(ceiling_t* c) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] == 0) + { + activeceilings[i] = c; + return; + } + } +} + + +// +// Remove a ceiling's thinker +// +void P_RemoveActiveCeiling(ceiling_t* c) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] == c) + { + activeceilings[i]->sector->specialdata = 0; + P_RemoveThinker(&activeceilings[i]->thinker); + activeceilings[i] = 0; + break; + } + } +} + + +// +// Restart a ceiling that's in-stasis +// +void P_ActivateInStasisCeiling(line_t* line) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] + && (activeceilings[i]->tag == line->tag) + && (activeceilings[i]->direction == 0)) + { + activeceilings[i]->direction = activeceilings[i]->olddirection; + activeceilings[i]->thinker.function.acp1 + = (actionf_p1)T_MoveCeiling; + } + } +} + + +// +// EV_CeilingCrushStop +// Stop a ceiling from crushing! +// +int EV_CeilingCrushStop(line_t* line) +{ + int i; + int rtn; + + rtn = 0; + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] + && (activeceilings[i]->tag == line->tag) + && (activeceilings[i]->direction != 0)) + { + activeceilings[i]->olddirection = activeceilings[i]->direction; + activeceilings[i]->thinker.function.acv = (actionf_v)0; + activeceilings[i]->direction = 0; // in-stasis + rtn = 1; + } + } + + return rtn; +} +void T_VerticalDoor(vldoor_t* door) +{ + result_e res; + + switch (door->direction) + { + case 0: + // WAITING + if (!--door->topcountdown) + { + switch (door->type) + { + case blazeRaise: + door->direction = -1; // time to go back down + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_bdcls); + break; + + case door_normal: + door->direction = -1; // time to go back down + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_dorcls); + break; + + case close30ThenOpen: + door->direction = 1; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_doropn); + break; + + default: + break; + } + } + break; + + case 2: + // INITIAL WAIT + if (!--door->topcountdown) + { + switch (door->type) + { + case raiseIn5Mins: + door->direction = 1; + door->type = door_normal; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_doropn); + break; + + default: + break; + } + } + break; + + case -1: + // DOWN + res = T_MovePlane(door->sector, + door->speed, + door->sector->floorheight, + false, 1, door->direction); + if (res == pastdest) + { + switch (door->type) + { + case blazeRaise: + case blazeClose: + door->sector->specialdata = 0; + P_RemoveThinker(&door->thinker); // unlink and free + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_bdcls); + break; + + case door_normal: + case door_close: + door->sector->specialdata = 0; + P_RemoveThinker(&door->thinker); // unlink and free + break; + + case close30ThenOpen: + door->direction = 0; + door->topcountdown = 35 * 30; + break; + + default: + break; + } + } + else if (res == crushed) + { + switch (door->type) + { + case blazeClose: + case door_close: // DO NOT GO BACK UP! + break; + + default: + door->direction = 1; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_doropn); + break; + } + } + break; + + case 1: + // UP + res = T_MovePlane(door->sector, + door->speed, + door->topheight, + false, 1, door->direction); + + if (res == pastdest) + { + switch (door->type) + { + case blazeRaise: + case door_normal: + door->direction = 0; // wait at top + door->topcountdown = door->topwait; + break; + + case close30ThenOpen: + case blazeOpen: + case door_open: + door->sector->specialdata = 0; + P_RemoveThinker(&door->thinker); // unlink and free + break; + + default: + break; + } + } + break; + } +} + + +// +// EV_DoLockedDoor +// Move a locked door up/down +// +int EV_DoLockedDoor(line_t* line, vldoor_e type, mobj_t* thing) +{ + player_t* p; + + p = thing->player; + + if (!p) + return 0; + + switch (line->special) + { + case 99: // Blue Lock + case 133: + if (!p) + return 0; + if (!p->cards[it_bluecard] && !p->cards[it_blueskull]) + { + p->message = PD_BLUEO; + S_StartSound(0, sfx_oof); + return 0; + } + break; + + case 134: // Red Lock + case 135: + if (!p) + return 0; + if (!p->cards[it_redcard] && !p->cards[it_redskull]) + { + p->message = PD_REDO; + S_StartSound(0, sfx_oof); + return 0; + } + break; + + case 136: // Yellow Lock + case 137: + if (!p) + return 0; + if (!p->cards[it_yellowcard] && + !p->cards[it_yellowskull]) + { + p->message = PD_YELLOWO; + S_StartSound(0, sfx_oof); + return 0; + } + break; + } + + return EV_DoDoor(line, type); +} + + +int EV_DoDoor(line_t* line, vldoor_e type) +{ + int secnum, rtn; + sector_t* sec; + vldoor_t* door; + + secnum = -1; + rtn = 0; + + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) + continue; + + + // new door thinker + rtn = 1; + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + P_AddThinker(&door->thinker); + sec->specialdata = door; + + door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; + door->sector = sec; + door->type = type; + door->topwait = VDOORWAIT; + door->speed = VDOORSPEED; + + switch (type) + { + case blazeClose: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->direction = -1; + door->speed = VDOORSPEED * 4; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_bdcls); + break; + + case door_close: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->direction = -1; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_dorcls); + break; + + case close30ThenOpen: + door->topheight = sec->ceilingheight; + door->direction = -1; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_dorcls); + break; + + case blazeRaise: + case blazeOpen: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->speed = VDOORSPEED * 4; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_bdopn); + break; + + case door_normal: + case door_open: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_doropn); + break; + + default: + break; + } + } + + return rtn; +} + + +// +// EV_VerticalDoor : open a door manually, no tag value +// +void EV_VerticalDoor(line_t* line, mobj_t* thing) +{ + player_t* player; + int secnum; + sector_t* sec; + vldoor_t* door; + int side; + + side = 0; // only front sides can be used + + // Check for locks + player = thing->player; + + switch (line->special) + { + case 26: // Blue Lock + case 32: + if (!player) + return; + + if (!player->cards[it_bluecard] && !player->cards[it_blueskull]) + { + player->message = PD_BLUEK; + S_StartSound(0, sfx_oof); + return; + } + break; + + case 27: // Yellow Lock + case 34: + if (!player) + return; + + if (!player->cards[it_yellowcard] && + !player->cards[it_yellowskull]) + { + player->message = PD_YELLOWK; + S_StartSound(0, sfx_oof); + return; + } + break; + + case 28: // Red Lock + case 33: + if (!player) + return; + + if (!player->cards[it_redcard] && !player->cards[it_redskull]) + { + player->message = PD_REDK; + S_StartSound(0, sfx_oof); + return; + } + break; + } + + // if the sector has an active thinker, use it + sec = sides[line->sidenum[side ^ 1]].sector; + secnum = (int)(sec - sectors); + + if (sec->specialdata) + { + door = sec->specialdata; + switch (line->special) + { + case 1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s + case 26: + case 27: + case 28: + case 117: + if (door->direction == -1) + door->direction = 1; // go back up + else + { + if (!thing->player) + return; // JDC: bad guys never close doors + + door->direction = -1; // start going down immediately + } + return; + } + } + + // for proper sound + switch (line->special) + { + case 117: // BLAZING DOOR RAISE + case 118: // BLAZING DOOR OPEN + S_StartSound((mobj_t*)&sec->soundorg, sfx_bdopn); + break; + + case 1: // NORMAL DOOR SOUND + case 31: + S_StartSound((mobj_t*)&sec->soundorg, sfx_doropn); + break; + + default: // LOCKED DOOR SOUND + S_StartSound((mobj_t*)&sec->soundorg, sfx_doropn); + break; + } + + + // new door thinker + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + P_AddThinker(&door->thinker); + sec->specialdata = door; + door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; + door->sector = sec; + door->direction = 1; + door->speed = VDOORSPEED; + door->topwait = VDOORWAIT; + + switch (line->special) + { + case 1: + case 26: + case 27: + case 28: + door->type = door_normal; + break; + + case 31: + case 32: + case 33: + case 34: + door->type = door_open; + line->special = 0; + break; + + case 117: // blazing door raise + door->type = blazeRaise; + door->speed = VDOORSPEED * 4; + break; + case 118: // blazing door open + door->type = blazeOpen; + line->special = 0; + door->speed = VDOORSPEED * 4; + break; + } + + // find the top and bottom of the movement range + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; +} + + +// +// Spawn a door that closes after 30 seconds +// +void P_SpawnDoorCloseIn30(sector_t* sec) +{ + vldoor_t* door; + + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + + P_AddThinker(&door->thinker); + + sec->specialdata = door; + sec->special = 0; + + door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; + door->sector = sec; + door->direction = 0; + door->type = door_normal; + door->speed = VDOORSPEED; + door->topcountdown = 30 * 35; +} + + +// +// Spawn a door that opens after 5 minutes +// +void P_SpawnDoorRaiseIn5Mins(sector_t* sec, int secnum) +{ + vldoor_t* door; + + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + + P_AddThinker(&door->thinker); + + sec->specialdata = door; + sec->special = 0; + + door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; + door->sector = sec; + door->direction = 2; + door->type = raiseIn5Mins; + door->speed = VDOORSPEED; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->topwait = VDOORWAIT; + door->topcountdown = 5 * 60 * 35; +} +#define MAXSPECIALCROSS 8 +#define FATSPREAD (ANG90/8) +#define SKULLSPEED (20*FRACUNIT) + + +typedef enum +{ + DI_EAST, + DI_NORTHEAST, + DI_NORTH, + DI_NORTHWEST, + DI_WEST, + DI_SOUTHWEST, + DI_SOUTH, + DI_SOUTHEAST, + DI_NODIR, + NUMDIRS +} dirtype_t; + + +// +// P_NewChaseDir related LUT. +// +dirtype_t opposite[] = +{ + DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, + DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR +}; + +dirtype_t diags[] = +{ + DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST +}; + +mobj_t* soundtarget; +fixed_t xspeed[8] = { FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000 }; +fixed_t yspeed[8] = { 0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000 }; +int TRACEANGLE = 0xc000000; +mobj_t* corpsehit; +mobj_t* vileobj; +fixed_t viletryx; +fixed_t viletryy; +mobj_t* braintargets[32]; +int numbraintargets; +int braintargeton; + + +extern line_t* spechit[MAXSPECIALCROSS]; +extern int numspechit; + + +void A_Fall(mobj_t* actor); +void A_Fire(mobj_t* actor); +void A_ReFire(player_t* player, pspdef_t* psp); +void A_SpawnFly(mobj_t* mo); + + +// +// ENEMY THINKING +// Enemies are allways spawned +// with targetplayer = -1, threshold = 0 +// Most monsters are spawned unaware of all players, +// but some can be made preaware +// + + +// +// Called by P_NoiseAlert. +// Recursively traverse adjacent sectors, +// sound blocking lines cut off traversal. +// +void P_RecursiveSound(sector_t* sec, int soundblocks) +{ + int i; + line_t* check; + sector_t* other; + + // wake up all monsters in this sector + if (sec->validcount == validcount + && sec->soundtraversed <= soundblocks + 1) + { + return; // already flooded + } + + sec->validcount = validcount; + sec->soundtraversed = soundblocks + 1; + sec->soundtarget = soundtarget; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + if (!(check->flags & ML_TWOSIDED)) + continue; + + P_LineOpening(check); + + if (openrange <= 0) + continue; // closed door + + if (sides[check->sidenum[0]].sector == sec) + other = sides[check->sidenum[1]].sector; + else + other = sides[check->sidenum[0]].sector; + + if (check->flags & ML_SOUNDBLOCK) + { + if (!soundblocks) + P_RecursiveSound(other, 1); + } + else + P_RecursiveSound(other, soundblocks); + } +} + + +// +// P_NoiseAlert +// If a monster yells at a player, +// it will alert other monsters to the player. +// +void P_NoiseAlert(mobj_t* target, mobj_t* emmiter) +{ + soundtarget = target; + validcount++; + P_RecursiveSound(emmiter->subsector->sector, 0); +} + + +// +// P_CheckMeleeRange +// +doom_boolean P_CheckMeleeRange(mobj_t* actor) +{ + mobj_t* pl; + fixed_t dist; + + if (!actor->target) + return false; + + pl = actor->target; + dist = P_AproxDistance(pl->x - actor->x, pl->y - actor->y); + + if (dist >= MELEERANGE - 20 * FRACUNIT + pl->info->radius) + return false; + + if (!P_CheckSight(actor, actor->target)) + return false; + + return true; +} + + +// +// P_CheckMissileRange +// +doom_boolean P_CheckMissileRange(mobj_t* actor) +{ + fixed_t dist; + + if (!P_CheckSight(actor, actor->target)) + return false; + + if (actor->flags & MF_JUSTHIT) + { + // the target just hit the enemy, + // so fight back! + actor->flags &= ~MF_JUSTHIT; + return true; + } + + if (actor->reactiontime) + return false; // do not attack yet + + // OPTIMIZE: get this from a global checksight + dist = P_AproxDistance(actor->x - actor->target->x, + actor->y - actor->target->y) - 64 * FRACUNIT; + + if (!actor->info->meleestate) + dist -= 128 * FRACUNIT; // no melee attack, so fire more + + dist >>= 16; + + if (actor->type == MT_VILE) + { + if (dist > 14 * 64) + return false; // too far away + } + + + if (actor->type == MT_UNDEAD) + { + if (dist < 196) + return false; // close for fist attack + dist >>= 1; + } + + if (actor->type == MT_CYBORG + || actor->type == MT_SPIDER + || actor->type == MT_SKULL) + { + dist >>= 1; + } + + if (dist > 200) + dist = 200; + + if (actor->type == MT_CYBORG && dist > 160) + dist = 160; + + if (P_Random() < dist) + return false; + + return true; +} + + +// +// P_Move +// Move in the current direction, +// returns false if the move is blocked. +// +doom_boolean P_Move(mobj_t* actor) +{ + fixed_t tryx; + fixed_t tryy; + + line_t* ld; + + // warning: 'catch', 'throw', and 'try' + // are all C++ reserved words + doom_boolean try_ok; + doom_boolean good; + + if (actor->movedir == DI_NODIR) + return false; + + if ((unsigned)actor->movedir >= 8) + I_Error("Error: Weird actor->movedir!"); + + tryx = actor->x + actor->info->speed * xspeed[actor->movedir]; + tryy = actor->y + actor->info->speed * yspeed[actor->movedir]; + + try_ok = P_TryMove(actor, tryx, tryy); + + if (!try_ok) + { + // open any specials + if (actor->flags & MF_FLOAT && floatok) + { + // must adjust height + if (actor->z < tmfloorz) + actor->z += FLOATSPEED; + else + actor->z -= FLOATSPEED; + + actor->flags |= MF_INFLOAT; + return true; + } + + if (!numspechit) + return false; + + actor->movedir = DI_NODIR; + good = false; + while (numspechit--) + { + ld = spechit[numspechit]; + // if the special is not a door + // that can be opened, + // return false + if (P_UseSpecialLine(actor, ld, 0)) + good = true; + } + return good; + } + else + { + actor->flags &= ~MF_INFLOAT; + } + + if (!(actor->flags & MF_FLOAT)) + actor->z = actor->floorz; + + return true; +} + + +// +// TryWalk +// Attempts to move actor on +// in its current (ob->moveangle) direction. +// If blocked by either a wall or an actor +// returns FALSE +// If move is either clear or blocked only by a door, +// returns TRUE and sets... +// If a door is in the way, +// an OpenDoor call is made to start it opening. +// +doom_boolean P_TryWalk(mobj_t* actor) +{ + if (!P_Move(actor)) + { + return false; + } + + actor->movecount = P_Random() & 15; + return true; +} + + +void P_NewChaseDir(mobj_t* actor) +{ + fixed_t deltax; + fixed_t deltay; + + dirtype_t d[3]; + + int tdir; + dirtype_t olddir; + + dirtype_t turnaround; + + if (!actor->target) + I_Error("Error: P_NewChaseDir: called with no target"); + + olddir = actor->movedir; + turnaround = opposite[olddir]; + + deltax = actor->target->x - actor->x; + deltay = actor->target->y - actor->y; + + if (deltax > 10 * FRACUNIT) + d[1] = DI_EAST; + else if (deltax < -10 * FRACUNIT) + d[1] = DI_WEST; + else + d[1] = DI_NODIR; + + if (deltay < -10 * FRACUNIT) + d[2] = DI_SOUTH; + else if (deltay > 10 * FRACUNIT) + d[2] = DI_NORTH; + else + d[2] = DI_NODIR; + + // try direct route + if (d[1] != DI_NODIR + && d[2] != DI_NODIR) + { + actor->movedir = diags[((deltay < 0) << 1) + (deltax > 0)]; + if (actor->movedir != turnaround && P_TryWalk(actor)) + return; + } + + // try other directions + if (P_Random() > 200 + || doom_abs(deltay) > doom_abs(deltax)) + { + tdir = d[1]; + d[1] = d[2]; + d[2] = tdir; + } + + if (d[1] == turnaround) + d[1] = DI_NODIR; + if (d[2] == turnaround) + d[2] = DI_NODIR; + + if (d[1] != DI_NODIR) + { + actor->movedir = d[1]; + if (P_TryWalk(actor)) + { + // either moved forward or attacked + return; + } + } + + if (d[2] != DI_NODIR) + { + actor->movedir = d[2]; + + if (P_TryWalk(actor)) + return; + } + + // there is no direct path to the player, + // so pick another direction. + if (olddir != DI_NODIR) + { + actor->movedir = olddir; + + if (P_TryWalk(actor)) + return; + } + + // randomly determine direction of search + if (P_Random() & 1) + { + for (tdir = DI_EAST; + tdir <= DI_SOUTHEAST; + tdir++) + { + if (tdir != turnaround) + { + actor->movedir = tdir; + + if (P_TryWalk(actor)) + return; + } + } + } + else + { + for (tdir = DI_SOUTHEAST; + tdir != (DI_EAST - 1); + tdir--) + { + if (tdir != turnaround) + { + actor->movedir = tdir; + + if (P_TryWalk(actor)) + return; + } + } + } + + if (turnaround != DI_NODIR) + { + actor->movedir = turnaround; + if (P_TryWalk(actor)) + return; + } + + actor->movedir = DI_NODIR; // can not move +} + + +// +// P_LookForPlayers +// If allaround is false, only look 180 degrees in front. +// Returns true if a player is targeted. +// +doom_boolean P_LookForPlayers(mobj_t* actor, doom_boolean allaround) +{ + int c; + int stop; + player_t* player; + sector_t* sector; + angle_t an; + fixed_t dist; + + sector = actor->subsector->sector; + + c = 0; + stop = (actor->lastlook - 1) & 3; + + for (; ; actor->lastlook = (actor->lastlook + 1) & 3) + { + if (!playeringame[actor->lastlook]) + continue; + + if (c++ == 2 + || actor->lastlook == stop) + { + // done looking + return false; + } + + player = &players[actor->lastlook]; + + if (player->health <= 0) + continue; // dead + + if (!P_CheckSight(actor, player->mo)) + continue; // out of sight + + if (!allaround) + { + an = R_PointToAngle2(actor->x, + actor->y, + player->mo->x, + player->mo->y) + - actor->angle; + + if (an > ANG90 && an < ANG270) + { + dist = P_AproxDistance(player->mo->x - actor->x, + player->mo->y - actor->y); + // if real close, react anyway + if (dist > MELEERANGE) + continue; // behind back + } + } + + actor->target = player->mo; + return true; + } + + return false; +} + + +// +// A_KeenDie +// DOOM II special, map 32. +// Uses special tag 666. +// +void A_KeenDie(mobj_t* mo) +{ + thinker_t* th; + mobj_t* mo2; + line_t junk; + + A_Fall(mo); + + // scan the remaining thinkers + // to see if all Keens are dead + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t*)th; + if (mo2 != mo + && mo2->type == mo->type + && mo2->health > 0) + { + // other Keen not dead + return; + } + } + + junk.tag = 666; + EV_DoDoor(&junk, door_open); +} + + +// +// ACTION ROUTINES +// + +// +// A_Look +// Stay in state until a player is sighted. +// +void A_Look(mobj_t* actor) +{ + mobj_t* targ; + + actor->threshold = 0; // any shot will wake up + targ = actor->subsector->sector->soundtarget; + + if (targ + && (targ->flags & MF_SHOOTABLE)) + { + actor->target = targ; + + if (actor->flags & MF_AMBUSH) + { + if (P_CheckSight(actor, actor->target)) + goto seeyou; + } + else + goto seeyou; + } + + + if (!P_LookForPlayers(actor, false)) + return; + + // go into chase state +seeyou: + if (actor->info->seesound) + { + int sound; + + switch (actor->info->seesound) + { + case sfx_posit1: + case sfx_posit2: + case sfx_posit3: + sound = sfx_posit1 + P_Random() % 3; + break; + + case sfx_bgsit1: + case sfx_bgsit2: + sound = sfx_bgsit1 + P_Random() % 2; + break; + + default: + sound = actor->info->seesound; + break; + } + + if (actor->type == MT_SPIDER + || actor->type == MT_CYBORG) + { + // full volume + S_StartSound(0, sound); + } + else + S_StartSound(actor, sound); + } + + P_SetMobjState(actor, actor->info->seestate); +} + + +// +// A_Chase +// Actor has a melee attack, +// so it tries to close as fast as possible +// +void A_Chase(mobj_t* actor) +{ + int delta; + + if (actor->reactiontime) + actor->reactiontime--; + + // modify target threshold + if (actor->threshold) + { + if (!actor->target + || actor->target->health <= 0) + { + actor->threshold = 0; + } + else + actor->threshold--; + } + + // turn towards movement direction if not there yet + if (actor->movedir < 8) + { + actor->angle &= (7 << 29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANG90 / 2; + else if (delta < 0) + actor->angle += ANG90 / 2; + } + + if (!actor->target + || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true)) + return; // got a new target + + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + // do not attack twice in a row + if (actor->flags & MF_JUSTATTACKED) + { + actor->flags &= ~MF_JUSTATTACKED; + if (gameskill != sk_nightmare && !fastparm) + P_NewChaseDir(actor); + return; + } + + // check for melee attack + if (actor->info->meleestate + && P_CheckMeleeRange(actor)) + { + if (actor->info->attacksound) + S_StartSound(actor, actor->info->attacksound); + + P_SetMobjState(actor, actor->info->meleestate); + return; + } + + // check for missile attack + if (actor->info->missilestate) + { + if (gameskill < sk_nightmare + && !fastparm && actor->movecount) + { + goto nomissile; + } + + if (!P_CheckMissileRange(actor)) + goto nomissile; + + P_SetMobjState(actor, actor->info->missilestate); + actor->flags |= MF_JUSTATTACKED; + return; + } + + // ? +nomissile: + // possibly choose another target + if (netgame + && !actor->threshold + && !P_CheckSight(actor, actor->target)) + { + if (P_LookForPlayers(actor, true)) + return; // got a new target + } + + // chase towards player + if (--actor->movecount < 0 + || !P_Move(actor)) + { + P_NewChaseDir(actor); + } + + // make active sound + if (actor->info->activesound + && P_Random() < 3) + { + S_StartSound(actor, actor->info->activesound); + } +} + + +// +// A_FaceTarget +// +void A_FaceTarget(mobj_t* actor) +{ + if (!actor->target) + return; + + actor->flags &= ~MF_AMBUSH; + + actor->angle = R_PointToAngle2(actor->x, + actor->y, + actor->target->x, + actor->target->y); + + if (actor->target->flags & MF_SHADOW) + actor->angle += (P_Random() - P_Random()) << 21; +} + + +// +// A_PosAttack +// +void A_PosAttack(mobj_t* actor) +{ + int angle; + int damage; + int slope; + + if (!actor->target) + return; + + A_FaceTarget(actor); + angle = actor->angle; + slope = P_AimLineAttack(actor, angle, MISSILERANGE); + + S_StartSound(actor, sfx_pistol); + angle += (P_Random() - P_Random()) << 20; + damage = ((P_Random() % 5) + 1) * 3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); +} + + +void A_SPosAttack(mobj_t* actor) +{ + int i; + int angle; + int bangle; + int damage; + int slope; + + if (!actor->target) + return; + + S_StartSound(actor, sfx_shotgn); + A_FaceTarget(actor); + bangle = actor->angle; + slope = P_AimLineAttack(actor, bangle, MISSILERANGE); + + for (i = 0; i < 3; i++) + { + angle = bangle + ((P_Random() - P_Random()) << 20); + damage = ((P_Random() % 5) + 1) * 3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); + } +} + + +void A_CPosAttack(mobj_t* actor) +{ + int angle; + int bangle; + int damage; + int slope; + + if (!actor->target) + return; + + S_StartSound(actor, sfx_shotgn); + A_FaceTarget(actor); + bangle = actor->angle; + slope = P_AimLineAttack(actor, bangle, MISSILERANGE); + + angle = bangle + ((P_Random() - P_Random()) << 20); + damage = ((P_Random() % 5) + 1) * 3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); +} + + +void A_CPosRefire(mobj_t* actor) +{ + // keep firing unless target got out of sight + A_FaceTarget(actor); + + if (P_Random() < 40) + return; + + if (!actor->target + || actor->target->health <= 0 + || !P_CheckSight(actor, actor->target)) + { + P_SetMobjState(actor, actor->info->seestate); + } +} + + +void A_SpidRefire(mobj_t* actor) +{ + // keep firing unless target got out of sight + A_FaceTarget(actor); + + if (P_Random() < 10) + return; + + if (!actor->target + || actor->target->health <= 0 + || !P_CheckSight(actor, actor->target)) + { + P_SetMobjState(actor, actor->info->seestate); + } +} + + +void A_BspiAttack(mobj_t* actor) +{ + if (!actor->target) + return; + + A_FaceTarget(actor); + + // launch a missile + P_SpawnMissile(actor, actor->target, MT_ARACHPLAZ); +} + + +// +// A_TroopAttack +// +void A_TroopAttack(mobj_t* actor) +{ + int damage; + + if (!actor->target) + return; + + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + S_StartSound(actor, sfx_claw); + damage = (P_Random() % 8 + 1) * 3; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + + // launch a missile + P_SpawnMissile(actor, actor->target, MT_TROOPSHOT); +} + + +void A_SargAttack(mobj_t* actor) +{ + int damage; + + if (!actor->target) + return; + + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + damage = ((P_Random() % 10) + 1) * 4; + P_DamageMobj(actor->target, actor, actor, damage); + } +} + + +void A_HeadAttack(mobj_t* actor) +{ + int damage; + + if (!actor->target) + return; + + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + damage = (P_Random() % 6 + 1) * 10; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + + // launch a missile + P_SpawnMissile(actor, actor->target, MT_HEADSHOT); +} + + +void A_CyberAttack(mobj_t* actor) +{ + if (!actor->target) + return; + + A_FaceTarget(actor); + P_SpawnMissile(actor, actor->target, MT_ROCKET); +} + + +void A_BruisAttack(mobj_t* actor) +{ + int damage; + + if (!actor->target) + return; + + if (P_CheckMeleeRange(actor)) + { + S_StartSound(actor, sfx_claw); + damage = (P_Random() % 8 + 1) * 10; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + + // launch a missile + P_SpawnMissile(actor, actor->target, MT_BRUISERSHOT); +} + + +// +// A_SkelMissile +// +void A_SkelMissile(mobj_t* actor) +{ + mobj_t* mo; + + if (!actor->target) + return; + + A_FaceTarget(actor); + actor->z += 16 * FRACUNIT; // so missile spawns higher + mo = P_SpawnMissile(actor, actor->target, MT_TRACER); + actor->z -= 16 * FRACUNIT; // back to normal + + mo->x += mo->momx; + mo->y += mo->momy; + mo->tracer = actor->target; +} + + +void A_Tracer(mobj_t* actor) +{ + angle_t exact; + fixed_t dist; + fixed_t slope; + mobj_t* dest; + mobj_t* th; + + if (gametic & 3) + return; + + // spawn a puff of smoke behind the rocket + P_SpawnPuff(actor->x, actor->y, actor->z); + + th = P_SpawnMobj(actor->x - actor->momx, + actor->y - actor->momy, + actor->z, MT_SMOKE); + + th->momz = FRACUNIT; + th->tics -= P_Random() & 3; + if (th->tics < 1) + th->tics = 1; + + // adjust direction + dest = actor->tracer; + + if (!dest || dest->health <= 0) + return; + + // change angle + exact = R_PointToAngle2(actor->x, + actor->y, + dest->x, + dest->y); + + if (exact != actor->angle) + { + if (exact - actor->angle > 0x80000000) + { + actor->angle -= TRACEANGLE; + if (exact - actor->angle < 0x80000000) + actor->angle = exact; + } + else + { + actor->angle += TRACEANGLE; + if (exact - actor->angle > 0x80000000) + actor->angle = exact; + } + } + + exact = actor->angle >> ANGLETOFINESHIFT; + actor->momx = FixedMul(actor->info->speed, finecosine[exact]); + actor->momy = FixedMul(actor->info->speed, finesine[exact]); + + // change slope + dist = P_AproxDistance(dest->x - actor->x, + dest->y - actor->y); + + dist = dist / actor->info->speed; + + if (dist < 1) + dist = 1; + slope = (dest->z + 40 * FRACUNIT - actor->z) / dist; + + if (slope < actor->momz) + actor->momz -= FRACUNIT / 8; + else + actor->momz += FRACUNIT / 8; +} + + +void A_SkelWhoosh(mobj_t* actor) +{ + if (!actor->target) + return; + + A_FaceTarget(actor); + S_StartSound(actor, sfx_skeswg); +} + + +void A_SkelFist(mobj_t* actor) +{ + int damage; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (P_CheckMeleeRange(actor)) + { + damage = ((P_Random() % 10) + 1) * 6; + S_StartSound(actor, sfx_skepch); + P_DamageMobj(actor->target, actor, actor, damage); + } +} + + +// +// PIT_VileCheck +// Detect a corpse that could be raised. +// +doom_boolean PIT_VileCheck(mobj_t* thing) +{ + int maxdist; + doom_boolean check; + + if (!(thing->flags & MF_CORPSE)) + return true; // not a monster + + if (thing->tics != -1) + return true; // not lying still yet + + if (thing->info->raisestate == S_NULL) + return true; // monster doesn't have a raise state + + maxdist = thing->info->radius + mobjinfo[MT_VILE].radius; + + if (doom_abs(thing->x - viletryx) > maxdist + || doom_abs(thing->y - viletryy) > maxdist) + return true; // not actually touching + + corpsehit = thing; + corpsehit->momx = corpsehit->momy = 0; + corpsehit->height <<= 2; + check = P_CheckPosition(corpsehit, corpsehit->x, corpsehit->y); + corpsehit->height >>= 2; + + if (!check) + return true; // doesn't fit here + + return false; // got one, so stop checking +} + + +// +// A_VileChase +// Check for ressurecting a body +// +void A_VileChase(mobj_t* actor) +{ + int xl; + int xh; + int yl; + int yh; + + int bx; + int by; + + mobjinfo_t* info; + mobj_t* temp; + + if (actor->movedir != DI_NODIR) + { + // check for corpses to raise + viletryx = + actor->x + actor->info->speed * xspeed[actor->movedir]; + viletryy = + actor->y + actor->info->speed * yspeed[actor->movedir]; + + xl = (viletryx - bmaporgx - MAXRADIUS * 2) >> MAPBLOCKSHIFT; + xh = (viletryx - bmaporgx + MAXRADIUS * 2) >> MAPBLOCKSHIFT; + yl = (viletryy - bmaporgy - MAXRADIUS * 2) >> MAPBLOCKSHIFT; + yh = (viletryy - bmaporgy + MAXRADIUS * 2) >> MAPBLOCKSHIFT; + + vileobj = actor; + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + // Call PIT_VileCheck to check + // whether object is a corpse + // that canbe raised. + if (!P_BlockThingsIterator(bx, by, PIT_VileCheck)) + { + // got one! + temp = actor->target; + actor->target = corpsehit; + A_FaceTarget(actor); + actor->target = temp; + + P_SetMobjState(actor, S_VILE_HEAL1); + S_StartSound(corpsehit, sfx_slop); + info = corpsehit->info; + + P_SetMobjState(corpsehit, info->raisestate); + corpsehit->height <<= 2; + corpsehit->flags = info->flags; + corpsehit->health = info->spawnhealth; + corpsehit->target = 0; + + return; + } + } + } + } + + // Return to normal attack. + A_Chase(actor); +} + + +// +// A_VileStart +// +void A_VileStart(mobj_t* actor) +{ + S_StartSound(actor, sfx_vilatk); +} + + +// +// A_Fire +// Keep fire in front of player unless out of sight +// +void A_StartFire(mobj_t* actor) +{ + S_StartSound(actor, sfx_flamst); + A_Fire(actor); +} + + +void A_FireCrackle(mobj_t* actor) +{ + S_StartSound(actor, sfx_flame); + A_Fire(actor); +} + + +void A_Fire(mobj_t* actor) +{ + mobj_t* dest; + unsigned an; + + dest = actor->tracer; + if (!dest) + return; + + // don't move it if the vile lost sight + if (!P_CheckSight(actor->target, dest)) + return; + + an = dest->angle >> ANGLETOFINESHIFT; + + P_UnsetThingPosition(actor); + actor->x = dest->x + FixedMul(24 * FRACUNIT, finecosine[an]); + actor->y = dest->y + FixedMul(24 * FRACUNIT, finesine[an]); + actor->z = dest->z; + P_SetThingPosition(actor); +} + + +// +// A_VileTarget +// Spawn the hellfire +// +void A_VileTarget(mobj_t* actor) +{ + mobj_t* fog; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + fog = P_SpawnMobj(actor->target->x, + actor->target->x, + actor->target->z, MT_FIRE); + + actor->tracer = fog; + fog->target = actor; + fog->tracer = actor->target; + A_Fire(fog); +} + + +// +// A_VileAttack +// +void A_VileAttack(mobj_t* actor) +{ + mobj_t* fire; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (!P_CheckSight(actor, actor->target)) + return; + + S_StartSound(actor, sfx_barexp); + P_DamageMobj(actor->target, actor, actor, 20); + actor->target->momz = 1000 * FRACUNIT / actor->target->info->mass; + + an = actor->angle >> ANGLETOFINESHIFT; + + fire = actor->tracer; + + if (!fire) + return; + + // move the fire between the vile and the player + fire->x = actor->target->x - FixedMul(24 * FRACUNIT, finecosine[an]); + fire->y = actor->target->y - FixedMul(24 * FRACUNIT, finesine[an]); + P_RadiusAttack(fire, actor, 70); +} + + +// +// Mancubus attack, +// firing three missiles (bruisers) +// in three different directions? +// Doesn't look like it. +// +void A_FatRaise(mobj_t* actor) +{ + A_FaceTarget(actor); + S_StartSound(actor, sfx_manatk); +} + + +void A_FatAttack1(mobj_t* actor) +{ + mobj_t* mo; + int an; + + A_FaceTarget(actor); + // Change direction to ... + actor->angle += FATSPREAD; + P_SpawnMissile(actor, actor->target, MT_FATSHOT); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle += FATSPREAD; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + + +void A_FatAttack2(mobj_t* actor) +{ + mobj_t* mo; + int an; + + A_FaceTarget(actor); + // Now here choose opposite deviation. + actor->angle -= FATSPREAD; + P_SpawnMissile(actor, actor->target, MT_FATSHOT); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle -= FATSPREAD * 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + + +void A_FatAttack3(mobj_t* actor) +{ + mobj_t* mo; + int an; + + A_FaceTarget(actor); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle -= FATSPREAD / 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle += FATSPREAD / 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + + +// +// SkullAttack +// Fly at the player like a missile. +// +void A_SkullAttack(mobj_t* actor) +{ + mobj_t* dest; + angle_t an; + int dist; + + if (!actor->target) + return; + + dest = actor->target; + actor->flags |= MF_SKULLFLY; + + S_StartSound(actor, actor->info->attacksound); + A_FaceTarget(actor); + an = actor->angle >> ANGLETOFINESHIFT; + actor->momx = FixedMul(SKULLSPEED, finecosine[an]); + actor->momy = FixedMul(SKULLSPEED, finesine[an]); + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + dist = dist / SKULLSPEED; + + if (dist < 1) + dist = 1; + actor->momz = (dest->z + (dest->height >> 1) - actor->z) / dist; +} + + +// +// A_PainShootSkull +// Spawn a lost soul and launch it at the target +// +void A_PainShootSkull(mobj_t* actor, angle_t angle) +{ + fixed_t x; + fixed_t y; + fixed_t z; + + mobj_t* newmobj; + angle_t an; + int prestep; + int count; + thinker_t* currentthinker; + + // count total number of skull currently on the level + count = 0; + + currentthinker = thinkercap.next; + while (currentthinker != &thinkercap) + { + if ((currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) + && ((mobj_t*)currentthinker)->type == MT_SKULL) + count++; + currentthinker = currentthinker->next; + } + + // 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 + an = angle >> ANGLETOFINESHIFT; + + prestep = + 4 * FRACUNIT + + 3 * (actor->info->radius + mobjinfo[MT_SKULL].radius) / 2; + + x = actor->x + FixedMul(prestep, finecosine[an]); + y = actor->y + FixedMul(prestep, finesine[an]); + z = actor->z + 8 * FRACUNIT; + + newmobj = P_SpawnMobj(x, y, z, MT_SKULL); + + // Check for movements. + if (!P_TryMove(newmobj, newmobj->x, newmobj->y)) + { + // kill it immediately + P_DamageMobj(newmobj, actor, actor, 10000); + return; + } + + newmobj->target = actor->target; + A_SkullAttack(newmobj); +} + + +// +// A_PainAttack +// Spawn a lost soul and launch it at the target +// +void A_PainAttack(mobj_t* actor) +{ + if (!actor->target) + return; + + A_FaceTarget(actor); + A_PainShootSkull(actor, actor->angle); +} + + +void A_PainDie(mobj_t* actor) +{ + A_Fall(actor); + A_PainShootSkull(actor, actor->angle + ANG90); + A_PainShootSkull(actor, actor->angle + ANG180); + A_PainShootSkull(actor, actor->angle + ANG270); +} + + +void A_Scream(mobj_t* actor) +{ + int sound; + + switch (actor->info->deathsound) + { + case 0: + return; + + case sfx_podth1: + case sfx_podth2: + case sfx_podth3: + sound = sfx_podth1 + P_Random() % 3; + break; + + case sfx_bgdth1: + case sfx_bgdth2: + sound = sfx_bgdth1 + P_Random() % 2; + break; + + default: + sound = actor->info->deathsound; + break; + } + + // Check for bosses. + if (actor->type == MT_SPIDER + || actor->type == MT_CYBORG) + { + // full volume + S_StartSound(0, sound); + } + else + S_StartSound(actor, sound); +} + + +void A_XScream(mobj_t* actor) +{ + S_StartSound(actor, sfx_slop); +} + + +void A_Pain(mobj_t* actor) +{ + if (actor->info->painsound) + S_StartSound(actor, actor->info->painsound); +} + + +void A_Fall(mobj_t* actor) +{ + // actor is on ground, it can be walked over + actor->flags &= ~MF_SOLID; + + // So change this if corpse objects + // are meant to be obstacles. +} + + +// +// A_Explode +// +void A_Explode(mobj_t* thingy) +{ + P_RadiusAttack(thingy, thingy->target, 128); +} + + +// +// A_BossDeath +// Possibly trigger special effects +// if on first boss level +// +void A_BossDeath(mobj_t* mo) +{ + thinker_t* th; + mobj_t* mo2; + line_t junk; + int i; + + if (gamemode == commercial) + { + if (gamemap != 7) + return; + + if ((mo->type != MT_FATSO) + && (mo->type != MT_BABY)) + return; + } + else + { + switch (gameepisode) + { + case 1: + if (gamemap != 8) + return; + + if (mo->type != MT_BRUISER) + return; + break; + + case 2: + if (gamemap != 8) + return; + + if (mo->type != MT_CYBORG) + return; + break; + + case 3: + if (gamemap != 8) + return; + + if (mo->type != MT_SPIDER) + return; + + break; + + case 4: + switch (gamemap) + { + case 6: + if (mo->type != MT_CYBORG) + return; + break; + + case 8: + if (mo->type != MT_SPIDER) + return; + break; + + default: + return; + break; + } + break; + + default: + if (gamemap != 8) + return; + break; + } + } + + // make sure there is a player alive for victory + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].health > 0) + break; + + if (i == MAXPLAYERS) + return; // no one left alive, so do not end game + + // scan the remaining thinkers to see + // if all bosses are dead + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t*)th; + if (mo2 != mo + && mo2->type == mo->type + && mo2->health > 0) + { + // other boss not dead + return; + } + } + + // victory! + if (gamemode == commercial) + { + if (gamemap == 7) + { + if (mo->type == MT_FATSO) + { + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + } + + if (mo->type == MT_BABY) + { + junk.tag = 667; + EV_DoFloor(&junk, raiseToTexture); + return; + } + } + } + else + { + switch (gameepisode) + { + case 1: + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + break; + + case 4: + switch (gamemap) + { + case 6: + junk.tag = 666; + EV_DoDoor(&junk, blazeOpen); + return; + break; + + case 8: + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + break; + } + } + } + + G_ExitLevel(); +} + + +void A_Hoof(mobj_t* mo) +{ + S_StartSound(mo, sfx_hoof); + A_Chase(mo); +} + + +void A_Metal(mobj_t* mo) +{ + S_StartSound(mo, sfx_metal); + A_Chase(mo); +} + + +void A_BabyMetal(mobj_t* mo) +{ + S_StartSound(mo, sfx_bspwlk); + A_Chase(mo); +} + + +void A_OpenShotgun2(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_dbopn); +} + + +void A_LoadShotgun2(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_dbload); +} + + +void A_CloseShotgun2(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_dbcls); + A_ReFire(player, psp); +} + + +void A_BrainAwake(mobj_t* mo) +{ + thinker_t* thinker; + mobj_t* m; + + // find all the target spots + numbraintargets = 0; + braintargeton = 0; + + thinker = thinkercap.next; + for (thinker = thinkercap.next; + thinker != &thinkercap; + thinker = thinker->next) + { + if (thinker->function.acp1 != (actionf_p1)P_MobjThinker) + continue; // not a mobj + + m = (mobj_t*)thinker; + + if (m->type == MT_BOSSTARGET) + { + braintargets[numbraintargets] = m; + numbraintargets++; + } + } + + S_StartSound(0, sfx_bossit); +} + + +void A_BrainPain(mobj_t* mo) +{ + S_StartSound(0, sfx_bospn); +} + + +void A_BrainScream(mobj_t* mo) +{ + int x; + int y; + int z; + mobj_t* th; + + for (x = mo->x - 196 * FRACUNIT; x < mo->x + 320 * FRACUNIT; x += FRACUNIT * 8) + { + y = mo->y - 320 * FRACUNIT; + z = 128 + P_Random() * 2 * FRACUNIT; + th = P_SpawnMobj(x, y, z, MT_ROCKET); + th->momz = P_Random() * 512; + + P_SetMobjState(th, S_BRAINEXPLODE1); + + th->tics -= P_Random() & 7; + if (th->tics < 1) + th->tics = 1; + } + + S_StartSound(0, sfx_bosdth); +} + + +void A_BrainExplode(mobj_t* mo) +{ + int x; + int y; + int z; + mobj_t* th; + + x = mo->x + (P_Random() - P_Random()) * 2048; + y = mo->y; + z = 128 + P_Random() * 2 * FRACUNIT; + th = P_SpawnMobj(x, y, z, MT_ROCKET); + th->momz = P_Random() * 512; + + P_SetMobjState(th, S_BRAINEXPLODE1); + + th->tics -= P_Random() & 7; + if (th->tics < 1) + th->tics = 1; +} + + +void A_BrainDie(mobj_t* mo) +{ + G_ExitLevel(); +} + + +void A_BrainSpit(mobj_t* mo) +{ + mobj_t* targ; + mobj_t* newmobj; + + static int easy = 0; + + easy ^= 1; + if (gameskill <= sk_easy && (!easy)) + return; + + // shoot a cube at current target + targ = braintargets[braintargeton]; + braintargeton = (braintargeton + 1) % numbraintargets; + + // spawn brain missile + newmobj = P_SpawnMissile(mo, targ, MT_SPAWNSHOT); + newmobj->target = targ; + newmobj->reactiontime = + ((targ->y - mo->y) / newmobj->momy) / newmobj->state->tics; + + S_StartSound(0, sfx_bospit); +} + + +// travelling cube sound +void A_SpawnSound(mobj_t* mo) +{ + S_StartSound(mo, sfx_boscub); + A_SpawnFly(mo); +} + + +void A_SpawnFly(mobj_t* mo) +{ + mobj_t* newmobj; + mobj_t* fog; + mobj_t* targ; + int r; + mobjtype_t type; + + if (--mo->reactiontime) + return; // still flying + + targ = mo->target; + + // First spawn teleport fog. + fog = P_SpawnMobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE); + S_StartSound(fog, sfx_telept); + + // Randomly select monster to spawn. + r = P_Random(); + + // Probability distribution (kind of :), + // decreasing likelihood. + if (r < 50) + type = MT_TROOP; + else if (r < 90) + type = MT_SERGEANT; + else if (r < 120) + type = MT_SHADOWS; + else if (r < 130) + type = MT_PAIN; + else if (r < 160) + type = MT_HEAD; + else if (r < 162) + type = MT_VILE; + else if (r < 172) + type = MT_UNDEAD; + else if (r < 192) + type = MT_BABY; + else if (r < 222) + type = MT_FATSO; + else if (r < 246) + type = MT_KNIGHT; + else + type = MT_BRUISER; + + newmobj = P_SpawnMobj(targ->x, targ->y, targ->z, type); + if (P_LookForPlayers(newmobj, true)) + P_SetMobjState(newmobj, newmobj->info->seestate); + + // telefrag anything in this spot + P_TeleportMove(newmobj, newmobj->x, newmobj->y); + + // remove self (i.e., cube). + P_RemoveMobj(mo); +} + + +void A_PlayerScream(mobj_t* mo) +{ + // Default death sound. + int sound = sfx_pldeth; + + if ((gamemode == commercial) + && (mo->health < -50)) + { + // IF THE PLAYER DIES + // LESS THAN -50% WITHOUT GIBBING + sound = sfx_pdiehi; + } + + S_StartSound(mo, sound); +} +result_e T_MovePlane(sector_t* sector, + fixed_t speed, + fixed_t dest, + doom_boolean crush, + int floorOrCeiling, + int direction) +{ + doom_boolean flag; + fixed_t lastpos; + + switch (floorOrCeiling) + { + case 0: + // FLOOR + switch (direction) + { + case -1: + // DOWN + if (sector->floorheight - speed < dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = P_ChangeSector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + P_ChangeSector(sector, crush); + //return crushed; + } + return pastdest; + } + else + { + lastpos = sector->floorheight; + sector->floorheight -= speed; + flag = P_ChangeSector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + P_ChangeSector(sector, crush); + return crushed; + } + } + break; + + case 1: + // UP + if (sector->floorheight + speed > dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = P_ChangeSector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + P_ChangeSector(sector, crush); + //return crushed; + } + return pastdest; + } + else + { + // COULD GET CRUSHED + lastpos = sector->floorheight; + sector->floorheight += speed; + flag = P_ChangeSector(sector, crush); + if (flag == true) + { + if (crush == true) + return crushed; + sector->floorheight = lastpos; + P_ChangeSector(sector, crush); + return crushed; + } + } + break; + } + break; + + case 1: + // CEILING + switch (direction) + { + case -1: + // DOWN + if (sector->ceilingheight - speed < dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = P_ChangeSector(sector, crush); + + if (flag == true) + { + sector->ceilingheight = lastpos; + P_ChangeSector(sector, crush); + //return crushed; + } + return pastdest; + } + else + { + // COULD GET CRUSHED + lastpos = sector->ceilingheight; + sector->ceilingheight -= speed; + flag = P_ChangeSector(sector, crush); + + if (flag == true) + { + if (crush == true) + return crushed; + sector->ceilingheight = lastpos; + P_ChangeSector(sector, crush); + return crushed; + } + } + break; + + case 1: + // UP + if (sector->ceilingheight + speed > dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = P_ChangeSector(sector, crush); + if (flag == true) + { + sector->ceilingheight = lastpos; + P_ChangeSector(sector, crush); + //return crushed; + } + return pastdest; + } + else + { + lastpos = sector->ceilingheight; + sector->ceilingheight += speed; + flag = P_ChangeSector(sector, crush); + // UNUSED +#if 0 + if (flag == true) + { + sector->ceilingheight = lastpos; + P_ChangeSector(sector, crush); + return crushed; + } +#endif + } + break; + } + break; + } + + return ok; +} + + +// +// MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) +// +void T_MoveFloor(floormove_t* floor) +{ + result_e res; + + res = T_MovePlane(floor->sector, + floor->speed, + floor->floordestheight, + floor->crush, 0, floor->direction); + + if (!(leveltime & 7)) + S_StartSound((mobj_t*)&floor->sector->soundorg, + sfx_stnmov); + + if (res == pastdest) + { + floor->sector->specialdata = 0; + + if (floor->direction == 1) + { + switch (floor->type) + { + case donutRaise: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + default: + break; + } + } + else if (floor->direction == -1) + { + switch (floor->type) + { + case lowerAndChange: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + default: + break; + } + } + P_RemoveThinker(&floor->thinker); + + S_StartSound((mobj_t*)&floor->sector->soundorg, + sfx_pstop); + } +} + + +// +// HANDLE FLOOR TYPES +// +int EV_DoFloor(line_t* line, floor_e floortype) +{ + int secnum; + int rtn; + int i; + sector_t* sec; + floormove_t* floor; + + secnum = -1; + rtn = 0; + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (sec->specialdata) + continue; + + // new floor thinker + rtn = 1; + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + P_AddThinker(&floor->thinker); + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + floor->type = floortype; + floor->crush = false; + + switch (floortype) + { + case lowerFloor: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindHighestFloorSurrounding(sec); + break; + + case lowerFloorToLowest: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindLowestFloorSurrounding(sec); + break; + + case turboLower: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = + P_FindHighestFloorSurrounding(sec); + if (floor->floordestheight != sec->floorheight) + floor->floordestheight += 8 * FRACUNIT; + break; + + case raiseFloorCrush: + floor->crush = true; + case raiseFloor: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindLowestCeilingSurrounding(sec); + if (floor->floordestheight > sec->ceilingheight) + floor->floordestheight = sec->ceilingheight; + floor->floordestheight -= (8 * FRACUNIT) * + (floortype == raiseFloorCrush); + break; + + case raiseFloorTurbo: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = + P_FindNextHighestFloor(sec, sec->floorheight); + break; + + case raiseFloorToNearest: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindNextHighestFloor(sec, sec->floorheight); + break; + + case raiseFloor24: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + + 24 * FRACUNIT; + break; + case raiseFloor512: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + + 512 * FRACUNIT; + break; + + case raiseFloor24AndChange: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + + 24 * FRACUNIT; + sec->floorpic = line->frontsector->floorpic; + sec->special = line->frontsector->special; + break; + + case raiseToTexture: + { + int minsize = DOOM_MAXINT; + side_t* side; + + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + for (i = 0; i < sec->linecount; i++) + { + if (twoSided(secnum, i)) + { + side = getSide(secnum, i, 0); + if (side->bottomtexture >= 0) + if (textureheight[side->bottomtexture] < + minsize) + minsize = + textureheight[side->bottomtexture]; + side = getSide(secnum, i, 1); + if (side->bottomtexture >= 0) + if (textureheight[side->bottomtexture] < + minsize) + minsize = + textureheight[side->bottomtexture]; + } + } + floor->floordestheight = + floor->sector->floorheight + minsize; + } + break; + + case lowerAndChange: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindLowestFloorSurrounding(sec); + floor->texture = sec->floorpic; + + for (i = 0; i < sec->linecount; i++) + { + if (twoSided(secnum, i)) + { + if (getSide(secnum, i, 0)->sector - sectors == secnum) + { + sec = getSector(secnum, i, 1); + + if (sec->floorheight == floor->floordestheight) + { + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + break; + } + } + else + { + sec = getSector(secnum, i, 0); + + if (sec->floorheight == floor->floordestheight) + { + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + break; + } + } + } + } + default: + break; + } + } + + return rtn; +} + + +// +// BUILD A STAIRCASE! +// +int EV_BuildStairs(line_t* line, stair_e type) +{ + int secnum; + int height; + int i; + int newsecnum; + int texture; + int ok; + int rtn; + + sector_t* sec; + sector_t* tsec; + + floormove_t* floor; + + fixed_t stairsize; + fixed_t speed; + + secnum = -1; + rtn = 0; + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (sec->specialdata) + continue; + + // new floor thinker + rtn = 1; + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + P_AddThinker(&floor->thinker); + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + floor->direction = 1; + floor->sector = sec; + switch (type) + { + case build8: + speed = FLOORSPEED / 4; + stairsize = 8 * FRACUNIT; + break; + case turbo16: + speed = FLOORSPEED * 4; + stairsize = 16 * FRACUNIT; + break; + } + floor->speed = speed; + height = sec->floorheight + stairsize; + floor->floordestheight = height; + + texture = sec->floorpic; + + // Find next sector to raise + // 1. Find 2-sided line with same sector side[0] + // 2. Other side is the next sector to raise + do + { + ok = 0; + for (i = 0; i < sec->linecount; i++) + { + if (!((sec->lines[i])->flags & ML_TWOSIDED)) + continue; + + tsec = (sec->lines[i])->frontsector; + newsecnum = (int)(tsec - sectors); + + if (secnum != newsecnum) + continue; + + tsec = (sec->lines[i])->backsector; + newsecnum = (int)(tsec - sectors); + + if (tsec->floorpic != texture) + continue; + + height += stairsize; + + if (tsec->specialdata) + continue; + + sec = tsec; + secnum = newsecnum; + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + + P_AddThinker(&floor->thinker); + + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + floor->direction = 1; + floor->sector = sec; + floor->speed = speed; + floor->floordestheight = height; + ok = 1; + break; + } + } while (ok); + } + + return rtn; +} +#ifdef __GNUG__ +#pragma implementation "p_inter.h" +#endif +//#include "p_inter.h" + + +#define BONUSADD 6 + + + + +// a weapon is found with two clip loads, +// a big item has five clip loads +int maxammo[NUMAMMO] = {200, 50, 300, 50}; +int clipammo[NUMAMMO] = {10, 4, 20, 1}; + + +// +// GET STUFF +// + +// +// P_GiveAmmo +// Num is the number of clip loads, +// not the individual count (0= 1/2 clip). +// Returns false if the ammo can't be picked up at all +// + +doom_boolean +P_GiveAmmo +( player_t* player, + ammotype_t ammo, + int num ) +{ + int oldammo; + + if (ammo == am_noammo) + return false; + + if (ammo < 0 || ammo > NUMAMMO) + { + //I_Error ("P_GiveAmmo: bad type %i", ammo); + + doom_strcpy(error_buf, "P_GiveAmmo: bad type "); + doom_concat(error_buf, doom_itoa(ammo, 10)); + I_Error(error_buf); + } + + if ( player->ammo[ammo] == player->maxammo[ammo] ) + return false; + + if (num) + num *= clipammo[ammo]; + else + num = clipammo[ammo]/2; + + if (gameskill == sk_baby + || gameskill == sk_nightmare) + { + // give double ammo in trainer mode, + // you'll need in nightmare + num <<= 1; + } + + + oldammo = player->ammo[ammo]; + player->ammo[ammo] += num; + + if (player->ammo[ammo] > player->maxammo[ammo]) + player->ammo[ammo] = player->maxammo[ammo]; + + // If non zero ammo, + // don't change up weapons, + // player was lower on purpose. + if (oldammo) + return true; + + // We were down to zero, + // so select a new weapon. + // Preferences are not user selectable. + switch (ammo) + { + case am_clip: + if (player->readyweapon == wp_fist) + { + if (player->weaponowned[wp_chaingun]) + player->pendingweapon = wp_chaingun; + else + player->pendingweapon = wp_pistol; + } + break; + + case am_shell: + if (player->readyweapon == wp_fist + || player->readyweapon == wp_pistol) + { + if (player->weaponowned[wp_shotgun]) + player->pendingweapon = wp_shotgun; + } + break; + + case am_cell: + if (player->readyweapon == wp_fist + || player->readyweapon == wp_pistol) + { + if (player->weaponowned[wp_plasma]) + player->pendingweapon = wp_plasma; + } + break; + + case am_misl: + if (player->readyweapon == wp_fist) + { + if (player->weaponowned[wp_missile]) + player->pendingweapon = wp_missile; + } + default: + break; + } + + return true; +} + + +// +// P_GiveWeapon +// The weapon name may have a MF_DROPPED flag ored in. +// +doom_boolean +P_GiveWeapon +( player_t* player, + weapontype_t weapon, + doom_boolean dropped ) +{ + doom_boolean gaveammo; + doom_boolean gaveweapon; + + if (netgame + && (deathmatch!=2) + && !dropped ) + { + // leave placed weapons forever on net games + if (player->weaponowned[weapon]) + return false; + + player->bonuscount += BONUSADD; + player->weaponowned[weapon] = true; + + if (deathmatch) + P_GiveAmmo (player, weaponinfo[weapon].ammo, 5); + else + P_GiveAmmo (player, weaponinfo[weapon].ammo, 2); + player->pendingweapon = weapon; + + if (player == &players[consoleplayer]) + S_StartSound (0, sfx_wpnup); + return false; + } + + if (weaponinfo[weapon].ammo != am_noammo) + { + // give one clip with a dropped weapon, + // two clips with a found weapon + if (dropped) + gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1); + else + gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2); + } + else + gaveammo = false; + + if (player->weaponowned[weapon]) + gaveweapon = false; + else + { + gaveweapon = true; + player->weaponowned[weapon] = true; + player->pendingweapon = weapon; + } + + return (gaveweapon || gaveammo); +} + + + +// +// P_GiveBody +// Returns false if the body isn't needed at all +// +doom_boolean +P_GiveBody +( player_t* player, + int num ) +{ + if (player->health >= MAXHEALTH) + return false; + + player->health += num; + if (player->health > MAXHEALTH) + player->health = MAXHEALTH; + player->mo->health = player->health; + + return true; +} + + + +// +// P_GiveArmor +// Returns false if the armor is worse +// than the current armor. +// +doom_boolean +P_GiveArmor +( player_t* player, + int armortype ) +{ + int hits; + + hits = armortype*100; + if (player->armorpoints >= hits) + return false; // don't pick up + + player->armortype = armortype; + player->armorpoints = hits; + + return true; +} + + + +// +// P_GiveCard +// +void +P_GiveCard +( player_t* player, + card_t card ) +{ + if (player->cards[card]) + return; + + player->bonuscount = BONUSADD; + player->cards[card] = 1; +} + + +// +// P_GivePower +// +doom_boolean +P_GivePower +( player_t* player, + int /*powertype_t*/ power ) +{ + if (power == pw_invulnerability) + { + player->powers[power] = INVULNTICS; + return true; + } + + if (power == pw_invisibility) + { + player->powers[power] = INVISTICS; + player->mo->flags |= MF_SHADOW; + return true; + } + + if (power == pw_infrared) + { + player->powers[power] = INFRATICS; + return true; + } + + if (power == pw_ironfeet) + { + player->powers[power] = IRONTICS; + return true; + } + + if (power == pw_strength) + { + P_GiveBody (player, 100); + player->powers[power] = 1; + return true; + } + + if (player->powers[power]) + return false; // already got it + + player->powers[power] = 1; + return true; +} + + + +// +// P_TouchSpecialThing +// +void +P_TouchSpecialThing +( mobj_t* special, + mobj_t* toucher ) +{ + player_t* player; + int i; + fixed_t delta; + int sound; + + delta = special->z - toucher->z; + + if (delta > toucher->height + || delta < -8*FRACUNIT) + { + // out of reach + return; + } + + + sound = sfx_itemup; + 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 SPR_ARM1: + if (!P_GiveArmor (player, 1)) + return; + player->message = GOTARMOR; + break; + + case SPR_ARM2: + if (!P_GiveArmor (player, 2)) + return; + player->message = GOTMEGA; + break; + + // bonus items + case SPR_BON1: + player->health++; // can go over 100% + if (player->health > 200) + player->health = 200; + player->mo->health = player->health; + player->message = GOTHTHBONUS; + break; + + case SPR_BON2: + player->armorpoints++; // can go over 100% + if (player->armorpoints > 200) + player->armorpoints = 200; + if (!player->armortype) + player->armortype = 1; + player->message = GOTARMBONUS; + break; + + case SPR_SOUL: + player->health += 100; + if (player->health > 200) + player->health = 200; + player->mo->health = player->health; + player->message = GOTSUPER; + sound = sfx_getpow; + break; + + case SPR_MEGA: + if (gamemode != commercial) + return; + player->health = 200; + player->mo->health = player->health; + P_GiveArmor (player,2); + player->message = GOTMSPHERE; + sound = sfx_getpow; + break; + + // cards + // leave cards for everyone + case SPR_BKEY: + if (!player->cards[it_bluecard]) + player->message = GOTBLUECARD; + P_GiveCard (player, it_bluecard); + if (!netgame) + break; + return; + + case SPR_YKEY: + if (!player->cards[it_yellowcard]) + player->message = GOTYELWCARD; + P_GiveCard (player, it_yellowcard); + if (!netgame) + break; + return; + + case SPR_RKEY: + if (!player->cards[it_redcard]) + player->message = GOTREDCARD; + P_GiveCard (player, it_redcard); + if (!netgame) + break; + return; + + case SPR_BSKU: + if (!player->cards[it_blueskull]) + player->message = GOTBLUESKUL; + P_GiveCard (player, it_blueskull); + if (!netgame) + break; + return; + + case SPR_YSKU: + if (!player->cards[it_yellowskull]) + player->message = GOTYELWSKUL; + P_GiveCard (player, it_yellowskull); + if (!netgame) + break; + return; + + case SPR_RSKU: + if (!player->cards[it_redskull]) + player->message = GOTREDSKULL; + P_GiveCard (player, it_redskull); + if (!netgame) + break; + return; + + // medikits, heals + case SPR_STIM: + if (!P_GiveBody (player, 10)) + return; + player->message = GOTSTIM; + break; + + case SPR_MEDI: + if (!P_GiveBody (player, 25)) + return; + + if (player->health < 25) + player->message = GOTMEDINEED; + else + player->message = GOTMEDIKIT; + break; + + + // power ups + case SPR_PINV: + if (!P_GivePower (player, pw_invulnerability)) + return; + player->message = GOTINVUL; + sound = sfx_getpow; + break; + + case SPR_PSTR: + if (!P_GivePower (player, pw_strength)) + return; + player->message = GOTBERSERK; + if (player->readyweapon != wp_fist) + player->pendingweapon = wp_fist; + sound = sfx_getpow; + break; + + case SPR_PINS: + if (!P_GivePower (player, pw_invisibility)) + return; + player->message = GOTINVIS; + sound = sfx_getpow; + break; + + case SPR_SUIT: + if (!P_GivePower (player, pw_ironfeet)) + return; + player->message = GOTSUIT; + sound = sfx_getpow; + break; + + case SPR_PMAP: + if (!P_GivePower (player, pw_allmap)) + return; + player->message = GOTMAP; + sound = sfx_getpow; + break; + + case SPR_PVIS: + if (!P_GivePower (player, pw_infrared)) + return; + player->message = GOTVISOR; + sound = sfx_getpow; + break; + + // ammo + case SPR_CLIP: + if (special->flags & MF_DROPPED) + { + if (!P_GiveAmmo (player,am_clip,0)) + return; + } + else + { + if (!P_GiveAmmo (player,am_clip,1)) + return; + } + player->message = GOTCLIP; + break; + + case SPR_AMMO: + if (!P_GiveAmmo (player, am_clip,5)) + return; + player->message = GOTCLIPBOX; + break; + + case SPR_ROCK: + if (!P_GiveAmmo (player, am_misl,1)) + return; + player->message = GOTROCKET; + break; + + case SPR_BROK: + if (!P_GiveAmmo (player, am_misl,5)) + return; + player->message = GOTROCKBOX; + break; + + case SPR_CELL: + if (!P_GiveAmmo (player, am_cell,1)) + return; + player->message = GOTCELL; + break; + + case SPR_CELP: + if (!P_GiveAmmo (player, am_cell,5)) + return; + player->message = GOTCELLBOX; + break; + + case SPR_SHEL: + if (!P_GiveAmmo (player, am_shell,1)) + return; + player->message = GOTSHELLS; + break; + + case SPR_SBOX: + if (!P_GiveAmmo (player, am_shell,5)) + return; + player->message = GOTSHELLBOX; + break; + + case SPR_BPAK: + if (!player->backpack) + { + for (i=0 ; imaxammo[i] *= 2; + player->backpack = true; + } + for (i=0 ; imessage = GOTBACKPACK; + break; + + // weapons + case SPR_BFUG: + if (!P_GiveWeapon (player, wp_bfg, false) ) + return; + player->message = GOTBFG9000; + sound = sfx_wpnup; + break; + + case SPR_MGUN: + if (!P_GiveWeapon (player, wp_chaingun, special->flags&MF_DROPPED) ) + return; + player->message = GOTCHAINGUN; + sound = sfx_wpnup; + break; + + case SPR_CSAW: + if (!P_GiveWeapon (player, wp_chainsaw, false) ) + return; + player->message = GOTCHAINSAW; + sound = sfx_wpnup; + break; + + case SPR_LAUN: + if (!P_GiveWeapon (player, wp_missile, false) ) + return; + player->message = GOTLAUNCHER; + sound = sfx_wpnup; + break; + + case SPR_PLAS: + if (!P_GiveWeapon (player, wp_plasma, false) ) + return; + player->message = GOTPLASMA; + sound = sfx_wpnup; + break; + + case SPR_SHOT: + if (!P_GiveWeapon (player, wp_shotgun, special->flags&MF_DROPPED ) ) + return; + player->message = GOTSHOTGUN; + sound = sfx_wpnup; + break; + + case SPR_SGN2: + if (!P_GiveWeapon (player, wp_supershotgun, special->flags&MF_DROPPED ) ) + return; + player->message = GOTSHOTGUN2; + sound = sfx_wpnup; + break; + + default: + I_Error ("P_SpecialThing: Unknown gettable thing"); + } + + if (special->flags & MF_COUNTITEM) + player->itemcount++; + P_RemoveMobj (special); + player->bonuscount += BONUSADD; + if (player == &players[consoleplayer]) + S_StartSound (0, sound); +} + + +// +// KillMobj +// +void +P_KillMobj +( mobj_t* source, + mobj_t* target ) +{ + mobjtype_t item; + mobj_t* mo; + + target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY); + + if (target->type != MT_SKULL) + target->flags &= ~MF_NOGRAVITY; + + target->flags |= MF_CORPSE|MF_DROPOFF; + target->height >>= 2; + + if (source && source->player) + { + // count for intermission + if (target->flags & MF_COUNTKILL) + source->player->killcount++; + + if (target->player) + source->player->frags[target->player-players]++; + } + else if (!netgame && (target->flags & MF_COUNTKILL) ) + { + // count all monster deaths, + // even those caused by other monsters + players[0].killcount++; + } + + if (target->player) + { + // count environment kills against you + if (!source) + target->player->frags[target->player-players]++; + + target->flags &= ~MF_SOLID; + target->player->playerstate = PST_DEAD; + P_DropWeapon (target->player); + + if (target->player == &players[consoleplayer] + && automapactive) + { + // don't die in auto map, + // switch view prior to dying + AM_Stop (); + } + + } + + if (target->health < -target->info->spawnhealth + && target->info->xdeathstate) + { + P_SetMobjState (target, target->info->xdeathstate); + } + else + P_SetMobjState (target, target->info->deathstate); + target->tics -= P_Random()&3; + + if (target->tics < 1) + target->tics = 1; + + // I_StartSound (&actor->r, actor->info->deathsound); + + + // Drop stuff. + // This determines the kind of object spawned + // during the death frame of a thing. + switch (target->type) + { + case MT_WOLFSS: + case MT_POSSESSED: + item = MT_CLIP; + break; + + case MT_SHOTGUY: + item = MT_SHOTGUN; + break; + + case MT_CHAINGUY: + item = MT_CHAINGUN; + break; + + default: + return; + } + + mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item); + mo->flags |= MF_DROPPED; // special versions of items +} + + + + +// +// P_DamageMobj +// Damages both enemies and players +// "inflictor" is the thing that caused the damage +// creature or missile, can be 0 (slime, etc) +// "source" is the thing to target after taking damage +// creature or 0 +// Source and inflictor are the same for melee attacks. +// Source can be 0 for slime, barrel explosions +// and other environmental stuff. +// +void +P_DamageMobj +( mobj_t* target, + mobj_t* inflictor, + mobj_t* source, + int damage ) +{ + unsigned ang; + int saved; + player_t* player; + fixed_t thrust; + int temp; + + if ( !(target->flags & MF_SHOOTABLE) ) + return; // shouldn't happen... + + if (target->health <= 0) + return; + + if ( target->flags & MF_SKULLFLY ) + { + target->momx = target->momy = target->momz = 0; + } + + player = target->player; + if (player && gameskill == sk_baby) + damage >>= 1; // take half damage in trainer mode + + + // Some close combat weapons should not + // inflict thrust and push the victim out of reach, + // thus kick away unless using the chainsaw. + if (inflictor + && !(target->flags & MF_NOCLIP) + && (!source + || !source->player + || source->player->readyweapon != wp_chainsaw)) + { + ang = R_PointToAngle2 ( inflictor->x, + inflictor->y, + target->x, + target->y); + + thrust = damage*(FRACUNIT>>3)*100/target->info->mass; + + // make fall forwards sometimes + if ( damage < 40 + && damage > target->health + && target->z - inflictor->z > 64*FRACUNIT + && (P_Random ()&1) ) + { + ang += ANG180; + thrust *= 4; + } + + ang >>= ANGLETOFINESHIFT; + target->momx += FixedMul (thrust, finecosine[ang]); + target->momy += FixedMul (thrust, finesine[ang]); + } + + // player specific + if (player) + { + // end of game hell hack + if (target->subsector->sector->special == 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&CF_GODMODE) + || player->powers[pw_invulnerability] ) ) + { + return; + } + + if (player->armortype) + { + 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; + } + player->health -= damage; // mirror mobj health here for Dave + if (player->health < 0) + player->health = 0; + + player->attacker = source; + player->damagecount += damage; // add damage after armor / invuln + + if (player->damagecount > 100) + player->damagecount = 100; // teleport stomp does 10k points... + + temp = damage < 100 ? damage : 100; + + if (player == &players[consoleplayer]) + I_Tactile (40,10,40+temp*2); + } + + // do the damage + target->health -= damage; + if (target->health <= 0) + { + P_KillMobj (source, target); + return; + } + + if ( (P_Random () < target->info->painchance) + && !(target->flags&MF_SKULLFLY) ) + { + target->flags |= MF_JUSTHIT; // fight back! + + P_SetMobjState (target, target->info->painstate); + } + + target->reactiontime = 0; // we're awake now... + + if ( (!target->threshold || target->type == MT_VILE) + && source && source != target + && source->type != MT_VILE) + { + // if not intent on another player, + // chase after this one + target->target = source; + target->threshold = BASETHRESHOLD; + if (target->state == &states[target->info->spawnstate] + && target->info->seestate != S_NULL) + P_SetMobjState (target, target->info->seestate); + } + +} + +void T_FireFlicker(fireflicker_t* flick) +{ + int amount; + + if (--flick->count) + return; + + amount = (P_Random() & 3) * 16; + + if (flick->sector->lightlevel - amount < flick->minlight) + flick->sector->lightlevel = flick->minlight; + else + flick->sector->lightlevel = flick->maxlight - amount; + + flick->count = 4; +} + + +// +// P_SpawnFireFlicker +// +void P_SpawnFireFlicker(sector_t* sector) +{ + fireflicker_t* flick; + + // Note that we are resetting sector attributes. + // Nothing special about it during gameplay. + sector->special = 0; + + flick = Z_Malloc(sizeof(*flick), PU_LEVSPEC, 0); + + P_AddThinker(&flick->thinker); + + flick->thinker.function.acp1 = (actionf_p1)T_FireFlicker; + flick->sector = sector; + flick->maxlight = sector->lightlevel; + flick->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel) + 16; + flick->count = 4; +} + + +// +// BROKEN LIGHT FLASHING +// + +// +// T_LightFlash +// Do flashing lights. +// +void T_LightFlash(lightflash_t* flash) +{ + if (--flash->count) + return; + + if (flash->sector->lightlevel == flash->maxlight) + { + flash->sector->lightlevel = flash->minlight; + flash->count = (P_Random() & flash->mintime) + 1; + } + else + { + flash->sector->lightlevel = flash->maxlight; + flash->count = (P_Random() & flash->maxtime) + 1; + } +} + + +// +// P_SpawnLightFlash +// After the map has been loaded, scan each sector +// for specials that spawn thinkers +// +void P_SpawnLightFlash(sector_t* sector) +{ + lightflash_t* flash; + + // nothing special about it during gameplay + sector->special = 0; + + flash = Z_Malloc(sizeof(*flash), PU_LEVSPEC, 0); + + P_AddThinker(&flash->thinker); + + flash->thinker.function.acp1 = (actionf_p1)T_LightFlash; + flash->sector = sector; + flash->maxlight = sector->lightlevel; + + flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); + flash->maxtime = 64; + flash->mintime = 7; + flash->count = (P_Random() & flash->maxtime) + 1; +} + + +// +// STROBE LIGHT FLASHING +// + +// +// T_StrobeFlash +// +void T_StrobeFlash(strobe_t* flash) +{ + if (--flash->count) + return; + + if (flash->sector->lightlevel == flash->minlight) + { + flash->sector->lightlevel = flash->maxlight; + flash->count = flash->brighttime; + } + else + { + flash->sector->lightlevel = flash->minlight; + flash->count = flash->darktime; + } +} + + +// +// P_SpawnStrobeFlash +// After the map has been loaded, scan each sector +// for specials that spawn thinkers +// +void P_SpawnStrobeFlash(sector_t* sector, int fastOrSlow, int inSync) +{ + strobe_t* flash; + + flash = Z_Malloc(sizeof(*flash), PU_LEVSPEC, 0); + + P_AddThinker(&flash->thinker); + + flash->sector = sector; + flash->darktime = fastOrSlow; + flash->brighttime = STROBEBRIGHT; + flash->thinker.function.acp1 = (actionf_p1)T_StrobeFlash; + flash->maxlight = sector->lightlevel; + flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); + + if (flash->minlight == flash->maxlight) + flash->minlight = 0; + + // nothing special about it during gameplay + sector->special = 0; + + if (!inSync) + flash->count = (P_Random() & 7) + 1; + else + flash->count = 1; +} + + +// +// Start strobing lights (usually from a trigger) +// +void EV_StartLightStrobing(line_t* line) +{ + int secnum; + sector_t* sec; + + secnum = -1; + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) + continue; + + P_SpawnStrobeFlash(sec, SLOWDARK, 0); + } +} + + +// +// TURN LINE'S TAG LIGHTS OFF +// +void EV_TurnTagLightsOff(line_t* line) +{ + int i; + int j; + int min; + sector_t* sector; + sector_t* tsec; + line_t* templine; + + sector = sectors; + + for (j = 0; j < numsectors; j++, sector++) + { + if (sector->tag == line->tag) + { + min = sector->lightlevel; + for (i = 0; i < sector->linecount; i++) + { + templine = sector->lines[i]; + tsec = getNextSector(templine, sector); + if (!tsec) + continue; + if (tsec->lightlevel < min) + min = tsec->lightlevel; + } + sector->lightlevel = min; + } + } +} + + +// +// TURN LINE'S TAG LIGHTS ON +// +void EV_LightTurnOn(line_t* line, int bright) +{ + int i; + int j; + sector_t* sector; + sector_t* temp; + line_t* templine; + + sector = sectors; + + for (i = 0; i < numsectors; i++, sector++) + { + if (sector->tag == line->tag) + { + // bright = 0 means to search + // for highest light level + // surrounding sector + if (!bright) + { + for (j = 0; j < sector->linecount; j++) + { + templine = sector->lines[j]; + temp = getNextSector(templine, sector); + + if (!temp) + continue; + + if (temp->lightlevel > bright) + bright = temp->lightlevel; + } + } + sector->lightlevel = bright; + } + } +} + + +// +// Spawn glowing light +// +void T_Glow(glow_t* g) +{ + switch (g->direction) + { + case -1: + // DOWN + g->sector->lightlevel -= GLOWSPEED; + if (g->sector->lightlevel <= g->minlight) + { + g->sector->lightlevel += GLOWSPEED; + g->direction = 1; + } + break; + + case 1: + // UP + g->sector->lightlevel += GLOWSPEED; + if (g->sector->lightlevel >= g->maxlight) + { + g->sector->lightlevel -= GLOWSPEED; + g->direction = -1; + } + break; + } +} + + +void P_SpawnGlowingLight(sector_t* sector) +{ + glow_t* g; + + g = Z_Malloc(sizeof(*g), PU_LEVSPEC, 0); + + P_AddThinker(&g->thinker); + + g->sector = sector; + g->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); + g->maxlight = sector->lightlevel; + g->thinker.function.acp1 = (actionf_p1)T_Glow; + g->direction = -1; + + sector->special = 0; +} +#define MAXSPECIALCROSS 8 + + +fixed_t tmbbox[4]; +mobj_t* tmthing; +int tmflags; +fixed_t tmx; +fixed_t tmy; + +// If "floatok" true, move would be ok +// if within "tmfloorz - tmceilingz". +doom_boolean floatok; + +fixed_t tmfloorz; +fixed_t tmceilingz; +fixed_t tmdropoffz; + +// keep track of the line that lowers the ceiling, +// so missiles don't explode against sky hack walls +line_t* ceilingline; + +// keep track of special lines as they are hit, +// but don't process them until the move is proven valid + +line_t* spechit[MAXSPECIALCROSS]; +int numspechit; + +mobj_t* linetarget; // who got hit (or 0) +mobj_t* shootthing; + +// Height if not aiming up or down +// ???: use slope for monsters? +fixed_t shootz; + +int la_damage; +fixed_t attackrange; + +fixed_t aimslope; +mobj_t* usething; +doom_boolean crushchange; +doom_boolean nofit; + + +// slopes to top and bottom of target +extern fixed_t topslope; +extern fixed_t bottomslope; + + +// +// TELEPORT MOVE +// + +// +// PIT_StompThing +// +doom_boolean PIT_StompThing(mobj_t* thing) +{ + fixed_t blockdist; + + if (!(thing->flags & MF_SHOOTABLE)) + return true; + + blockdist = thing->radius + tmthing->radius; + + if (doom_abs(thing->x - tmx) >= blockdist + || doom_abs(thing->y - tmy) >= blockdist) + { + // didn't hit it + return true; + } + + // don't clip against self + if (thing == tmthing) + return true; + + // monsters don't stomp things except on boss level + if (!tmthing->player && gamemap != 30) + return false; + + P_DamageMobj(thing, tmthing, tmthing, 10000); + + return true; +} + + +// +// P_TeleportMove +// +doom_boolean P_TeleportMove(mobj_t* thing, fixed_t x, fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + + subsector_t* newsubsec; + + // kill anything occupying the position + tmthing = thing; + tmflags = thing->flags; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector(x, y); + ceilingline = 0; + + // The base floor/ceiling is from the subsector + // that contains the point. + // Any contacted lines the step closer together + // will adjust them. + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + + validcount++; + numspechit = 0; + + // stomp on any things contacted + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + + for (bx = xl; bx <= xh; bx++) + for (by = yl; by <= yh; by++) + if (!P_BlockThingsIterator(bx, by, PIT_StompThing)) + return false; + + // the move is ok, + // so link the thing into its new position + P_UnsetThingPosition(thing); + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->x = x; + thing->y = y; + + P_SetThingPosition(thing); + + return true; +} + + +// +// MOVEMENT ITERATOR FUNCTIONS +// + +// +// PIT_CheckLine +// Adjusts tmfloorz and tmceilingz as lines are contacted +// +doom_boolean PIT_CheckLine(line_t* ld) +{ + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] + || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] + || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) + return true; + + if (P_BoxOnLineSide(tmbbox, ld) != -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 (!ld->backsector) + return false; // one sided line + + if (!(tmthing->flags & MF_MISSILE)) + { + if (ld->flags & ML_BLOCKING) + return false; // explicitly blocking everything + + if (!tmthing->player && ld->flags & ML_BLOCKMONSTERS) + return false; // block monsters only + } + + // set openrange, opentop, openbottom + P_LineOpening(ld); + + // adjust floor / ceiling heights + if (opentop < tmceilingz) + { + tmceilingz = opentop; + ceilingline = ld; + } + + if (openbottom > tmfloorz) + tmfloorz = openbottom; + + if (lowfloor < tmdropoffz) + tmdropoffz = lowfloor; + + // if contacted a special line, add it to the list + if (ld->special) + { + spechit[numspechit] = ld; + numspechit++; + } + + return true; +} + + +// +// PIT_CheckThing +// +doom_boolean PIT_CheckThing(mobj_t* thing) +{ + fixed_t blockdist; + doom_boolean solid; + int damage; + + if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE))) + return true; + + blockdist = thing->radius + tmthing->radius; + + if (doom_abs(thing->x - tmx) >= blockdist + || doom_abs(thing->y - tmy) >= blockdist) + { + // didn't hit it + return true; + } + + // don't clip against self + if (thing == tmthing) + return true; + + // check for skulls slamming into things + if (tmthing->flags & MF_SKULLFLY) + { + damage = ((P_Random() % 8) + 1) * tmthing->info->damage; + + P_DamageMobj(thing, tmthing, tmthing, damage); + + tmthing->flags &= ~MF_SKULLFLY; + tmthing->momx = tmthing->momy = tmthing->momz = 0; + + P_SetMobjState(tmthing, tmthing->info->spawnstate); + + return false; // stop moving + } + + + // missiles can hit other things + if (tmthing->flags & MF_MISSILE) + { + // see if it went over / under + if (tmthing->z > thing->z + thing->height) + return true; // overhead + if (tmthing->z + tmthing->height < thing->z) + return true; // underneath + + if (tmthing->target && ( + tmthing->target->type == thing->type || + (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER) || + (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT))) + { + // Don't hit same species as originator. + if (thing == tmthing->target) + return true; + + if (thing->type != MT_PLAYER) + { + // Explode, but do no damage. + // Let players missile other players. + return false; + } + } + + if (!(thing->flags & MF_SHOOTABLE)) + { + // didn't do any damage + return !(thing->flags & MF_SOLID); + } + + // damage / explode + damage = ((P_Random() % 8) + 1) * tmthing->info->damage; + P_DamageMobj(thing, tmthing, tmthing->target, damage); + + // don't traverse any more + return false; + } + + // check for special pickup + if (thing->flags & MF_SPECIAL) + { + solid = thing->flags & MF_SOLID; + if (tmflags & MF_PICKUP) + { + // can remove thing + P_TouchSpecialThing(thing, tmthing); + } + return !solid; + } + + return !(thing->flags & MF_SOLID); +} + + +// +// MOVEMENT CLIPPING +// + +// +// P_CheckPosition +// This is purely informative, nothing is modified +// (except things picked up). +// +// in: +// a mobj_t (can be valid or invalid) +// a position to be checked +// (doesn't need to be related to the mobj_t->x,y) +// +// during: +// special things are touched if MF_PICKUP +// early out on solid lines? +// +// out: +// newsubsec +// floorz +// ceilingz +// tmdropoffz +// the lowest point contacted +// (monsters won't move to a dropoff) +// speciallines[] +// numspeciallines +// +doom_boolean P_CheckPosition(mobj_t* thing, fixed_t x, fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + subsector_t* newsubsec; + + tmthing = thing; + tmflags = thing->flags; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector(x, y); + ceilingline = 0; + + // The base floor / ceiling is from the subsector + // that contains the point. + // Any contacted lines the step closer together + // will adjust them. + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + + validcount++; + numspechit = 0; + + if (tmflags & MF_NOCLIP) + return true; + + // Check things first, possibly picking things up. + // The bounding box is extended by MAXRADIUS + // because mobj_ts are grouped into mapblocks + // based on their origin point, and can overlap + // into adjacent blocks by up to MAXRADIUS units. + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + + for (bx = xl; bx <= xh; bx++) + for (by = yl; by <= yh; by++) + if (!P_BlockThingsIterator(bx, by, PIT_CheckThing)) + return false; + + // check lines + xl = (tmbbox[BOXLEFT] - bmaporgx) >> MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx) >> MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy) >> MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy) >> MAPBLOCKSHIFT; + + for (bx = xl; bx <= xh; bx++) + for (by = yl; by <= yh; by++) + if (!P_BlockLinesIterator(bx, by, PIT_CheckLine)) + return false; + + return true; +} + + +// +// P_TryMove +// Attempt to move to a new position, +// crossing special lines unless MF_TELEPORT is set. +// +doom_boolean P_TryMove(mobj_t* thing, fixed_t x, fixed_t y) +{ + fixed_t oldx; + fixed_t oldy; + int side; + int oldside; + line_t* ld; + + floatok = false; + if (!P_CheckPosition(thing, x, y)) + return false; // solid wall or thing + + if (!(thing->flags & MF_NOCLIP)) + { + if (tmceilingz - tmfloorz < thing->height) + return false; // doesn't fit + + floatok = true; + + if (!(thing->flags & MF_TELEPORT) + && tmceilingz - thing->z < thing->height) + return false; // mobj must lower itself to fit + + if (!(thing->flags & MF_TELEPORT) + && tmfloorz - thing->z > 24 * FRACUNIT) + return false; // too big a step up + + if (!(thing->flags & (MF_DROPOFF | MF_FLOAT)) + && tmfloorz - tmdropoffz > 24 * FRACUNIT) + return false; // don't stand over a dropoff + } + + // the move is ok, + // so link the thing into its new position + P_UnsetThingPosition(thing); + + oldx = thing->x; + oldy = thing->y; + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->x = x; + thing->y = y; + + P_SetThingPosition(thing); + + // if any special lines were hit, do the effect + if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP))) + { + while (numspechit--) + { + // see if the line was crossed + ld = spechit[numspechit]; + side = P_PointOnLineSide(thing->x, thing->y, ld); + oldside = P_PointOnLineSide(oldx, oldy, ld); + if (side != oldside) + { + if (ld->special) + P_CrossSpecialLine((int)(ld - lines), oldside, thing); + } + } + } + + return true; +} + + +// +// P_ThingHeightClip +// Takes a valid thing and adjusts the thing->floorz, +// thing->ceilingz, and possibly thing->z. +// This is called for all nearby monsters +// whenever a sector changes height. +// If the thing doesn't fit, +// the z will be set to the lowest value +// and false will be returned. +// +doom_boolean P_ThingHeightClip(mobj_t* thing) +{ + doom_boolean onfloor; + + onfloor = (thing->z == thing->floorz); + + P_CheckPosition(thing, thing->x, thing->y); + // what about stranding a monster partially off an edge? + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + + 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; +} + + +// +// SLIDE MOVE +// Allows the player to slide along any angled walls. +// +fixed_t bestslidefrac; +fixed_t secondslidefrac; + +line_t* bestslideline; +line_t* secondslideline; + +mobj_t* slidemo; + +fixed_t tmxmove; +fixed_t tmymove; + + +// +// P_HitSlideLine +// Adjusts the xmove / ymove +// so that the next move will slide along the wall. +// +void P_HitSlideLine(line_t* ld) +{ + int side; + + angle_t lineangle; + angle_t moveangle; + angle_t deltaangle; + + fixed_t movelen; + fixed_t newlen; + + if (ld->slopetype == ST_HORIZONTAL) + { + tmymove = 0; + return; + } + + if (ld->slopetype == ST_VERTICAL) + { + tmxmove = 0; + return; + } + + side = P_PointOnLineSide(slidemo->x, slidemo->y, ld); + + lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy); + + if (side == 1) + lineangle += ANG180; + + moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove); + deltaangle = moveangle - lineangle; + + if (deltaangle > ANG180) + deltaangle += ANG180; + + lineangle >>= ANGLETOFINESHIFT; + deltaangle >>= ANGLETOFINESHIFT; + + movelen = P_AproxDistance(tmxmove, tmymove); + newlen = FixedMul(movelen, finecosine[deltaangle]); + + tmxmove = FixedMul(newlen, finecosine[lineangle]); + tmymove = FixedMul(newlen, finesine[lineangle]); +} + + +// +// PTR_SlideTraverse +// +doom_boolean PTR_SlideTraverse(intercept_t* in) +{ + line_t* li; + + if (!in->isaline) + I_Error("Error: PTR_SlideTraverse: not a line?"); + + li = in->d.line; + + if (!(li->flags & ML_TWOSIDED)) + { + if (P_PointOnLineSide(slidemo->x, slidemo->y, li)) + { + // don't hit the back side + return true; + } + goto isblocking; + } + + // set openrange, opentop, openbottom + P_LineOpening(li); + + if (openrange < slidemo->height) + goto isblocking; // doesn't fit + + if (opentop - slidemo->z < slidemo->height) + goto isblocking; // mobj is too high + + if (openbottom - slidemo->z > 24 * FRACUNIT) + goto isblocking; // too big a step up + + // this line doesn't block movement + return true; + + // the line does block movement, + // see if it is closer than best so far +isblocking: + if (in->frac < bestslidefrac) + { + secondslidefrac = bestslidefrac; + secondslideline = bestslideline; + bestslidefrac = in->frac; + bestslideline = li; + } + + return false; // stop +} + + +// +// P_SlideMove +// 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. +// +void P_SlideMove(mobj_t* mo) +{ + fixed_t leadx; + fixed_t leady; + fixed_t trailx; + fixed_t traily; + fixed_t newx; + fixed_t newy; + int hitcount; + + slidemo = mo; + hitcount = 0; + +retry: + if (++hitcount == 3) + goto stairstep; // don't loop forever + + + // trace along the three leading corners + if (mo->momx > 0) + { + leadx = mo->x + mo->radius; + trailx = mo->x - mo->radius; + } + else + { + leadx = mo->x - mo->radius; + trailx = mo->x + mo->radius; + } + + if (mo->momy > 0) + { + leady = mo->y + mo->radius; + traily = mo->y - mo->radius; + } + else + { + leady = mo->y - mo->radius; + traily = mo->y + mo->radius; + } + + bestslidefrac = FRACUNIT + 1; + + P_PathTraverse(leadx, leady, leadx + mo->momx, leady + mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + P_PathTraverse(trailx, leady, trailx + mo->momx, leady + mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + P_PathTraverse(leadx, traily, leadx + mo->momx, traily + mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + + // move up to the wall + if (bestslidefrac == FRACUNIT + 1) + { + // the move most have hit the middle, so stairstep +stairstep: + if (!P_TryMove(mo, mo->x, mo->y + mo->momy)) + P_TryMove(mo, mo->x + mo->momx, mo->y); + return; + } + + // fudge a bit to make sure it doesn't hit + bestslidefrac -= 0x800; + if (bestslidefrac > 0) + { + newx = FixedMul(mo->momx, bestslidefrac); + newy = FixedMul(mo->momy, bestslidefrac); + + if (!P_TryMove(mo, mo->x + newx, mo->y + newy)) + goto stairstep; + } + + // Now continue along the wall. + // First calculate remainder. + bestslidefrac = FRACUNIT - (bestslidefrac + 0x800); + + if (bestslidefrac > FRACUNIT) + bestslidefrac = FRACUNIT; + + if (bestslidefrac <= 0) + return; + + tmxmove = FixedMul(mo->momx, bestslidefrac); + tmymove = FixedMul(mo->momy, bestslidefrac); + + P_HitSlideLine(bestslideline); // clip the moves + + mo->momx = tmxmove; + mo->momy = tmymove; + + if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove)) + { + goto retry; + } +} + + +// +// P_LineAttack +// + +// +// PTR_AimTraverse +// Sets linetaget and aimslope when a target is aimed at. +// +doom_boolean +PTR_AimTraverse(intercept_t* in) +{ + line_t* li; + mobj_t* th; + fixed_t slope; + fixed_t thingtopslope; + fixed_t thingbottomslope; + fixed_t dist; + + if (in->isaline) + { + li = in->d.line; + + if (!(li->flags & ML_TWOSIDED)) + return false; // stop + + // Crosses a two sided line. + // A two sided line will restrict + // the possible target ranges. + P_LineOpening(li); + + if (openbottom >= opentop) + return false; // stop + + dist = FixedMul(attackrange, in->frac); + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv(openbottom - shootz, dist); + if (slope > bottomslope) + bottomslope = slope; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv(opentop - shootz, dist); + if (slope < topslope) + topslope = slope; + } + + if (topslope <= bottomslope) + return false; // stop + + return true; // shot continues + } + + // shoot a thing + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + + if (!(th->flags & MF_SHOOTABLE)) + return true; // corpse or something + + // check angles to see if the thing can be aimed at + dist = FixedMul(attackrange, in->frac); + thingtopslope = FixedDiv(th->z + th->height - shootz, dist); + + if (thingtopslope < bottomslope) + return true; // shot over the thing + + thingbottomslope = FixedDiv(th->z - shootz, dist); + + if (thingbottomslope > topslope) + return true; // shot under the thing + + // this thing can be hit! + if (thingtopslope > topslope) + thingtopslope = topslope; + + if (thingbottomslope < bottomslope) + thingbottomslope = bottomslope; + + aimslope = (thingtopslope + thingbottomslope) / 2; + linetarget = th; + + return false; // don't go any farther +} + + +// +// PTR_ShootTraverse +// +doom_boolean PTR_ShootTraverse(intercept_t* in) +{ + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t frac; + + line_t* li; + + mobj_t* th; + + fixed_t slope; + fixed_t dist; + fixed_t thingtopslope; + fixed_t thingbottomslope; + + if (in->isaline) + { + li = in->d.line; + + if (li->special) + P_ShootSpecialLine(shootthing, li); + + if (!(li->flags & ML_TWOSIDED)) + goto hitline; + + // crosses a two sided line + P_LineOpening(li); + + dist = FixedMul(attackrange, in->frac); + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv(openbottom - shootz, dist); + if (slope > aimslope) + goto hitline; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv(opentop - shootz, dist); + if (slope < aimslope) + goto hitline; + } + + // shot continues + return true; + + + // hit line +hitline: + // position a bit closer + frac = in->frac - FixedDiv(4 * FRACUNIT, attackrange); + x = trace.x + FixedMul(trace.dx, frac); + y = trace.y + FixedMul(trace.dy, frac); + z = shootz + FixedMul(aimslope, FixedMul(frac, attackrange)); + + if (li->frontsector->ceilingpic == skyflatnum) + { + // don't shoot the sky! + if (z > li->frontsector->ceilingheight) + return false; + + // it's a sky hack wall + if (li->backsector && li->backsector->ceilingpic == skyflatnum) + return false; + } + + // Spawn bullet puffs. + P_SpawnPuff(x, y, z); + + // don't go any farther + return false; + } + + // shoot a thing + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + + if (!(th->flags & MF_SHOOTABLE)) + return true; // corpse or something + + // check angles to see if the thing can be aimed at + dist = FixedMul(attackrange, in->frac); + thingtopslope = FixedDiv(th->z + th->height - shootz, dist); + + if (thingtopslope < aimslope) + return true; // shot over the thing + + thingbottomslope = FixedDiv(th->z - shootz, dist); + + if (thingbottomslope > aimslope) + return true; // shot under the thing + + + // hit thing + // position a bit closer + frac = in->frac - FixedDiv(10 * FRACUNIT, attackrange); + + x = trace.x + FixedMul(trace.dx, frac); + y = trace.y + FixedMul(trace.dy, frac); + z = shootz + FixedMul(aimslope, FixedMul(frac, attackrange)); + + // Spawn bullet puffs or blod spots, + // depending on target type. + if (in->d.thing->flags & MF_NOBLOOD) + P_SpawnPuff(x, y, z); + else + P_SpawnBlood(x, y, z, la_damage); + + if (la_damage) + P_DamageMobj(th, shootthing, shootthing, la_damage); + + // don't go any farther + return false; +} + + +// +// P_AimLineAttack +// +fixed_t P_AimLineAttack(mobj_t* t1, angle_t angle, fixed_t distance) +{ + fixed_t x2; + fixed_t y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + + x2 = t1->x + (distance >> FRACBITS) * finecosine[angle]; + y2 = t1->y + (distance >> FRACBITS) * finesine[angle]; + shootz = t1->z + (t1->height >> 1) + 8 * FRACUNIT; + + // can't shoot outside view angles + topslope = 100 * FRACUNIT / 160; + bottomslope = -100 * FRACUNIT / 160; + + attackrange = distance; + linetarget = 0; + + P_PathTraverse(t1->x, t1->y, + x2, y2, + PT_ADDLINES | PT_ADDTHINGS, + PTR_AimTraverse); + + if (linetarget) + return aimslope; + + return 0; +} + + +// +// P_LineAttack +// If damage == 0, it is just a test trace +// that will leave linetarget set. +// +void P_LineAttack(mobj_t* t1, angle_t angle, fixed_t distance, fixed_t slope, int damage) +{ + fixed_t x2; + fixed_t y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + la_damage = damage; + x2 = t1->x + (distance >> FRACBITS) * finecosine[angle]; + y2 = t1->y + (distance >> FRACBITS) * finesine[angle]; + shootz = t1->z + (t1->height >> 1) + 8 * FRACUNIT; + attackrange = distance; + aimslope = slope; + + P_PathTraverse(t1->x, t1->y, + x2, y2, + PT_ADDLINES | PT_ADDTHINGS, + PTR_ShootTraverse); +} + + +// +// USE LINES +// +doom_boolean PTR_UseTraverse(intercept_t* in) +{ + int side; + + if (!in->d.line->special) + { + P_LineOpening(in->d.line); + if (openrange <= 0) + { + S_StartSound(usething, sfx_noway); + + // can't use through a wall + return false; + } + // not a special line, but keep checking + return true; + } + + side = 0; + if (P_PointOnLineSide(usething->x, usething->y, in->d.line) == 1) + side = 1; + + P_UseSpecialLine(usething, in->d.line, side); + + // can't use for than one special line in a row + return false; +} + + +// +// P_UseLines +// Looks for special lines in front of the player to activate. +// +void P_UseLines(player_t* player) +{ + int angle; + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + usething = player->mo; + + angle = player->mo->angle >> ANGLETOFINESHIFT; + + x1 = player->mo->x; + y1 = player->mo->y; + x2 = x1 + (USERANGE >> FRACBITS) * finecosine[angle]; + y2 = y1 + (USERANGE >> FRACBITS) * finesine[angle]; + + P_PathTraverse(x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse); +} + + +// +// RADIUS ATTACK +// +mobj_t* bombsource; +mobj_t* bombspot; +int bombdamage; + + +// +// PIT_RadiusAttack +// "bombsource" is the creature +// that caused the explosion at "bombspot". +// +doom_boolean PIT_RadiusAttack(mobj_t* thing) +{ + fixed_t dx; + fixed_t dy; + fixed_t dist; + + if (!(thing->flags & MF_SHOOTABLE)) + return true; + + // Boss spider and cyborg + // take no damage from concussion. + if (thing->type == MT_CYBORG + || thing->type == MT_SPIDER) + return true; + + dx = doom_abs(thing->x - bombspot->x); + dy = doom_abs(thing->y - bombspot->y); + + dist = dx > dy ? dx : dy; + dist = (dist - thing->radius) >> FRACBITS; + + if (dist < 0) + dist = 0; + + if (dist >= bombdamage) + return true; // out of range + + if (P_CheckSight(thing, bombspot)) + { + // must be in direct path + P_DamageMobj(thing, bombspot, bombsource, bombdamage - dist); + } + + return true; +} + + +// +// P_RadiusAttack +// Source is the creature that caused the explosion at spot. +// +void P_RadiusAttack(mobj_t* spot, mobj_t* source, int damage) +{ + int x; + int y; + + int xl; + int xh; + int yl; + int yh; + + fixed_t dist; + + dist = (damage + MAXRADIUS) << FRACBITS; + yh = (spot->y + dist - bmaporgy) >> MAPBLOCKSHIFT; + yl = (spot->y - dist - bmaporgy) >> MAPBLOCKSHIFT; + xh = (spot->x + dist - bmaporgx) >> MAPBLOCKSHIFT; + xl = (spot->x - dist - bmaporgx) >> MAPBLOCKSHIFT; + bombspot = spot; + bombsource = source; + bombdamage = damage; + + for (y = yl; y <= yh; y++) + for (x = xl; x <= xh; x++) + P_BlockThingsIterator(x, y, PIT_RadiusAttack); +} + + +// +// 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. +// + +// +// PIT_ChangeSector +// +doom_boolean PIT_ChangeSector(mobj_t* thing) +{ + mobj_t* mo; + + if (P_ThingHeightClip(thing)) + { + // keep checking + return true; + } + + + // crunch bodies to giblets + if (thing->health <= 0) + { + P_SetMobjState(thing, S_GIBS); + + thing->flags &= ~MF_SOLID; + thing->height = 0; + thing->radius = 0; + + // keep checking + return true; + } + + // crunch dropped items + if (thing->flags & MF_DROPPED) + { + P_RemoveMobj(thing); + + // keep checking + return true; + } + + if (!(thing->flags & MF_SHOOTABLE)) + { + // assume it is bloody gibs or something + return true; + } + + nofit = true; + + if (crushchange && !(leveltime & 3)) + { + P_DamageMobj(thing, 0, 0, 10); + + // spray blood in a random direction + mo = P_SpawnMobj(thing->x, + thing->y, + thing->z + thing->height / 2, MT_BLOOD); + + mo->momx = (P_Random() - P_Random()) << 12; + mo->momy = (P_Random() - P_Random()) << 12; + } + + // keep checking (crush other things) + return true; +} + + +// +// P_ChangeSector +// +doom_boolean P_ChangeSector(sector_t* sector, doom_boolean crunch) +{ + int x; + int y; + + nofit = false; + crushchange = crunch; + + // re-check heights for all things near the moving sector + for (x = sector->blockbox[BOXLEFT]; x <= sector->blockbox[BOXRIGHT]; x++) + for (y = sector->blockbox[BOXBOTTOM]; y <= sector->blockbox[BOXTOP]; y++) + P_BlockThingsIterator(x, y, PIT_ChangeSector); + + return nofit; +} +fixed_t opentop; +fixed_t openbottom; +fixed_t openrange; +fixed_t lowfloor; +intercept_t intercepts[MAXINTERCEPTS]; +intercept_t* intercept_p; +divline_t trace; +doom_boolean earlyout; +int ptflags; + + +// +// P_AproxDistance +// Gives an estimation of distance (not exact) +// +fixed_t P_AproxDistance(fixed_t dx, fixed_t dy) +{ + dx = doom_abs(dx); + dy = doom_abs(dy); + if (dx < dy) + return dx + dy - (dx >> 1); + return dx + dy - (dy >> 1); +} + + +// +// P_PointOnLineSide +// Returns 0 or 1 +// +int P_PointOnLineSide(fixed_t x, fixed_t y, line_t* line) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!line->dx) + { + if (x <= line->v1->x) + return line->dy > 0; + + return line->dy < 0; + } + if (!line->dy) + { + if (y <= line->v1->y) + return line->dx < 0; + + return line->dx > 0; + } + + dx = (x - line->v1->x); + dy = (y - line->v1->y); + + left = FixedMul(line->dy >> FRACBITS, dx); + right = FixedMul(dy, line->dx >> FRACBITS); + + if (right < left) + return 0; // front side + return 1; // back side +} + + +// +// P_BoxOnLineSide +// Considers the line to be infinite +// Returns side 0 or 1, -1 if box crosses the line. +// +int P_BoxOnLineSide(fixed_t* tmbox, line_t* ld) +{ + int p1; + int p2; + + switch (ld->slopetype) + { + case ST_HORIZONTAL: + p1 = tmbox[BOXTOP] > ld->v1->y; + p2 = tmbox[BOXBOTTOM] > ld->v1->y; + if (ld->dx < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case ST_VERTICAL: + p1 = tmbox[BOXRIGHT] < ld->v1->x; + p2 = tmbox[BOXLEFT] < ld->v1->x; + if (ld->dy < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case ST_POSITIVE: + p1 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld); + p2 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); + break; + + case ST_NEGATIVE: + p1 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld); + p2 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); + break; + } + + if (p1 == p2) + return p1; + return -1; +} + + +// +// P_PointOnDivlineSide +// Returns 0 or 1. +// +int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t* line) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!line->dx) + { + if (x <= line->x) + return line->dy > 0; + + return line->dy < 0; + } + if (!line->dy) + { + if (y <= line->y) + return line->dx < 0; + + return line->dx > 0; + } + + dx = (x - line->x); + dy = (y - line->y); + + // try to quickly decide by looking at sign bits + if ((line->dy ^ line->dx ^ dx ^ dy) & 0x80000000) + { + if ((line->dy ^ dx) & 0x80000000) + return 1; // (left is negative) + return 0; + } + + left = FixedMul(line->dy >> 8, dx >> 8); + right = FixedMul(dy >> 8, line->dx >> 8); + + if (right < left) + return 0; // front side + return 1; // back side +} + + +// +// P_MakeDivline +// +void P_MakeDivline(line_t* li, divline_t* dl) +{ + dl->x = li->v1->x; + dl->y = li->v1->y; + dl->dx = li->dx; + dl->dy = li->dy; +} + + +// +// P_InterceptVector +// Returns the fractional intercept point +// along the first divline. +// This is only called by the addthings +// and addlines traversers. +// +fixed_t P_InterceptVector(divline_t* v2, divline_t* v1) +{ + fixed_t frac; + fixed_t num; + fixed_t den; + + den = FixedMul(v1->dy >> 8, v2->dx) - FixedMul(v1->dx >> 8, v2->dy); + + if (den == 0) + return 0; + // I_Error ("P_InterceptVector: parallel"); + + num = + FixedMul((v1->x - v2->x) >> 8, v1->dy) + + FixedMul((v2->y - v1->y) >> 8, v1->dx); + + frac = FixedDiv(num, den); + + return frac; +} + + +// +// P_LineOpening +// Sets opentop and openbottom to the window +// through a two sided line. +// OPTIMIZE: keep this precalculated +// +void P_LineOpening(line_t* linedef) +{ + sector_t* front; + sector_t* back; + + if (linedef->sidenum[1] == -1) + { + // single sided line + openrange = 0; + return; + } + + front = linedef->frontsector; + back = linedef->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; +} + + +// +// THING POSITION SETTING +// + +// +// P_UnsetThingPosition +// 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. +// +void P_UnsetThingPosition(mobj_t* thing) +{ + int blockx; + int blocky; + + if (!(thing->flags & MF_NOSECTOR)) + { + // inert things don't need to be in blockmap? + // unlink from subsector + if (thing->snext) + thing->snext->sprev = thing->sprev; + + if (thing->sprev) + thing->sprev->snext = thing->snext; + else + thing->subsector->sector->thinglist = thing->snext; + } + + if (!(thing->flags & MF_NOBLOCKMAP)) + { + // inert things don't need to be in blockmap + // unlink from block map + if (thing->bnext) + thing->bnext->bprev = thing->bprev; + + if (thing->bprev) + thing->bprev->bnext = thing->bnext; + else + { + blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT; + blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT; + + if (blockx >= 0 && blockx < bmapwidth + && blocky >= 0 && blocky < bmapheight) + { + blocklinks[blocky * bmapwidth + blockx] = thing->bnext; + } + } + } +} + + +// +// P_SetThingPosition +// Links a thing into both a block and a subsector +// based on it's x y. +// Sets thing->subsector properly +// +void P_SetThingPosition(mobj_t* thing) +{ + subsector_t* ss; + sector_t* sec; + int blockx; + int blocky; + mobj_t** link; + + + // link into subsector + ss = R_PointInSubsector(thing->x, thing->y); + thing->subsector = ss; + + if (!(thing->flags & MF_NOSECTOR)) + { + // invisible things don't go into the sector links + sec = ss->sector; + + thing->sprev = 0; + thing->snext = sec->thinglist; + + if (sec->thinglist) + sec->thinglist->sprev = thing; + + sec->thinglist = thing; + } + + // link into blockmap + if (!(thing->flags & MF_NOBLOCKMAP)) + { + // inert things don't need to be in blockmap + blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT; + blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT; + + if (blockx >= 0 + && blockx < bmapwidth + && blocky >= 0 + && blocky < bmapheight) + { + link = &blocklinks[blocky * bmapwidth + blockx]; + thing->bprev = 0; + thing->bnext = *link; + if (*link) + (*link)->bprev = thing; + + *link = thing; + } + else + { + // thing is off the map + thing->bnext = thing->bprev = 0; + } + } +} + + +// +// BLOCK MAP ITERATORS +// For each line/thing in the given mapblock, +// call the passed PIT_* function. +// If the function returns false, +// exit with false without checking anything else. +// + +// +// P_BlockLinesIterator +// The validcount flags are used to avoid checking lines +// that are marked in multiple mapblocks, +// so increment validcount before the first call +// to P_BlockLinesIterator, then make one or more calls +// to it. +// +doom_boolean P_BlockLinesIterator(int x, int y, doom_boolean(*func)(line_t*)) +{ + int offset; + short* list; + line_t* ld; + + if (x < 0 + || y < 0 + || x >= bmapwidth + || y >= bmapheight) + { + return true; + } + + offset = y * bmapwidth + x; + + offset = *(blockmap + offset); + + for (list = blockmaplump + offset; *list != -1; list++) + { + ld = &lines[*list]; + + if (ld->validcount == validcount) + continue; // line has already been checked + + ld->validcount = validcount; + + if (!func(ld)) + return false; + } + return true; // everything was checked +} + + +// +// P_BlockThingsIterator +// +doom_boolean P_BlockThingsIterator(int x, int y, doom_boolean(*func)(mobj_t*)) +{ + mobj_t* mobj; + + if (x < 0 + || y < 0 + || x >= bmapwidth + || y >= bmapheight) + { + return true; + } + + + for (mobj = blocklinks[y * bmapwidth + x]; + mobj; + mobj = mobj->bnext) + { + if (!func(mobj)) + return false; + } + return true; +} + + +// +// INTERCEPT ROUTINES +// + +// +// PIT_AddLineIntercepts. +// 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 sides of the trace. +// Returns true if earlyout and a solid line hit. +// +doom_boolean PIT_AddLineIntercepts(line_t* ld) +{ + int s1; + int s2; + fixed_t frac; + divline_t dl; + + // avoid precision problems with two routines + if (trace.dx > FRACUNIT * 16 + || trace.dy > FRACUNIT * 16 + || trace.dx < -FRACUNIT * 16 + || trace.dy < -FRACUNIT * 16) + { + s1 = P_PointOnDivlineSide(ld->v1->x, ld->v1->y, &trace); + s2 = P_PointOnDivlineSide(ld->v2->x, ld->v2->y, &trace); + } + else + { + s1 = P_PointOnLineSide(trace.x, trace.y, ld); + s2 = P_PointOnLineSide(trace.x + trace.dx, trace.y + trace.dy, ld); + } + + if (s1 == s2) + return true; // line isn't crossed + + // hit the line + P_MakeDivline(ld, &dl); + frac = P_InterceptVector(&trace, &dl); + + if (frac < 0) + return true; // behind source + + // try to early out the check + if (earlyout + && frac < FRACUNIT + && !ld->backsector) + { + return false; // stop checking + } + + intercept_p->frac = frac; + intercept_p->isaline = true; + intercept_p->d.line = ld; + intercept_p++; + + return true; // continue +} + + +// +// PIT_AddThingIntercepts +// +doom_boolean PIT_AddThingIntercepts(mobj_t* thing) +{ + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + int s1; + int s2; + + doom_boolean tracepositive; + + divline_t dl; + + fixed_t frac; + + tracepositive = (trace.dx ^ trace.dy) > 0; + + // 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; + } + + s1 = P_PointOnDivlineSide(x1, y1, &trace); + s2 = P_PointOnDivlineSide(x2, y2, &trace); + + if (s1 == s2) + return true; // line isn't crossed + + dl.x = x1; + dl.y = y1; + dl.dx = x2 - x1; + dl.dy = y2 - y1; + + frac = P_InterceptVector(&trace, &dl); + + if (frac < 0) + return true; // behind source + + intercept_p->frac = frac; + intercept_p->isaline = false; + intercept_p->d.thing = thing; + intercept_p++; + + return true; // keep going +} + + +// +// P_TraverseIntercepts +// Returns true if the traverser function returns true +// for all lines. +// +doom_boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac) +{ + int count; + fixed_t dist; + intercept_t* scan; + intercept_t* in; + + count = (int)(intercept_p - intercepts); + + in = 0; // shut up compiler warning + + while (count--) + { + dist = DOOM_MAXINT; + for (scan = intercepts; scan < intercept_p; scan++) + { + if (scan->frac < dist) + { + dist = scan->frac; + in = scan; + } + } + + if (dist > maxfrac) + return true; // checked everything in range + +#if 0 // UNUSED + { + // don't check these yet, there may be others inserted + in = scan = intercepts; + for (scan = intercepts; scan < intercept_p; scan++) + if (scan->frac > maxfrac) + *in++ = *scan; + intercept_p = in; + return false; + } +#endif + + if (!func(in)) + return false; // don't bother going farther + + in->frac = DOOM_MAXINT; + } + + return true; // everything was traversed +} + + +// +// P_PathTraverse +// 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. +// +doom_boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, doom_boolean(*trav) (intercept_t*)) +{ + fixed_t xt1; + fixed_t yt1; + fixed_t xt2; + fixed_t yt2; + + fixed_t xstep; + fixed_t ystep; + + fixed_t partial; + + fixed_t xintercept; + fixed_t yintercept; + + int mapx; + int mapy; + + int mapxstep; + int mapystep; + + int count; + + earlyout = flags & PT_EARLYOUT; + + validcount++; + intercept_p = intercepts; + + if (((x1 - bmaporgx) & (MAPBLOCKSIZE - 1)) == 0) + x1 += FRACUNIT; // don't side exactly on a line + + if (((y1 - bmaporgy) & (MAPBLOCKSIZE - 1)) == 0) + y1 += FRACUNIT; // don't side exactly on a line + + trace.x = x1; + trace.y = y1; + trace.dx = x2 - x1; + trace.dy = y2 - y1; + + x1 -= bmaporgx; + y1 -= bmaporgy; + xt1 = x1 >> MAPBLOCKSHIFT; + yt1 = y1 >> MAPBLOCKSHIFT; + + x2 -= bmaporgx; + y2 -= bmaporgy; + xt2 = x2 >> MAPBLOCKSHIFT; + yt2 = y2 >> MAPBLOCKSHIFT; + + if (xt2 > xt1) + { + mapxstep = 1; + partial = FRACUNIT - ((x1 >> MAPBTOFRAC) & (FRACUNIT - 1)); + ystep = FixedDiv(y2 - y1, doom_abs(x2 - x1)); + } + else if (xt2 < xt1) + { + mapxstep = -1; + partial = (x1 >> MAPBTOFRAC) & (FRACUNIT - 1); + ystep = FixedDiv(y2 - y1, doom_abs(x2 - x1)); + } + else + { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256 * FRACUNIT; + } + + yintercept = (y1 >> MAPBTOFRAC) + FixedMul(partial, ystep); + + + if (yt2 > yt1) + { + mapystep = 1; + partial = FRACUNIT - ((y1 >> MAPBTOFRAC) & (FRACUNIT - 1)); + xstep = FixedDiv(x2 - x1, doom_abs(y2 - y1)); + } + else if (yt2 < yt1) + { + mapystep = -1; + partial = (y1 >> MAPBTOFRAC) & (FRACUNIT - 1); + xstep = FixedDiv(x2 - x1, doom_abs(y2 - y1)); + } + else + { + mapystep = 0; + partial = FRACUNIT; + xstep = 256 * FRACUNIT; + } + xintercept = (x1 >> MAPBTOFRAC) + FixedMul(partial, xstep); + + // Step through map blocks. + // Count is present to prevent a round off error + // from skipping the break. + mapx = xt1; + mapy = yt1; + + for (count = 0; count < 64; count++) + { + if (flags & PT_ADDLINES) + { + if (!P_BlockLinesIterator(mapx, mapy, PIT_AddLineIntercepts)) + return false; // early out + } + + if (flags & PT_ADDTHINGS) + { + if (!P_BlockThingsIterator(mapx, mapy, PIT_AddThingIntercepts)) + return false; // early out + } + + if (mapx == xt2 + && mapy == yt2) + { + break; + } + + if ((yintercept >> FRACBITS) == mapy) + { + yintercept += ystep; + mapx += mapxstep; + } + else if ((xintercept >> FRACBITS) == mapx) + { + xintercept += xstep; + mapy += mapystep; + } + } + + // go through the sorted list + return P_TraverseIntercepts(trav, FRACUNIT); +} +#define STOPSPEED 0x1000 +#define FRICTION 0xe800 + + +// +// P_SetMobjState +// Returns true if the mobj is still present. +// +mapthing_t itemrespawnque[ITEMQUESIZE]; +int itemrespawntime[ITEMQUESIZE]; +int iquehead; +int iquetail; + + +extern fixed_t attackrange; + + +void G_PlayerReborn(int player); +void P_SpawnMapThing(mapthing_t* mthing); + + +doom_boolean P_SetMobjState(mobj_t* mobj, statenum_t state) +{ + state_t* st; + + do + { + if (state == S_NULL) + { + mobj->state = (state_t*)S_NULL; + P_RemoveMobj(mobj); + return false; + } + + st = &states[state]; + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + + // Modified handling. + // Call action functions when the state is set + if (st->action.acp1) + st->action.acp1(mobj); + + state = st->nextstate; + } while (!mobj->tics); + + return true; +} + + +// +// P_ExplodeMissile +// +void P_ExplodeMissile(mobj_t* mo) +{ + mo->momx = mo->momy = mo->momz = 0; + + P_SetMobjState(mo, mobjinfo[mo->type].deathstate); + + mo->tics -= P_Random() & 3; + + if (mo->tics < 1) + mo->tics = 1; + + mo->flags &= ~MF_MISSILE; + + if (mo->info->deathsound) + S_StartSound(mo, mo->info->deathsound); +} + + +// +// P_XYMovement +// +void P_XYMovement(mobj_t* mo) +{ + fixed_t ptryx; + fixed_t ptryy; + player_t* player; + fixed_t xmove; + fixed_t ymove; + + if (!mo->momx && !mo->momy) + { + if (mo->flags & MF_SKULLFLY) + { + // the skull slammed into something + mo->flags &= ~MF_SKULLFLY; + mo->momx = mo->momy = mo->momz = 0; + + P_SetMobjState(mo, mo->info->spawnstate); + } + return; + } + + player = mo->player; + + if (mo->momx > MAXMOVE) + mo->momx = MAXMOVE; + else if (mo->momx < -MAXMOVE) + mo->momx = -MAXMOVE; + + if (mo->momy > MAXMOVE) + mo->momy = MAXMOVE; + else if (mo->momy < -MAXMOVE) + mo->momy = -MAXMOVE; + + xmove = mo->momx; + ymove = mo->momy; + + do + { + if (xmove > MAXMOVE / 2 || ymove > MAXMOVE / 2) + { + ptryx = mo->x + xmove / 2; + ptryy = mo->y + ymove / 2; + xmove >>= 1; + ymove >>= 1; + } + else + { + ptryx = mo->x + xmove; + ptryy = mo->y + ymove; + xmove = ymove = 0; + } + + if (!P_TryMove(mo, ptryx, ptryy)) + { + // blocked move + if (mo->player) + { // try to slide along it + P_SlideMove(mo); + } + else if (mo->flags & MF_MISSILE) + { + // explode a missile + if (ceilingline && + ceilingline->backsector && + ceilingline->backsector->ceilingpic == skyflatnum) + { + // Hack to prevent missiles exploding + // against the sky. + // Does not handle sky floors. + P_RemoveMobj(mo); + return; + } + P_ExplodeMissile(mo); + } + else + mo->momx = mo->momy = 0; + } + } while (xmove || ymove); + + // slow down + if (player && player->cheats & CF_NOMOMENTUM) + { + // debug option for no sliding at all + mo->momx = mo->momy = 0; + return; + } + + if (mo->flags & (MF_MISSILE | MF_SKULLFLY)) + return; // no friction for missiles ever + + if (mo->z > mo->floorz) + return; // no friction when airborne + + if (mo->flags & MF_CORPSE) + { + // do not stop sliding + // if halfway off a step with some momentum + if (mo->momx > FRACUNIT / 4 + || mo->momx < -FRACUNIT / 4 + || mo->momy > FRACUNIT / 4 + || mo->momy < -FRACUNIT / 4) + { + if (mo->floorz != mo->subsector->sector->floorheight) + return; + } + } + + if (mo->momx > -STOPSPEED + && mo->momx < STOPSPEED + && mo->momy > -STOPSPEED + && mo->momy < STOPSPEED + && (!player + || (player->cmd.forwardmove == 0 + && player->cmd.sidemove == 0))) + { + // if in a walking frame, stop moving + if (player && (unsigned)((player->mo->state - states) - S_PLAY_RUN1) < 4) + P_SetMobjState(player->mo, S_PLAY); + + mo->momx = 0; + mo->momy = 0; + } + else + { + mo->momx = FixedMul(mo->momx, FRICTION); + mo->momy = FixedMul(mo->momy, FRICTION); + } +} + + +// +// P_ZMovement +// +void P_ZMovement(mobj_t* mo) +{ + fixed_t dist; + fixed_t delta; + + // check for smooth step up + if (mo->player && mo->z < mo->floorz) + { + mo->player->viewheight -= mo->floorz - mo->z; + + mo->player->deltaviewheight + = (VIEWHEIGHT - mo->player->viewheight) >> 3; + } + + // adjust height + mo->z += mo->momz; + + if (mo->flags & MF_FLOAT + && mo->target) + { + // float down towards target if too close + if (!(mo->flags & MF_SKULLFLY) + && !(mo->flags & MF_INFLOAT)) + { + dist = P_AproxDistance(mo->x - mo->target->x, + mo->y - mo->target->y); + + delta = (mo->target->z + (mo->height >> 1)) - mo->z; + + if (delta < 0 && dist < -(delta * 3)) + mo->z -= FLOATSPEED; + else if (delta > 0 && dist < (delta * 3)) + mo->z += FLOATSPEED; + } + + } + + // clip movement + if (mo->z <= mo->floorz) + { + // hit the floor + + // Note (id): + // somebody left this after the setting momz to 0, + // kinda useless there. + if (mo->flags & MF_SKULLFLY) + { + // the skull slammed into something + mo->momz = -mo->momz; + } + + if (mo->momz < 0) + { + if (mo->player + && mo->momz < -GRAVITY * 8) + { + // Squat down. + // Decrease viewheight for a moment + // after hitting the ground (hard), + // and utter appropriate sound. + mo->player->deltaviewheight = mo->momz >> 3; + S_StartSound(mo, sfx_oof); + } + mo->momz = 0; + } + mo->z = mo->floorz; + + if ((mo->flags & MF_MISSILE) + && !(mo->flags & MF_NOCLIP)) + { + P_ExplodeMissile(mo); + return; + } + } + else if (!(mo->flags & MF_NOGRAVITY)) + { + if (mo->momz == 0) + mo->momz = -GRAVITY * 2; + else + mo->momz -= GRAVITY; + } + + if (mo->z + mo->height > mo->ceilingz) + { + // hit the ceiling + if (mo->momz > 0) + mo->momz = 0; + { + mo->z = mo->ceilingz - mo->height; + } + + if (mo->flags & MF_SKULLFLY) + { // the skull slammed into something + mo->momz = -mo->momz; + } + + if ((mo->flags & MF_MISSILE) + && !(mo->flags & MF_NOCLIP)) + { + P_ExplodeMissile(mo); + return; + } + } +} + + +// +// P_NightmareRespawn +// +void P_NightmareRespawn(mobj_t* mobj) +{ + fixed_t x; + fixed_t y; + fixed_t z; + subsector_t* ss; + mobj_t* mo; + mapthing_t* mthing; + + x = mobj->spawnpoint.x << FRACBITS; + y = mobj->spawnpoint.y << FRACBITS; + + // somthing is occupying it's position? + if (!P_CheckPosition(mobj, x, y)) + return; // no respwan + + // spawn a teleport fog at old spot + // because of removal of the body? + mo = P_SpawnMobj(mobj->x, + mobj->y, + mobj->subsector->sector->floorheight, MT_TFOG); + // initiate teleport sound + S_StartSound(mo, sfx_telept); + + // spawn a teleport fog at the new spot + ss = R_PointInSubsector(x, y); + + mo = P_SpawnMobj(x, y, ss->sector->floorheight, MT_TFOG); + + S_StartSound(mo, sfx_telept); + + // spawn the new monster + mthing = &mobj->spawnpoint; + + // spawn it + if (mobj->info->flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + // inherit attributes from deceased one + mo = P_SpawnMobj(x, y, z, mobj->type); + mo->spawnpoint = mobj->spawnpoint; + mo->angle = ANG45 * (mthing->angle / 45); + + if (mthing->options & MTF_AMBUSH) + mo->flags |= MF_AMBUSH; + + mo->reactiontime = 18; + + // remove the old monster, + P_RemoveMobj(mobj); +} + + +// +// P_MobjThinker +// +void P_MobjThinker(mobj_t* mobj) +{ + // momentum movement + if (mobj->momx + || mobj->momy + || (mobj->flags & MF_SKULLFLY)) + { + P_XYMovement(mobj); + + // FIXME: decent NOP/0/Nil function pointer please. + if (mobj->thinker.function.acv == (actionf_v)(-1)) + return; // mobj was removed + } + if ((mobj->z != mobj->floorz) + || mobj->momz) + { + P_ZMovement(mobj); + + // FIXME: decent NOP/0/Nil function pointer please. + if (mobj->thinker.function.acv == (actionf_v)(-1)) + return; // mobj was removed + } + + + // cycle through states, + // calling action functions at transitions + if (mobj->tics != -1) + { + mobj->tics--; + + // you can cycle through multiple states in a tic + if (!mobj->tics) + if (!P_SetMobjState(mobj, mobj->state->nextstate)) + return; // freed itself + } + else + { + // check for nightmare respawn + if (!(mobj->flags & MF_COUNTKILL)) + return; + + if (!respawnmonsters) + return; + + mobj->movecount++; + + if (mobj->movecount < 12 * 35) + return; + + if (leveltime & 31) + return; + + if (P_Random() > 4) + return; + + P_NightmareRespawn(mobj); + } +} + + +// +// P_SpawnMobj +// +mobj_t* P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) +{ + mobj_t* mobj; + state_t* st; + mobjinfo_t* info; + + mobj = Z_Malloc(sizeof(*mobj), PU_LEVEL, 0); + doom_memset(mobj, 0, sizeof(*mobj)); + info = &mobjinfo[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 (gameskill != sk_nightmare) + mobj->reactiontime = info->reactiontime; + + mobj->lastlook = P_Random() % MAXPLAYERS; + // do not set the state with P_SetMobjState, + // because action routines can not be called yet + st = &states[info->spawnstate]; + + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + + // set subsector and/or block links + P_SetThingPosition(mobj); + + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + if (z == ONFLOORZ) + mobj->z = mobj->floorz; + else if (z == ONCEILINGZ) + mobj->z = mobj->ceilingz - mobj->info->height; + else + mobj->z = z; + + mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; + + P_AddThinker(&mobj->thinker); + + return mobj; +} + + +// +// P_RemoveMobj +// +void P_RemoveMobj(mobj_t* mobj) +{ + if ((mobj->flags & MF_SPECIAL) + && !(mobj->flags & MF_DROPPED) + && (mobj->type != MT_INV) + && (mobj->type != MT_INS)) + { + itemrespawnque[iquehead] = mobj->spawnpoint; + itemrespawntime[iquehead] = leveltime; + iquehead = (iquehead + 1) & (ITEMQUESIZE - 1); + + // lose one off the end? + if (iquehead == iquetail) + iquetail = (iquetail + 1) & (ITEMQUESIZE - 1); + } + + // unlink from sector and block lists + P_UnsetThingPosition(mobj); + + // stop any playing sound + S_StopSound(mobj); + + // free block + P_RemoveThinker((thinker_t*)mobj); +} + + +// +// P_RespawnSpecials +// +void P_RespawnSpecials(void) +{ + fixed_t x; + fixed_t y; + fixed_t z; + + subsector_t* ss; + mobj_t* mo; + mapthing_t* mthing; + + int i; + + // only respawn items in deathmatch + if (deathmatch != 2) + return; // + + // nothing left to respawn? + if (iquehead == iquetail) + return; + + // wait at least 30 seconds + if (leveltime - itemrespawntime[iquetail] < 30 * 35) + return; + + mthing = &itemrespawnque[iquetail]; + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + // spawn a teleport fog at the new spot + ss = R_PointInSubsector(x, y); + mo = P_SpawnMobj(x, y, ss->sector->floorheight, MT_IFOG); + S_StartSound(mo, sfx_itmbk); + + // find which type to spawn + for (i = 0; i < NUMMOBJTYPES; i++) + { + if (mthing->type == mobjinfo[i].doomednum) + break; + } + + // spawn it + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mo = P_SpawnMobj(x, y, z, i); + mo->spawnpoint = *mthing; + mo->angle = ANG45 * (mthing->angle / 45); + + // pull it from the que + iquetail = (iquetail + 1) & (ITEMQUESIZE - 1); +} + + +// +// P_SpawnPlayer +// Called when a player is spawned on the level. +// Most of the player structure stays unchanged +// between levels. +// +void P_SpawnPlayer(mapthing_t* mthing) +{ + player_t* p; + fixed_t x; + fixed_t y; + fixed_t z; + + mobj_t* mobj; + + int i; + + // not playing? + if (!playeringame[mthing->type - 1]) + return; + + p = &players[mthing->type - 1]; + + if (p->playerstate == PST_REBORN) + G_PlayerReborn(mthing->type - 1); + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + z = ONFLOORZ; + mobj = P_SpawnMobj(x, y, z, MT_PLAYER); + + // set color translations for player sprites + if (mthing->type > 1) + mobj->flags |= (mthing->type - 1) << MF_TRANSSHIFT; + + mobj->angle = ANG45 * (mthing->angle / 45); + mobj->player = p; + mobj->health = p->health; + + p->mo = mobj; + p->playerstate = PST_LIVE; + p->refire = 0; + p->message = 0; + p->damagecount = 0; + p->bonuscount = 0; + p->extralight = 0; + p->fixedcolormap = 0; + p->viewheight = VIEWHEIGHT; + + // setup gun psprite + P_SetupPsprites(p); + + // give all cards in death match mode + if (deathmatch) + for (i = 0; i < NUMCARDS; i++) + p->cards[i] = true; + + if (mthing->type - 1 == consoleplayer) + { + // wake up the status bar + ST_Start(); + // wake up the heads up text + HU_Start(); + } +} + + +// +// P_SpawnMapThing +// The fields of the mapthing should +// already be in host byte order. +// +void P_SpawnMapThing(mapthing_t* mthing) +{ + int i; + int bit; + mobj_t* mobj; + fixed_t x; + fixed_t y; + fixed_t z; + + // count deathmatch start positions + if (mthing->type == 11) + { + if (deathmatch_p < &deathmatchstarts[10]) + { + doom_memcpy(deathmatch_p, mthing, sizeof(*mthing)); + deathmatch_p++; + } + return; + } + + // check for players specially + if (mthing->type <= 4) + { + // save spots for respawning in network games + playerstarts[mthing->type - 1] = *mthing; + if (!deathmatch) + P_SpawnPlayer(mthing); + + return; + } + + // check for apropriate skill level + if (!netgame && (mthing->options & 16)) + return; + + if (gameskill == sk_baby) + bit = 1; + else if (gameskill == sk_nightmare) + bit = 4; + else + bit = 1 << (gameskill - 1); + + if (!(mthing->options & bit)) + return; + + // find which type to spawn + for (i = 0; i < NUMMOBJTYPES; i++) + if (mthing->type == mobjinfo[i].doomednum) + break; + + if (i == NUMMOBJTYPES) + { + //I_Error("Error: P_SpawnMapThing: Unknown type %i at (%i, %i)", + // mthing->type, + // mthing->x, mthing->y); + + + doom_strcpy(error_buf, "Error: P_SpawnMapThing: Unknown type "); + doom_concat(error_buf, doom_itoa(mthing->type, 10)); + doom_concat(error_buf, " at ("); + doom_concat(error_buf, doom_itoa(mthing->x, 10)); + doom_concat(error_buf, ", "); + doom_concat(error_buf, doom_itoa(mthing->y, 10)); + doom_concat(error_buf, ")"); + I_Error(error_buf); + } + + // don't spawn keycards and players in deathmatch + if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) + return; + + // don't spawn any monsters if -nomonsters + if (nomonsters + && (i == MT_SKULL + || (mobjinfo[i].flags & MF_COUNTKILL))) + { + return; + } + + // spawn it + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mobj = P_SpawnMobj(x, y, z, i); + mobj->spawnpoint = *mthing; + + if (mobj->tics > 0) + mobj->tics = 1 + (P_Random() % mobj->tics); + if (mobj->flags & MF_COUNTKILL) + totalkills++; + if (mobj->flags & MF_COUNTITEM) + totalitems++; + + mobj->angle = ANG45 * (mthing->angle / 45); + if (mthing->options & MTF_AMBUSH) + mobj->flags |= MF_AMBUSH; +} + + +// +// GAME SPAWN FUNCTIONS +// + +// +// P_SpawnPuff +// +void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z) +{ + mobj_t* th; + + z += ((P_Random() - P_Random()) << 10); + + th = P_SpawnMobj(x, y, z, MT_PUFF); + th->momz = FRACUNIT; + th->tics -= P_Random() & 3; + + if (th->tics < 1) + th->tics = 1; + + // don't make punches spark on the wall + if (attackrange == MELEERANGE) + P_SetMobjState(th, S_PUFF3); +} + + +// +// P_SpawnBlood +// +void P_SpawnBlood(fixed_t x, fixed_t y, fixed_t z, int damage) +{ + mobj_t* th; + + z += ((P_Random() - P_Random()) << 10); + th = P_SpawnMobj(x, y, z, MT_BLOOD); + th->momz = FRACUNIT * 2; + th->tics -= P_Random() & 3; + + if (th->tics < 1) + th->tics = 1; + + if (damage <= 12 && damage >= 9) + P_SetMobjState(th, S_BLOOD2); + else if (damage < 9) + P_SetMobjState(th, S_BLOOD3); +} + + +// +// P_CheckMissileSpawn +// Moves the missile forward a bit +// and possibly explodes it right there. +// +void P_CheckMissileSpawn(mobj_t* th) +{ + th->tics -= P_Random() & 3; + if (th->tics < 1) + th->tics = 1; + + // move a little forward so an angle can + // be computed if it immediately explodes + th->x += (th->momx >> 1); + th->y += (th->momy >> 1); + th->z += (th->momz >> 1); + + if (!P_TryMove(th, th->x, th->y)) + P_ExplodeMissile(th); +} + + +// +// P_SpawnMissile +// +mobj_t* P_SpawnMissile(mobj_t* source, mobj_t* dest, mobjtype_t type) +{ + mobj_t* th; + angle_t an; + int dist; + + th = P_SpawnMobj(source->x, + source->y, + source->z + 4 * 8 * FRACUNIT, type); + + if (th->info->seesound) + S_StartSound(th, th->info->seesound); + + th->target = source; // where it came from + an = R_PointToAngle2(source->x, source->y, dest->x, dest->y); + + // fuzzy player + if (dest->flags & MF_SHADOW) + an += (P_Random() - P_Random()) << 20; + + th->angle = an; + an >>= ANGLETOFINESHIFT; + th->momx = FixedMul(th->info->speed, finecosine[an]); + th->momy = FixedMul(th->info->speed, finesine[an]); + + dist = P_AproxDistance(dest->x - source->x, dest->y - source->y); + dist = dist / th->info->speed; + + if (dist < 1) + dist = 1; + + th->momz = (dest->z - source->z) / dist; + P_CheckMissileSpawn(th); + + return th; +} + + +// +// P_SpawnPlayerMissile +// Tries to aim at a nearby monster +// +void P_SpawnPlayerMissile(mobj_t* source, mobjtype_t type) +{ + mobj_t* th; + angle_t an; + + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t slope; + + // see which target is to be aimed at + an = source->angle; + slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an += 1 << 26; + slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an -= 2 << 26; + slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); + } + + if (!linetarget) + { + an = source->angle; + slope = 0; + } + } + + x = source->x; + y = source->y; + z = source->z + 4 * 8 * FRACUNIT; + + th = P_SpawnMobj(x, y, z, type); + + if (th->info->seesound) + S_StartSound(th, th->info->seesound); + + th->target = source; + th->angle = an; + th->momx = FixedMul(th->info->speed, + finecosine[an >> ANGLETOFINESHIFT]); + th->momy = FixedMul(th->info->speed, + finesine[an >> ANGLETOFINESHIFT]); + th->momz = FixedMul(th->info->speed, slope); + + P_CheckMissileSpawn(th); +} +plat_t* activeplats[MAXPLATS]; + + +// +// Move a plat up and down +// +void T_PlatRaise(plat_t* plat) +{ + result_e res; + + switch (plat->status) + { + case up: + res = T_MovePlane(plat->sector, + plat->speed, + plat->high, + plat->crush, 0, 1); + + if (plat->type == raiseAndChange + || plat->type == raiseToNearestAndChange) + { + if (!(leveltime & 7)) + S_StartSound((mobj_t*)&plat->sector->soundorg, + sfx_stnmov); + } + + if (res == crushed && (!plat->crush)) + { + plat->count = plat->wait; + plat->status = down; + S_StartSound((mobj_t*)&plat->sector->soundorg, + sfx_pstart); + } + else + { + if (res == pastdest) + { + plat->count = plat->wait; + plat->status = waiting; + S_StartSound((mobj_t*)&plat->sector->soundorg, + sfx_pstop); + + switch (plat->type) + { + case blazeDWUS: + case downWaitUpStay: + P_RemoveActivePlat(plat); + break; + + case raiseAndChange: + case raiseToNearestAndChange: + P_RemoveActivePlat(plat); + break; + + default: + break; + } + } + } + break; + + case down: + res = T_MovePlane(plat->sector, plat->speed, plat->low, false, 0, -1); + + if (res == pastdest) + { + plat->count = plat->wait; + plat->status = waiting; + S_StartSound((mobj_t*)&plat->sector->soundorg, sfx_pstop); + } + break; + + case waiting: + if (!--plat->count) + { + if (plat->sector->floorheight == plat->low) + plat->status = up; + else + plat->status = down; + S_StartSound((mobj_t*)&plat->sector->soundorg, sfx_pstart); + } + + case in_stasis: + break; + } +} + + +// +// Do Platforms +// "amount" is only used for SOME platforms. +// +int EV_DoPlat(line_t* line, plattype_e type, int amount) +{ + plat_t* plat; + int secnum; + int rtn; + sector_t* sec; + + secnum = -1; + rtn = 0; + + // Activate all plats that are in_stasis + switch (type) + { + case perpetualRaise: + P_ActivateInStasis(line->tag); + break; + default: + break; + } + + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + + if (sec->specialdata) + continue; + + // Find lowest & highest floors around sector + rtn = 1; + plat = Z_Malloc(sizeof(*plat), PU_LEVSPEC, 0); + P_AddThinker(&plat->thinker); + + plat->type = type; + plat->sector = sec; + plat->sector->specialdata = plat; + plat->thinker.function.acp1 = (actionf_p1)T_PlatRaise; + plat->crush = false; + plat->tag = line->tag; + + switch (type) + { + case raiseToNearestAndChange: + plat->speed = PLATSPEED / 2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = P_FindNextHighestFloor(sec, sec->floorheight); + plat->wait = 0; + plat->status = up; + // NO MORE DAMAGE, IF APPLICABLE + sec->special = 0; + + S_StartSound((mobj_t*)&sec->soundorg, sfx_stnmov); + break; + + case raiseAndChange: + plat->speed = PLATSPEED / 2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = sec->floorheight + amount * FRACUNIT; + plat->wait = 0; + plat->status = up; + + S_StartSound((mobj_t*)&sec->soundorg, sfx_stnmov); + break; + + case downWaitUpStay: + plat->speed = PLATSPEED * 4; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = 35 * PLATWAIT; + plat->status = down; + S_StartSound((mobj_t*)&sec->soundorg, sfx_pstart); + break; + + case blazeDWUS: + plat->speed = PLATSPEED * 8; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = 35 * PLATWAIT; + plat->status = down; + S_StartSound((mobj_t*)&sec->soundorg, sfx_pstart); + break; + + case perpetualRaise: + plat->speed = PLATSPEED; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = P_FindHighestFloorSurrounding(sec); + + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + + plat->wait = 35 * PLATWAIT; + plat->status = P_Random() & 1; + + S_StartSound((mobj_t*)&sec->soundorg, sfx_pstart); + break; + } + P_AddActivePlat(plat); + } + + return rtn; +} + +void P_ActivateInStasis(int tag) +{ + int i; + + for (i = 0; i < MAXPLATS; i++) + if (activeplats[i] + && (activeplats[i])->tag == tag + && (activeplats[i])->status == in_stasis) + { + (activeplats[i])->status = (activeplats[i])->oldstatus; + (activeplats[i])->thinker.function.acp1 = (actionf_p1)T_PlatRaise; + } +} + +void EV_StopPlat(line_t* line) +{ + int j; + + for (j = 0; j < MAXPLATS; j++) + if (activeplats[j] + && ((activeplats[j])->status != in_stasis) + && ((activeplats[j])->tag == line->tag)) + { + (activeplats[j])->oldstatus = (activeplats[j])->status; + (activeplats[j])->status = in_stasis; + (activeplats[j])->thinker.function.acv = (actionf_v)0; + } +} + +void P_AddActivePlat(plat_t* plat) +{ + int i; + + for (i = 0; i < MAXPLATS; i++) + if (activeplats[i] == 0) + { + activeplats[i] = plat; + return; + } + I_Error("Error: P_AddActivePlat: no more plats!"); +} + +void P_RemoveActivePlat(plat_t* plat) +{ + int i; + for (i = 0; i < MAXPLATS; i++) + if (plat == activeplats[i]) + { + (activeplats[i])->sector->specialdata = 0; + P_RemoveThinker(&(activeplats[i])->thinker); + activeplats[i] = 0; + + return; + } + I_Error("Error: P_RemoveActivePlat: can't find plat!"); +} +#define LOWERSPEED (FRACUNIT*6) +#define RAISESPEED (FRACUNIT*6) + +#define WEAPONBOTTOM (128*FRACUNIT) +#define WEAPONTOP (32*FRACUNIT) + +// plasma cells for a bfg attack +#define BFGCELLS 40 + + +fixed_t swingx; +fixed_t swingy; +fixed_t bulletslope; + + +// +// P_SetPsprite +// +void P_SetPsprite(player_t* player, int position, statenum_t stnum) +{ + pspdef_t* psp; + state_t* state; + + psp = &player->psprites[position]; + + do + { + if (!stnum) + { + // object removed itself + psp->state = 0; + break; + } + + state = &states[stnum]; + psp->state = state; + psp->tics = state->tics; // could be 0 + + if (state->misc1) + { + // coordinate set + psp->sx = state->misc1 << FRACBITS; + psp->sy = state->misc2 << FRACBITS; + } + + // Call action routine. + // Modified handling. + if (state->action.acp2) + { + state->action.acp2(player, psp); + if (!psp->state) + break; + } + + stnum = psp->state->nextstate; + + } while (!psp->tics); + // an initial state of 0 could cycle through +} + + +// +// P_BringUpWeapon +// Starts bringing the pending weapon up +// from the bottom of the screen. +// Uses player +// +void P_BringUpWeapon(player_t* player) +{ + statenum_t newstate; + + if (player->pendingweapon == wp_nochange) + player->pendingweapon = player->readyweapon; + + if (player->pendingweapon == wp_chainsaw) + S_StartSound(player->mo, sfx_sawup); + + newstate = weaponinfo[player->pendingweapon].upstate; + + player->pendingweapon = wp_nochange; + player->psprites[ps_weapon].sy = WEAPONBOTTOM; + + P_SetPsprite(player, ps_weapon, newstate); +} + + +// +// P_CheckAmmo +// Returns true if there is enough ammo to shoot. +// If not, selects the next weapon to use. +// +doom_boolean P_CheckAmmo(player_t* player) +{ + ammotype_t ammo; + int count; + + ammo = weaponinfo[player->readyweapon].ammo; + + // Minimal amount for one shot varies. + if (player->readyweapon == wp_bfg) + count = BFGCELLS; + else if (player->readyweapon == wp_supershotgun) + count = 2; // Double barrel. + else + count = 1; // Regular. + + // Some do not need ammunition anyway. + // Return if current ammunition sufficient. + if (ammo == am_noammo || player->ammo[ammo] >= count) + return true; + + // Out of ammo, pick a weapon to change to. + // Preferences are set here. + do + { + if (player->weaponowned[wp_plasma] + && player->ammo[am_cell] + && (gamemode != shareware)) + { + player->pendingweapon = wp_plasma; + } + else if (player->weaponowned[wp_supershotgun] + && player->ammo[am_shell] > 2 + && (gamemode == commercial)) + { + player->pendingweapon = wp_supershotgun; + } + else if (player->weaponowned[wp_chaingun] + && player->ammo[am_clip]) + { + player->pendingweapon = wp_chaingun; + } + else if (player->weaponowned[wp_shotgun] + && player->ammo[am_shell]) + { + player->pendingweapon = wp_shotgun; + } + else if (player->ammo[am_clip]) + { + player->pendingweapon = wp_pistol; + } + else if (player->weaponowned[wp_chainsaw]) + { + player->pendingweapon = wp_chainsaw; + } + else if (player->weaponowned[wp_missile] + && player->ammo[am_misl]) + { + player->pendingweapon = wp_missile; + } + else if (player->weaponowned[wp_bfg] + && player->ammo[am_cell] > 40 + && (gamemode != shareware)) + { + player->pendingweapon = wp_bfg; + } + else + { + // If everything fails. + player->pendingweapon = wp_fist; + } + + } while (player->pendingweapon == wp_nochange); + + // Now set appropriate weapon overlay. + P_SetPsprite(player, + ps_weapon, + weaponinfo[player->readyweapon].downstate); + + return false; +} + + +// +// P_FireWeapon. +// +void P_FireWeapon(player_t* player) +{ + statenum_t newstate; + + if (!P_CheckAmmo(player)) + return; + + P_SetMobjState(player->mo, S_PLAY_ATK1); + newstate = weaponinfo[player->readyweapon].atkstate; + P_SetPsprite(player, ps_weapon, newstate); + P_NoiseAlert(player->mo, player->mo); + + // [pd] Stop gun bobbing when shooting + pspdef_t* psp; + psp = &player->psprites[ps_weapon]; + psp->sx = FRACUNIT; + psp->sy = WEAPONTOP; +} + + +// +// P_DropWeapon +// Player died, so put the weapon away. +// +void P_DropWeapon(player_t* player) +{ + P_SetPsprite(player, + ps_weapon, + weaponinfo[player->readyweapon].downstate); +} + + +// +// A_WeaponReady +// The player can fire the weapon +// or change to another weapon at this time. +// Follows after getting weapon up, +// or after previous attack/fire sequence. +// +void A_WeaponReady(player_t* player, pspdef_t* psp) +{ + statenum_t newstate; + int angle; + + // get out of attack state + if (player->mo->state == &states[S_PLAY_ATK1] + || player->mo->state == &states[S_PLAY_ATK2]) + { + P_SetMobjState(player->mo, S_PLAY); + } + + if (player->readyweapon == wp_chainsaw + && psp->state == &states[S_SAW]) + { + S_StartSound(player->mo, sfx_sawidl); + } + + // check for change + // if player is dead, put the weapon away + if (player->pendingweapon != wp_nochange || !player->health) + { + // change weapon + // (pending weapon should allready be validated) + newstate = weaponinfo[player->readyweapon].downstate; + P_SetPsprite(player, ps_weapon, newstate); + return; + } + + // check for fire + // the missile launcher and bfg do not auto fire + if (player->cmd.buttons & BT_ATTACK) + { + if (!player->attackdown + || (player->readyweapon != wp_missile + && player->readyweapon != wp_bfg)) + { + player->attackdown = true; + P_FireWeapon(player); + return; + } + } + else + player->attackdown = false; + + // bob the weapon based on movement speed + angle = (128 * leveltime) & FINEMASK; + psp->sx = FRACUNIT + FixedMul(player->bob, finecosine[angle]); + angle &= FINEANGLES / 2 - 1; + psp->sy = WEAPONTOP + FixedMul(player->bob, finesine[angle]); +} + + +// +// A_ReFire +// The player can re-fire the weapon +// without lowering it entirely. +// +void A_ReFire(player_t* player, pspdef_t* psp) +{ + // check for fire + // (if a weaponchange is pending, let it go through instead) + if ((player->cmd.buttons & BT_ATTACK) + && player->pendingweapon == wp_nochange + && player->health) + { + player->refire++; + P_FireWeapon(player); + } + else + { + player->refire = 0; + P_CheckAmmo(player); + } +} + + +void A_CheckReload(player_t* player, pspdef_t* psp) +{ + P_CheckAmmo(player); +} + + +// +// A_Lower +// Lowers current weapon, +// and changes weapon at bottom. +// +void A_Lower(player_t* player, pspdef_t* psp) +{ + psp->sy += LOWERSPEED; + + // Is already down. + if (psp->sy < WEAPONBOTTOM) + return; + + // Player is dead. + if (player->playerstate == PST_DEAD) + { + psp->sy = WEAPONBOTTOM; + + // don't bring weapon back up + return; + } + + // The old weapon has been lowered off the screen, + // so change the weapon and start raising it + if (!player->health) + { + // Player is dead, so keep the weapon off screen. + P_SetPsprite(player, ps_weapon, S_NULL); + return; + } + + player->readyweapon = player->pendingweapon; + + P_BringUpWeapon(player); +} + + +// +// A_Raise +// +void A_Raise(player_t* player, pspdef_t* psp) +{ + statenum_t newstate; + + 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. + newstate = weaponinfo[player->readyweapon].readystate; + + P_SetPsprite(player, ps_weapon, newstate); +} + + +// +// A_GunFlash +// +void A_GunFlash(player_t* player, pspdef_t* psp) +{ + P_SetMobjState(player->mo, S_PLAY_ATK2); + P_SetPsprite(player, ps_flash, weaponinfo[player->readyweapon].flashstate); +} + + +// +// WEAPON ATTACKS +// + +// +// A_Punch +// +void A_Punch(player_t* player, pspdef_t* psp) +{ + angle_t angle; + int damage; + int slope; + + damage = (P_Random() % 10 + 1) << 1; + + if (player->powers[pw_strength]) + damage *= 10; + + angle = player->mo->angle; + angle += (P_Random() - P_Random()) << 18; + slope = P_AimLineAttack(player->mo, angle, MELEERANGE); + P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); + + // turn to face target + if (linetarget) + { + S_StartSound(player->mo, sfx_punch); + player->mo->angle = R_PointToAngle2(player->mo->x, + player->mo->y, + linetarget->x, + linetarget->y); + } +} + + +// +// A_Saw +// +void A_Saw(player_t* player, pspdef_t* psp) +{ + angle_t angle; + int damage; + int slope; + + damage = 2 * (P_Random() % 10 + 1); + angle = player->mo->angle; + angle += (P_Random() - P_Random()) << 18; + + // use meleerange + 1 se the puff doesn't skip the flash + slope = P_AimLineAttack(player->mo, angle, MELEERANGE + 1); + P_LineAttack(player->mo, angle, MELEERANGE + 1, slope, damage); + + if (!linetarget) + { + S_StartSound(player->mo, sfx_sawful); + return; + } + S_StartSound(player->mo, sfx_sawhit); + + // turn to face target + angle = R_PointToAngle2(player->mo->x, player->mo->y, + linetarget->x, linetarget->y); + if (angle - player->mo->angle > ANG180) + { + if (angle - player->mo->angle < -ANG90 / 20) + player->mo->angle = angle + ANG90 / 21; + else + player->mo->angle -= ANG90 / 20; + } + else + { + if (angle - player->mo->angle > ANG90 / 20) + player->mo->angle = angle - ANG90 / 21; + else + player->mo->angle += ANG90 / 20; + } + player->mo->flags |= MF_JUSTATTACKED; +} + + +// +// A_FireMissile +// +void A_FireMissile(player_t* player, pspdef_t* psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo]--; + P_SpawnPlayerMissile(player->mo, MT_ROCKET); +} + + +// +// A_FireBFG +// +void A_FireBFG(player_t* player, pspdef_t* psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS; + P_SpawnPlayerMissile(player->mo, MT_BFG); +} + + +// +// A_FirePlasma +// +void A_FirePlasma(player_t* player, pspdef_t* psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + P_SetPsprite(player, + ps_flash, + weaponinfo[player->readyweapon].flashstate + (P_Random() & 1)); + + P_SpawnPlayerMissile(player->mo, MT_PLASMA); +} + + +// +// P_BulletSlope +// Sets a slope so a near miss is at aproximately +// the height of the intended target +// +void P_BulletSlope(mobj_t* mo) +{ + angle_t an; + + // see which target is to be aimed at + an = mo->angle; + bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an += 1 << 26; + bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT); + if (!linetarget) + { + an -= 2 << 26; + bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT); + } + } +} + + +// +// P_GunShot +// +void P_GunShot(mobj_t* mo, doom_boolean accurate) +{ + angle_t angle; + int damage; + + damage = 5 * (P_Random() % 3 + 1); + angle = mo->angle; + + if (!accurate) + angle += (P_Random() - P_Random()) << 18; + + P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage); +} + + +// +// A_FirePistol +// +void A_FirePistol(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_pistol); + + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + P_SetPsprite(player, + ps_flash, + weaponinfo[player->readyweapon].flashstate); + + P_BulletSlope(player->mo); + P_GunShot(player->mo, !player->refire); +} + + +// +// A_FireShotgun +// +void A_FireShotgun(player_t* player, pspdef_t* psp) +{ + int i; + + S_StartSound(player->mo, sfx_shotgn); + P_SetMobjState(player->mo, S_PLAY_ATK2); + + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + P_SetPsprite(player, + ps_flash, + weaponinfo[player->readyweapon].flashstate); + + P_BulletSlope(player->mo); + + for (i = 0; i < 7; i++) + P_GunShot(player->mo, false); +} + + +// +// A_FireShotgun2 +// +void A_FireShotgun2(player_t* player, pspdef_t* psp) +{ + int i; + angle_t angle; + int damage; + + + S_StartSound(player->mo, sfx_dshtgn); + P_SetMobjState(player->mo, S_PLAY_ATK2); + + player->ammo[weaponinfo[player->readyweapon].ammo] -= 2; + + P_SetPsprite(player, + ps_flash, + weaponinfo[player->readyweapon].flashstate); + + P_BulletSlope(player->mo); + + for (i = 0; i < 20; i++) + { + damage = 5 * (P_Random() % 3 + 1); + angle = player->mo->angle; + angle += (P_Random() - P_Random()) << 19; + P_LineAttack(player->mo, + angle, + MISSILERANGE, + bulletslope + ((P_Random() - P_Random()) << 5), damage); + } +} + + +// +// A_FireCGun +// +void A_FireCGun(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_pistol); + + if (!player->ammo[weaponinfo[player->readyweapon].ammo]) + return; + + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + P_SetPsprite(player, + ps_flash, + weaponinfo[player->readyweapon].flashstate + + psp->state + - &states[S_CHAIN1]); + + P_BulletSlope(player->mo); + + P_GunShot(player->mo, !player->refire); +} + + +// +// ? +// +void A_Light0(player_t* player, pspdef_t* psp) +{ + player->extralight = 0; +} + +void A_Light1(player_t* player, pspdef_t* psp) +{ + player->extralight = 1; +} + +void A_Light2(player_t* player, pspdef_t* psp) +{ + player->extralight = 2; +} + + +// +// A_BFGSpray +// Spawn a BFG explosion on every monster in view +// +void A_BFGSpray(mobj_t* mo) +{ + int i; + int j; + int damage; + angle_t an; + + // offset angles from its attack angle + for (i = 0; i < 40; i++) + { + an = mo->angle - ANG90 / 2 + ANG90 / 40 * i; + + // mo->target is the originator (player) + // of the missile + P_AimLineAttack(mo->target, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + continue; + + P_SpawnMobj(linetarget->x, + linetarget->y, + linetarget->z + (linetarget->height >> 2), + MT_EXTRABFG); + + damage = 0; + for (j = 0; j < 15; j++) + damage += (P_Random() & 7) + 1; + + P_DamageMobj(linetarget, mo->target, mo->target, damage); + } +} + + +// +// A_BFGsound +// +void A_BFGsound(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_bfg); +} + + +// +// P_SetupPsprites +// Called at start of level for each player. +// +void P_SetupPsprites(player_t* player) +{ + int i; + + // remove all psprites + for (i = 0; i < NUMPSPRITES; i++) + player->psprites[i].state = 0; + + // spawn the gun + player->pendingweapon = player->readyweapon; + P_BringUpWeapon(player); +} + + +// +// P_MovePsprites +// Called every tic by player thinking routine. +// +void P_MovePsprites(player_t* player) +{ + int i; + pspdef_t* psp; + state_t* state; + + psp = &player->psprites[0]; + for (i = 0; i < NUMPSPRITES; i++, psp++) + { + // a null state means not active + if ((state = psp->state)) + { + // drop tic count and possibly change state + + // a -1 tic count never changes + if (psp->tics != -1) + { + psp->tics--; + if (!psp->tics) + P_SetPsprite(player, i, psp->state->nextstate); + } + } + } + + player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; + player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; +} +#define PADSAVEP() save_p += (4 - ((long long)save_p & 3)) & 3 + + +byte* save_p; + + +// TODO: [pd] We are loading/saving raw pointers. It will not work with saves from 32bits system. We need to rewrite those functions. +// +// P_ArchivePlayers +// +void P_ArchivePlayers(void) +{ + int i; + int j; + player_t* dest; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + PADSAVEP(); + + dest = (player_t*)save_p; + doom_memcpy(dest, &players[i], sizeof(player_t)); + save_p += sizeof(player_t); + for (j = 0; j < NUMPSPRITES; j++) + { + if (dest->psprites[j].state) + { + dest->psprites[j].state + = (state_t*)(dest->psprites[j].state - states); + } + } + } +} + + +// +// P_UnArchivePlayers +// +void P_UnArchivePlayers(void) +{ + int i; + int j; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + PADSAVEP(); + + doom_memcpy(&players[i], save_p, sizeof(player_t)); + save_p += sizeof(player_t); + + // will be set when unarc thinker + players[i].mo = 0; + players[i].message = 0; + players[i].attacker = 0; + + for (j = 0; j < NUMPSPRITES; j++) + { + if (players[i].psprites[j].state) + { + players[i].psprites[j].state + = &states[(long long)players[i].psprites[j].state]; + } + } + } +} + + +// +// P_ArchiveWorld +// +void P_ArchiveWorld(void) +{ + int i; + int j; + sector_t* sec; + line_t* li; + side_t* si; + short* put; + + put = (short*)save_p; + + // do sectors + for (i = 0, sec = sectors; i < numsectors; i++, sec++) + { + *put++ = sec->floorheight >> FRACBITS; + *put++ = sec->ceilingheight >> FRACBITS; + *put++ = sec->floorpic; + *put++ = sec->ceilingpic; + *put++ = sec->lightlevel; + *put++ = sec->special; // needed? + *put++ = sec->tag; // needed? + } + + + // do lines + for (i = 0, li = lines; i < numlines; i++, li++) + { + *put++ = li->flags; + *put++ = li->special; + *put++ = li->tag; + for (j = 0; j < 2; j++) + { + if (li->sidenum[j] == -1) + continue; + + si = &sides[li->sidenum[j]]; + + *put++ = si->textureoffset >> FRACBITS; + *put++ = si->rowoffset >> FRACBITS; + *put++ = si->toptexture; + *put++ = si->bottomtexture; + *put++ = si->midtexture; + } + } + + save_p = (byte*)put; +} + + +// +// P_UnArchiveWorld +// +void P_UnArchiveWorld(void) +{ + int i; + int j; + sector_t* sec; + line_t* li; + side_t* si; + short* get; + + get = (short*)save_p; + + // do sectors + for (i = 0, sec = sectors; i < numsectors; i++, sec++) + { + sec->floorheight = *get++ << FRACBITS; + sec->ceilingheight = *get++ << FRACBITS; + sec->floorpic = *get++; + sec->ceilingpic = *get++; + sec->lightlevel = *get++; + sec->special = *get++; // needed? + sec->tag = *get++; // needed? + sec->specialdata = 0; + sec->soundtarget = 0; + } + + // do lines + for (i = 0, li = lines; i < numlines; i++, li++) + { + li->flags = *get++; + li->special = *get++; + li->tag = *get++; + for (j = 0; j < 2; j++) + { + if (li->sidenum[j] == -1) + continue; + si = &sides[li->sidenum[j]]; + si->textureoffset = *get++ << FRACBITS; + si->rowoffset = *get++ << FRACBITS; + si->toptexture = *get++; + si->bottomtexture = *get++; + si->midtexture = *get++; + } + } + + save_p = (byte*)get; +} + + +// +// Thinkers +// +typedef enum +{ + tc_end, + tc_mobj +} thinkerclass_t; + + +// +// P_ArchiveThinkers +// +void P_ArchiveThinkers(void) +{ + thinker_t* th; + mobj_t* mobj; + + // save off the current thinkers + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_MobjThinker) + { + *save_p++ = tc_mobj; + PADSAVEP(); + mobj = (mobj_t*)save_p; + doom_memcpy(mobj, th, sizeof(*mobj)); + save_p += sizeof(*mobj); + mobj->state = (state_t*)(mobj->state - states); + + if (mobj->player) + mobj->player = (player_t*)((mobj->player - players) + 1); + continue; + } + + // I_Error ("P_ArchiveThinkers: Unknown thinker function"); + } + + // add a terminating marker + *save_p++ = tc_end; +} + + +// +// P_UnArchiveThinkers +// +void P_UnArchiveThinkers(void) +{ + byte tclass; + thinker_t* currentthinker; + thinker_t* next; + mobj_t* mobj; + + // remove all the current thinkers + currentthinker = thinkercap.next; + while (currentthinker != &thinkercap) + { + next = currentthinker->next; + + if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) + P_RemoveMobj((mobj_t*)currentthinker); + else + Z_Free(currentthinker); + + currentthinker = next; + } + P_InitThinkers(); + + // read in saved thinkers + while (1) + { + tclass = *save_p++; + switch (tclass) + { + case tc_end: + return; // end of list + + case tc_mobj: + PADSAVEP(); + mobj = Z_Malloc(sizeof(*mobj), PU_LEVEL, 0); + doom_memcpy(mobj, save_p, sizeof(*mobj)); + save_p += sizeof(*mobj); + mobj->state = &states[(long long)mobj->state]; + mobj->target = 0; + if (mobj->player) + { + mobj->player = &players[(long long)mobj->player - 1]; + mobj->player->mo = mobj; + } + P_SetThingPosition(mobj); + mobj->info = &mobjinfo[mobj->type]; + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; + P_AddThinker(&mobj->thinker); + break; + + default: + { + //I_Error("Error: Unknown tclass %i in savegame", tclass); + + doom_strcpy(error_buf, "Error: Unknown tclass "); + doom_concat(error_buf, doom_itoa(tclass, 10)); + doom_concat(error_buf, " in savegame"); + I_Error(error_buf); + } + } + } +} + + +// +// P_ArchiveSpecials +// +enum +{ + tc_ceiling, + tc_door, + tc_floor, + tc_plat, + tc_flash, + tc_strobe, + tc_glow, + tc_endspecials +} specials_e; + + +// +// Things to handle: +// +// T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list +// T_VerticalDoor, (vldoor_t: sector_t * swizzle), +// T_MoveFloor, (floormove_t: sector_t * swizzle), +// T_LightFlash, (lightflash_t: sector_t * swizzle), +// T_StrobeFlash, (strobe_t: sector_t *), +// T_Glow, (glow_t: sector_t *), +// T_PlatRaise, (plat_t: sector_t *), - active list +// +void P_ArchiveSpecials(void) +{ + thinker_t* th; + ceiling_t* ceiling; + vldoor_t* door; + floormove_t* floor; + plat_t* plat; + lightflash_t* flash; + strobe_t* strobe; + glow_t* glow; + int i; + + // save off the current thinkers + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acv == (actionf_v)0) + { + for (i = 0; i < MAXCEILINGS; i++) + if (activeceilings[i] == (ceiling_t*)th) + break; + + if (i < MAXCEILINGS) + { + *save_p++ = tc_ceiling; + PADSAVEP(); + ceiling = (ceiling_t*)save_p; + doom_memcpy(ceiling, th, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = (sector_t*)(ceiling->sector - sectors); + } + continue; + } + + if (th->function.acp1 == (actionf_p1)T_MoveCeiling) + { + *save_p++ = tc_ceiling; + PADSAVEP(); + ceiling = (ceiling_t*)save_p; + doom_memcpy(ceiling, th, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = (sector_t*)(ceiling->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_VerticalDoor) + { + *save_p++ = tc_door; + PADSAVEP(); + door = (vldoor_t*)save_p; + doom_memcpy(door, th, sizeof(*door)); + save_p += sizeof(*door); + door->sector = (sector_t*)(door->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_MoveFloor) + { + *save_p++ = tc_floor; + PADSAVEP(); + floor = (floormove_t*)save_p; + doom_memcpy(floor, th, sizeof(*floor)); + save_p += sizeof(*floor); + floor->sector = (sector_t*)(floor->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_PlatRaise) + { + *save_p++ = tc_plat; + PADSAVEP(); + plat = (plat_t*)save_p; + doom_memcpy(plat, th, sizeof(*plat)); + save_p += sizeof(*plat); + plat->sector = (sector_t*)(plat->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_LightFlash) + { + *save_p++ = tc_flash; + PADSAVEP(); + flash = (lightflash_t*)save_p; + doom_memcpy(flash, th, sizeof(*flash)); + save_p += sizeof(*flash); + flash->sector = (sector_t*)(flash->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_StrobeFlash) + { + *save_p++ = tc_strobe; + PADSAVEP(); + strobe = (strobe_t*)save_p; + doom_memcpy(strobe, th, sizeof(*strobe)); + save_p += sizeof(*strobe); + strobe->sector = (sector_t*)(strobe->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_Glow) + { + *save_p++ = tc_glow; + PADSAVEP(); + glow = (glow_t*)save_p; + doom_memcpy(glow, th, sizeof(*glow)); + save_p += sizeof(*glow); + glow->sector = (sector_t*)(glow->sector - sectors); + continue; + } + } + + // add a terminating marker + *save_p++ = tc_endspecials; +} + + +// +// P_UnArchiveSpecials +// +void P_UnArchiveSpecials(void) +{ + byte tclass; + ceiling_t* ceiling; + vldoor_t* door; + floormove_t* floor; + plat_t* plat; + lightflash_t* flash; + strobe_t* strobe; + glow_t* glow; + + + // read in saved thinkers + while (1) + { + tclass = *save_p++; + switch (tclass) + { + case tc_endspecials: + return; // end of list + + case tc_ceiling: + PADSAVEP(); + ceiling = Z_Malloc(sizeof(*ceiling), PU_LEVEL, 0); + doom_memcpy(ceiling, save_p, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = §ors[(long long)ceiling->sector]; + ceiling->sector->specialdata = ceiling; + + if (ceiling->thinker.function.acp1) + ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; + + P_AddThinker(&ceiling->thinker); + P_AddActiveCeiling(ceiling); + break; + + case tc_door: + PADSAVEP(); + door = Z_Malloc(sizeof(*door), PU_LEVEL, 0); + doom_memcpy(door, save_p, sizeof(*door)); + save_p += sizeof(*door); + door->sector = §ors[(long long)door->sector]; + door->sector->specialdata = door; + door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; + P_AddThinker(&door->thinker); + break; + + case tc_floor: + PADSAVEP(); + floor = Z_Malloc(sizeof(*floor), PU_LEVEL, 0); + doom_memcpy(floor, save_p, sizeof(*floor)); + save_p += sizeof(*floor); + floor->sector = §ors[(long long)floor->sector]; + floor->sector->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + P_AddThinker(&floor->thinker); + break; + + case tc_plat: + PADSAVEP(); + plat = Z_Malloc(sizeof(*plat), PU_LEVEL, 0); + doom_memcpy(plat, save_p, sizeof(*plat)); + save_p += sizeof(*plat); + plat->sector = §ors[(long long)plat->sector]; + plat->sector->specialdata = plat; + + if (plat->thinker.function.acp1) + plat->thinker.function.acp1 = (actionf_p1)T_PlatRaise; + + P_AddThinker(&plat->thinker); + P_AddActivePlat(plat); + break; + + case tc_flash: + PADSAVEP(); + flash = Z_Malloc(sizeof(*flash), PU_LEVEL, 0); + doom_memcpy(flash, save_p, sizeof(*flash)); + save_p += sizeof(*flash); + flash->sector = §ors[(long long)flash->sector]; + flash->thinker.function.acp1 = (actionf_p1)T_LightFlash; + P_AddThinker(&flash->thinker); + break; + + case tc_strobe: + PADSAVEP(); + strobe = Z_Malloc(sizeof(*strobe), PU_LEVEL, 0); + doom_memcpy(strobe, save_p, sizeof(*strobe)); + save_p += sizeof(*strobe); + strobe->sector = §ors[(long long)strobe->sector]; + strobe->thinker.function.acp1 = (actionf_p1)T_StrobeFlash; + P_AddThinker(&strobe->thinker); + break; + + case tc_glow: + PADSAVEP(); + glow = Z_Malloc(sizeof(*glow), PU_LEVEL, 0); + doom_memcpy(glow, save_p, sizeof(*glow)); + save_p += sizeof(*glow); + glow->sector = §ors[(long long)glow->sector]; + glow->thinker.function.acp1 = (actionf_p1)T_Glow; + P_AddThinker(&glow->thinker); + break; + + default: + { + //I_Error("Error: P_UnarchiveSpecials:Unknown tclass %i " + // "in savegame", tclass); + + doom_strcpy(error_buf, "Error: P_UnarchiveSpecials:Unknown tclass "); + doom_concat(error_buf, doom_itoa(tclass, 10)); + doom_concat(error_buf, " in savegame"); + I_Error(error_buf); + } + } + } +} +#define MAX_DEATHMATCH_STARTS 10 + + +// +// MAP related Lookup tables. +// Store VERTEXES, LINEDEFS, SIDEDEFS, etc. +// +int numvertexes; +vertex_t* vertexes; + +int numsegs; +seg_t* segs; + +int numsectors; +sector_t* sectors; + +int numsubsectors; +subsector_t* subsectors; + +int numnodes; +node_t* nodes; + +int numlines; +line_t* lines; + +int numsides; +side_t* sides; + +// BLOCKMAP +// Created from axis aligned bounding box +// of the map, a rectangular array of +// blocks of size ... +// Used to speed up collision detection +// by spatial subdivision in 2D. +// +// Blockmap size. +int bmapwidth; +int bmapheight; // size in mapblocks +short* blockmap; // int for larger maps +// offsets in blockmap are from here +short* blockmaplump; +// origin of block map +fixed_t bmaporgx; +fixed_t bmaporgy; +// for thing chains +mobj_t** blocklinks; + +// REJECT +// For fast sight rejection. +// Speeds up enemy AI by skipping detailed +// LineOf Sight calculation. +// Without special effect, this could be +// used as a PVS lookup as well. +// +byte* rejectmatrix; + +mapthing_t deathmatchstarts[MAX_DEATHMATCH_STARTS]; +mapthing_t* deathmatch_p; +mapthing_t playerstarts[MAXPLAYERS]; + + +void P_SpawnMapThing(mapthing_t* mthing); + + +// +// P_LoadVertexes +// +void P_LoadVertexes(int lump) +{ + byte* data; + int i; + mapvertex_t* ml; + vertex_t* li; + + // Determine number of lumps: + // total lump length / vertex record length. + numvertexes = W_LumpLength(lump) / sizeof(mapvertex_t); + + // Allocate zone memory for buffer. + vertexes = Z_Malloc(numvertexes * sizeof(vertex_t), PU_LEVEL, 0); + + // Load data into cache. + data = W_CacheLumpNum(lump, PU_STATIC); + + ml = (mapvertex_t*)data; + li = vertexes; + + // Copy and convert vertex coordinates, + // internal representation as fixed. + for (i = 0; i < numvertexes; i++, li++, ml++) + { + li->x = SHORT(ml->x) << FRACBITS; + li->y = SHORT(ml->y) << FRACBITS; + } + + // Free buffer memory. + Z_Free(data); +} + + +// +// P_LoadSegs +// +void P_LoadSegs(int lump) +{ + byte* data; + int i; + mapseg_t* ml; + seg_t* li; + line_t* ldef; + int linedef; + int side; + + numsegs = W_LumpLength(lump) / sizeof(mapseg_t); + segs = Z_Malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0); + doom_memset(segs, 0, numsegs * sizeof(seg_t)); + data = W_CacheLumpNum(lump, PU_STATIC); + + ml = (mapseg_t*)data; + li = segs; + for (i = 0; i < numsegs; i++, li++, ml++) + { + li->v1 = &vertexes[SHORT(ml->v1)]; + li->v2 = &vertexes[SHORT(ml->v2)]; + + li->angle = (SHORT(ml->angle)) << 16; + li->offset = (SHORT(ml->offset)) << 16; + linedef = SHORT(ml->linedef); + ldef = &lines[linedef]; + li->linedef = ldef; + side = SHORT(ml->side); + li->sidedef = &sides[ldef->sidenum[side]]; + li->frontsector = sides[ldef->sidenum[side]].sector; + if (ldef->flags & ML_TWOSIDED) + li->backsector = sides[ldef->sidenum[side ^ 1]].sector; + else + li->backsector = 0; + } + + Z_Free(data); +} + + +// +// P_LoadSubsectors +// +void P_LoadSubsectors(int lump) +{ + byte* data; + int i; + mapsubsector_t* ms; + subsector_t* ss; + + numsubsectors = W_LumpLength(lump) / sizeof(mapsubsector_t); + subsectors = Z_Malloc(numsubsectors * sizeof(subsector_t), PU_LEVEL, 0); + data = W_CacheLumpNum(lump, PU_STATIC); + + ms = (mapsubsector_t*)data; + doom_memset(subsectors, 0, numsubsectors * sizeof(subsector_t)); + ss = subsectors; + + for (i = 0; i < numsubsectors; i++, ss++, ms++) + { + ss->numlines = SHORT(ms->numsegs); + ss->firstline = SHORT(ms->firstseg); + } + + Z_Free(data); +} + + +// +// P_LoadSectors +// +void P_LoadSectors(int lump) +{ + byte* data; + int i; + mapsector_t* ms; + sector_t* ss; + + numsectors = W_LumpLength(lump) / sizeof(mapsector_t); + sectors = Z_Malloc(numsectors * sizeof(sector_t), PU_LEVEL, 0); + doom_memset(sectors, 0, numsectors * sizeof(sector_t)); + data = W_CacheLumpNum(lump, PU_STATIC); + + ms = (mapsector_t*)data; + ss = sectors; + for (i = 0; i < numsectors; i++, ss++, ms++) + { + ss->floorheight = SHORT(ms->floorheight) << FRACBITS; + ss->ceilingheight = SHORT(ms->ceilingheight) << FRACBITS; + ss->floorpic = R_FlatNumForName(ms->floorpic); + ss->ceilingpic = R_FlatNumForName(ms->ceilingpic); + ss->lightlevel = SHORT(ms->lightlevel); + ss->special = SHORT(ms->special); + ss->tag = SHORT(ms->tag); + ss->thinglist = 0; + } + + Z_Free(data); +} + + +// +// P_LoadNodes +// +void P_LoadNodes(int lump) +{ + byte* data; + int i; + int j; + int k; + mapnode_t* mn; + node_t* no; + + numnodes = W_LumpLength(lump) / sizeof(mapnode_t); + nodes = Z_Malloc(numnodes * sizeof(node_t), PU_LEVEL, 0); + data = W_CacheLumpNum(lump, PU_STATIC); + + mn = (mapnode_t*)data; + no = nodes; + + for (i = 0; i < numnodes; i++, no++, mn++) + { + no->x = SHORT(mn->x) << FRACBITS; + no->y = SHORT(mn->y) << FRACBITS; + no->dx = SHORT(mn->dx) << FRACBITS; + no->dy = SHORT(mn->dy) << FRACBITS; + for (j = 0; j < 2; j++) + { + no->children[j] = SHORT(mn->children[j]); + for (k = 0; k < 4; k++) + no->bbox[j][k] = SHORT(mn->bbox[j][k]) << FRACBITS; + } + } + + Z_Free(data); +} + + +// +// P_LoadThings +// +void P_LoadThings(int lump) +{ + byte* data; + int i; + mapthing_t* mt; + int numthings; + doom_boolean spawn; + + data = W_CacheLumpNum(lump, PU_STATIC); + numthings = W_LumpLength(lump) / sizeof(mapthing_t); + + mt = (mapthing_t*)data; + for (i = 0; i < numthings; i++, mt++) + { + spawn = true; + + // Do not spawn cool, new monsters if !commercial + if (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 == false) + break; + + // Do spawn all other stuff. + mt->x = SHORT(mt->x); + mt->y = SHORT(mt->y); + mt->angle = SHORT(mt->angle); + mt->type = SHORT(mt->type); + mt->options = SHORT(mt->options); + + P_SpawnMapThing(mt); + } + + Z_Free(data); +} + + +// +// P_LoadLineDefs +// Also counts secret lines for intermissions. +// +void P_LoadLineDefs(int lump) +{ + byte* data; + int i; + maplinedef_t* mld; + line_t* ld; + vertex_t* v1; + vertex_t* v2; + + numlines = W_LumpLength(lump) / sizeof(maplinedef_t); + lines = Z_Malloc(numlines * sizeof(line_t), PU_LEVEL, 0); + doom_memset(lines, 0, numlines * sizeof(line_t)); + data = W_CacheLumpNum(lump, PU_STATIC); + + mld = (maplinedef_t*)data; + ld = lines; + for (i = 0; i < numlines; i++, mld++, ld++) + { + ld->flags = SHORT(mld->flags); + ld->special = SHORT(mld->special); + ld->tag = SHORT(mld->tag); + v1 = ld->v1 = &vertexes[SHORT(mld->v1)]; + v2 = ld->v2 = &vertexes[SHORT(mld->v2)]; + ld->dx = v2->x - v1->x; + ld->dy = v2->y - v1->y; + + if (!ld->dx) + ld->slopetype = ST_VERTICAL; + else if (!ld->dy) + ld->slopetype = ST_HORIZONTAL; + else + { + if (FixedDiv(ld->dy, ld->dx) > 0) + ld->slopetype = ST_POSITIVE; + else + ld->slopetype = ST_NEGATIVE; + } + + if (v1->x < v2->x) + { + ld->bbox[BOXLEFT] = v1->x; + ld->bbox[BOXRIGHT] = v2->x; + } + else + { + ld->bbox[BOXLEFT] = v2->x; + ld->bbox[BOXRIGHT] = v1->x; + } + + if (v1->y < v2->y) + { + ld->bbox[BOXBOTTOM] = v1->y; + ld->bbox[BOXTOP] = v2->y; + } + else + { + ld->bbox[BOXBOTTOM] = v2->y; + ld->bbox[BOXTOP] = v1->y; + } + + ld->sidenum[0] = SHORT(mld->sidenum[0]); + ld->sidenum[1] = SHORT(mld->sidenum[1]); + + if (ld->sidenum[0] != -1) + ld->frontsector = sides[ld->sidenum[0]].sector; + else + ld->frontsector = 0; + + if (ld->sidenum[1] != -1) + ld->backsector = sides[ld->sidenum[1]].sector; + else + ld->backsector = 0; + } + + Z_Free(data); +} + + +// +// P_LoadSideDefs +// +void P_LoadSideDefs(int lump) +{ + byte* data; + int i; + mapsidedef_t* msd; + side_t* sd; + + numsides = W_LumpLength(lump) / sizeof(mapsidedef_t); + sides = Z_Malloc(numsides * sizeof(side_t), PU_LEVEL, 0); + doom_memset(sides, 0, numsides * sizeof(side_t)); + data = W_CacheLumpNum(lump, PU_STATIC); + + msd = (mapsidedef_t*)data; + sd = sides; + for (i = 0; i < numsides; i++, msd++, sd++) + { + sd->textureoffset = SHORT(msd->textureoffset) << FRACBITS; + sd->rowoffset = SHORT(msd->rowoffset) << FRACBITS; + sd->toptexture = R_TextureNumForName(msd->toptexture); + sd->bottomtexture = R_TextureNumForName(msd->bottomtexture); + sd->midtexture = R_TextureNumForName(msd->midtexture); + sd->sector = §ors[SHORT(msd->sector)]; + } + + Z_Free(data); +} + + +// +// P_LoadBlockMap +// +void P_LoadBlockMap(int lump) +{ + int i; + int count; + + blockmaplump = W_CacheLumpNum(lump, PU_LEVEL); + blockmap = blockmaplump + 4; + count = W_LumpLength(lump) / 2; + + for (i = 0; i < count; i++) + blockmaplump[i] = SHORT(blockmaplump[i]); + + bmaporgx = blockmaplump[0] << FRACBITS; + bmaporgy = blockmaplump[1] << FRACBITS; + bmapwidth = blockmaplump[2]; + bmapheight = blockmaplump[3]; + + // clear out mobj chains + count = sizeof(*blocklinks) * bmapwidth * bmapheight; + blocklinks = Z_Malloc(count, PU_LEVEL, 0); + doom_memset(blocklinks, 0, count); +} + + +// +// P_GroupLines +// Builds sector line lists and subsector sector numbers. +// Finds block bounding boxes for sectors. +// +void P_GroupLines(void) +{ + line_t** linebuffer; + int i; + int j; + int total; + line_t* li; + sector_t* sector; + subsector_t* ss; + seg_t* seg; + fixed_t bbox[4]; + int block; + + // look up sector number for each subsector + ss = subsectors; + for (i = 0; i < numsubsectors; i++, ss++) + { + seg = &segs[ss->firstline]; + ss->sector = seg->sidedef->sector; + } + + // count number of lines in each sector + li = lines; + total = 0; + for (i = 0; i < numlines; i++, li++) + { + total++; + li->frontsector->linecount++; + + if (li->backsector && li->backsector != li->frontsector) + { + li->backsector->linecount++; + total++; + } + } + + // build line tables for each sector + linebuffer = Z_Malloc(total * sizeof(line_t*), PU_LEVEL, 0); + sector = sectors; + for (i = 0; i < numsectors; i++, sector++) + { + M_ClearBox(bbox); + sector->lines = linebuffer; + li = lines; + for (j = 0; j < numlines; j++, li++) + { + if (li->frontsector == sector || li->backsector == sector) + { + *linebuffer++ = li; + M_AddToBox(bbox, li->v1->x, li->v1->y); + M_AddToBox(bbox, li->v2->x, li->v2->y); + } + } + if (linebuffer - sector->lines != sector->linecount) + I_Error("Error: P_GroupLines: miscounted"); + + // set the degenmobj_t to the middle of the bounding box + sector->soundorg.x = (bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2; + sector->soundorg.y = (bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2; + + // adjust bounding box to map blocks + block = (bbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + block = block >= bmapheight ? bmapheight - 1 : block; + sector->blockbox[BOXTOP] = block; + + block = (bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXBOTTOM] = block; + + block = (bbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + block = block >= bmapwidth ? bmapwidth - 1 : block; + sector->blockbox[BOXRIGHT] = block; + + block = (bbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXLEFT] = block; + } +} + + +// +// P_SetupLevel +// +void P_SetupLevel(int episode, int map, int playermask, skill_t skill) +{ + int i; + char lumpname[9]; + int lumpnum; + + totalkills = totalitems = totalsecret = wminfo.maxfrags = 0; + wminfo.partime = 180; + for (i = 0; i < MAXPLAYERS; i++) + { + players[i].killcount = players[i].secretcount + = players[i].itemcount = 0; + } + + // Initial height of PointOfView + // will be set by player think. + players[consoleplayer].viewz = 1; + + // Make sure all sounds are stopped before Z_FreeTags. + S_Start(); + + Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1); + + // UNUSED W_Profile (); + P_InitThinkers(); + + // if working with a devlopment map, reload it + W_Reload(); + + // find map name + if (gamemode == commercial) + { + if (map < 10) + { + //doom_sprintf(lumpname, "map0%i", map); + doom_strcpy(lumpname, "map0"); + doom_concat(lumpname, doom_itoa(map, 10)); + } + else + { + //doom_sprintf(lumpname, "map%i", map); + doom_strcpy(lumpname, "map"); + doom_concat(lumpname, doom_itoa(map, 10)); + } + } + else + { + lumpname[0] = 'E'; + lumpname[1] = '0' + episode; + lumpname[2] = 'M'; + lumpname[3] = '0' + map; + lumpname[4] = 0; + } + + lumpnum = W_GetNumForName(lumpname); + + leveltime = 0; + + // note: most of this ordering is important + P_LoadBlockMap(lumpnum + ML_BLOCKMAP); + P_LoadVertexes(lumpnum + ML_VERTEXES); + P_LoadSectors(lumpnum + ML_SECTORS); + P_LoadSideDefs(lumpnum + ML_SIDEDEFS); + + P_LoadLineDefs(lumpnum + ML_LINEDEFS); + P_LoadSubsectors(lumpnum + ML_SSECTORS); + P_LoadNodes(lumpnum + ML_NODES); + P_LoadSegs(lumpnum + ML_SEGS); + + rejectmatrix = W_CacheLumpNum(lumpnum + ML_REJECT, PU_LEVEL); + P_GroupLines(); + + bodyqueslot = 0; + deathmatch_p = deathmatchstarts; + P_LoadThings(lumpnum + ML_THINGS); + + // if deathmatch, randomly spawn the active players + if (deathmatch) + { + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + { + players[i].mo = 0; + G_DeathMatchSpawnPlayer(i); + } + } + + // clear special respawning que + iquehead = iquetail = 0; + + // set up world state + P_SpawnSpecials(); + + // preload graphics + if (precache) + R_PrecacheLevel(); +} + + +// +// P_Init +// +void P_Init(void) +{ + P_InitSwitchList(); + P_InitPicAnims(); + R_InitSprites(sprnames); +} +fixed_t sightzstart; // eye z of looker +fixed_t topslope; +fixed_t bottomslope; // slopes to top and bottom of target + +divline_t strace; // from t1 to t2 +fixed_t t2x; +fixed_t t2y; + +int sightcounts[2]; + + +// +// P_DivlineSide +// Returns side 0 (front), 1 (back), or 2 (on). +// +int P_DivlineSide(fixed_t x, fixed_t y, divline_t* node) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!node->dx) + { + if (x == node->x) + return 2; + + if (x <= node->x) + return node->dy > 0; + + return node->dy < 0; + } + + if (!node->dy) + { + if (x == node->y) + return 2; + + if (y <= node->y) + return node->dx < 0; + + return node->dx > 0; + } + + dx = (x - node->x); + dy = (y - node->y); + + left = (node->dy >> FRACBITS) * (dx >> FRACBITS); + right = (dy >> FRACBITS) * (node->dx >> FRACBITS); + + if (right < left) + return 0; // front side + + if (left == right) + return 2; + return 1; // back side +} + + +// +// P_InterceptVector2 +// Returns the fractional intercept point +// along the first divline. +// This is only called by the addthings and addlines traversers. +// +fixed_t P_InterceptVector2(divline_t* v2, divline_t* v1) +{ + fixed_t frac; + fixed_t num; + fixed_t den; + + den = FixedMul(v1->dy >> 8, v2->dx) - FixedMul(v1->dx >> 8, v2->dy); + + if (den == 0) + return 0; + + num = FixedMul((v1->x - v2->x) >> 8, v1->dy) + + FixedMul((v2->y - v1->y) >> 8, v1->dx); + frac = FixedDiv(num, den); + + return frac; +} + + +// +// P_CrossSubsector +// Returns true +// if strace crosses the given subsector successfully. +// +doom_boolean P_CrossSubsector(int num) +{ + seg_t* seg; + line_t* line; + int s1; + int s2; + int count; + subsector_t* sub; + sector_t* front; + sector_t* back; + fixed_t opentop; + fixed_t openbottom; + divline_t divl; + vertex_t* v1; + vertex_t* v2; + fixed_t frac; + fixed_t slope; + +#ifdef RANGECHECK + if (num >= numsubsectors) + { + //I_Error("Error: P_CrossSubsector: ss %i with numss = %i", + // num, + // numsubsectors); + + doom_strcpy(error_buf, "Error: P_CrossSubsector: ss "); + doom_concat(error_buf, doom_itoa(num, 10)); + doom_concat(error_buf, " with numss = "); + doom_concat(error_buf, doom_itoa(numsubsectors, 10)); + I_Error(error_buf); + } +#endif + + sub = &subsectors[num]; + + // check lines + count = sub->numlines; + seg = &segs[sub->firstline]; + + for (; count; seg++, count--) + { + line = seg->linedef; + + // allready checked other side? + if (line->validcount == validcount) + continue; + + line->validcount = validcount; + + v1 = line->v1; + v2 = line->v2; + s1 = P_DivlineSide(v1->x, v1->y, &strace); + s2 = P_DivlineSide(v2->x, v2->y, &strace); + + // line isn't crossed? + if (s1 == s2) + continue; + + divl.x = v1->x; + divl.y = v1->y; + divl.dx = v2->x - v1->x; + divl.dy = v2->y - v1->y; + s1 = P_DivlineSide(strace.x, strace.y, &divl); + s2 = P_DivlineSide(t2x, t2y, &divl); + + // line isn't crossed? + if (s1 == s2) + continue; + + // stop because it is not two sided anyway + // might do this after updating validcount? + if (!(line->flags & ML_TWOSIDED)) + return false; + + // crosses a two sided line + front = seg->frontsector; + 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 + if (front->ceilingheight < back->ceilingheight) + opentop = front->ceilingheight; + else + opentop = back->ceilingheight; + + // because of ceiling height differences + if (front->floorheight > back->floorheight) + openbottom = front->floorheight; + else + openbottom = back->floorheight; + + // quick test for totally closed doors + if (openbottom >= opentop) + return false; // stop + + frac = P_InterceptVector2(&strace, &divl); + + if (front->floorheight != back->floorheight) + { + slope = FixedDiv(openbottom - sightzstart, frac); + if (slope > bottomslope) + bottomslope = slope; + } + + if (front->ceilingheight != back->ceilingheight) + { + slope = FixedDiv(opentop - sightzstart, frac); + if (slope < topslope) + topslope = slope; + } + + if (topslope <= bottomslope) + return false; // stop + } + + // passed the subsector ok + return true; +} + + +// +// P_CrossBSPNode +// Returns true +// if strace crosses the given node successfully. +// +doom_boolean P_CrossBSPNode(int bspnum) +{ + node_t* bsp; + int side; + + if (bspnum & NF_SUBSECTOR) + { + if (bspnum == -1) + return P_CrossSubsector(0); + else + return P_CrossSubsector(bspnum & (~NF_SUBSECTOR)); + } + + bsp = &nodes[bspnum]; + + // decide which side the start point is on + side = P_DivlineSide(strace.x, strace.y, (divline_t*)bsp); + if (side == 2) + side = 0; // an "on" should cross both sides + + // cross the starting side + if (!P_CrossBSPNode(bsp->children[side])) + return false; + + // the partition plane is crossed here + if (side == P_DivlineSide(t2x, t2y, (divline_t*)bsp)) + { + // the line doesn't touch the other side + return true; + } + + // cross the ending side + return P_CrossBSPNode(bsp->children[side ^ 1]); +} + + +// +// P_CheckSight +// Returns true +// if a straight line between t1 and t2 is unobstructed. +// Uses REJECT. +// +doom_boolean P_CheckSight(mobj_t* t1, mobj_t* t2) +{ + int s1; + int s2; + int pnum; + int bytenum; + int bitnum; + + // First check for trivial rejection. + + // Determine subsector entries in REJECT table. + s1 = (int)(t1->subsector->sector - sectors); + s2 = (int)(t2->subsector->sector - sectors); + pnum = s1 * numsectors + s2; + bytenum = pnum >> 3; + bitnum = 1 << (pnum & 7); + + // Check in REJECT table. + if (rejectmatrix[bytenum] & bitnum) + { + sightcounts[0]++; + + // can't possibly be connected + return false; + } + + // An unobstructed LOS is possible. + // Now look from eyes of t1 to any part of t2. + sightcounts[1]++; + + validcount++; + + sightzstart = t1->z + t1->height - (t1->height >> 2); + topslope = (t2->z + t2->height) - sightzstart; + bottomslope = (t2->z) - sightzstart; + + strace.x = t1->x; + strace.y = t1->y; + t2x = t2->x; + t2y = t2->y; + strace.dx = t2->x - t1->x; + strace.dy = t2->y - t1->y; + + // the head node is the last node output + return P_CrossBSPNode(numnodes - 1); +} +#define MAXANIMS 32 +#define MAXLINEANIMS 64 + +// 20 adjoining sectors max! [dsl] Useless comment is useless +#define MAX_ADJOINING_SECTORS 20 + + +// +// Animating textures and planes +// There is another anim_t used in wi_stuff, unrelated. +// +typedef struct +{ + doom_boolean istexture; + int picnum; + int basepic; + int numpics; + int speed; +} anim_t; + +// +// source animation definition +// +typedef struct +{ + doom_boolean istexture; // if false, it is a flat + char endname[9]; + char startname[9]; + int speed; +} animdef_t; + + +doom_boolean levelTimer; +int levelTimeCount; +short numlinespecials; +line_t* linespeciallist[MAXLINEANIMS]; + + +extern anim_t anims[MAXANIMS]; +extern anim_t* lastanim; + + +// +// P_InitPicAnims +// + +// Floor/ceiling animation sequences, +// defined by first and last frame, +// i.e. the flat (64x64 tile) name to +// be used. +// The full animation sequence is given +// using all the flats between the start +// and end entry, in the order found in +// the WAD file. +// +animdef_t animdefs[] = +{ + {false, "NUKAGE3", "NUKAGE1", 8}, + {false, "FWATER4", "FWATER1", 8}, + {false, "SWATER4", "SWATER1", 8}, + {false, "LAVA4", "LAVA1", 8}, + {false, "BLOOD3", "BLOOD1", 8}, + + // DOOM II flat animations. + {false, "RROCK08", "RROCK05", 8}, + {false, "SLIME04", "SLIME01", 8}, + {false, "SLIME08", "SLIME05", 8}, + {false, "SLIME12", "SLIME09", 8}, + + {true, "BLODGR4", "BLODGR1", 8}, + {true, "SLADRIP3", "SLADRIP1", 8}, + + {true, "BLODRIP4", "BLODRIP1", 8}, + {true, "FIREWALL", "FIREWALA", 8}, + {true, "GSTFONT3", "GSTFONT1", 8}, + {true, "FIRELAVA", "FIRELAV3", 8}, + {true, "FIREMAG3", "FIREMAG1", 8}, + {true, "FIREBLU2", "FIREBLU1", 8}, + {true, "ROCKRED3", "ROCKRED1", 8}, + + {true, "BFALL4", "BFALL1", 8}, + {true, "SFALL4", "SFALL1", 8}, + {true, "WFALL4", "WFALL1", 8}, + {true, "DBRAIN4", "DBRAIN1", 8}, + + {-1} +}; + +anim_t anims[MAXANIMS]; +anim_t* lastanim; + + +// +// Animating line specials +// + +extern short numlinespecials; +extern line_t* linespeciallist[MAXLINEANIMS]; + + +void P_InitPicAnims(void) +{ + int i; + + // Init animation + lastanim = anims; + for (i = 0; animdefs[i].istexture != (doom_boolean)-1; i++) + { + if (animdefs[i].istexture) + { + // different episode ? + if (R_CheckTextureNumForName(animdefs[i].startname) == -1) + continue; + + lastanim->picnum = R_TextureNumForName(animdefs[i].endname); + lastanim->basepic = R_TextureNumForName(animdefs[i].startname); + } + else + { + if (W_CheckNumForName(animdefs[i].startname) == -1) + continue; + + lastanim->picnum = R_FlatNumForName(animdefs[i].endname); + lastanim->basepic = R_FlatNumForName(animdefs[i].startname); + } + + lastanim->istexture = animdefs[i].istexture; + lastanim->numpics = lastanim->picnum - lastanim->basepic + 1; + + if (lastanim->numpics < 2) + { + //I_Error("Error: P_InitPicAnims: bad cycle from %s to %s", + // animdefs[i].startname, + // animdefs[i].endname); + + doom_strcpy(error_buf, "Error: P_InitPicAnims: bad cycle from "); + doom_concat(error_buf, animdefs[i].startname); + doom_concat(error_buf, " to "); + doom_concat(error_buf, animdefs[i].endname); + I_Error(error_buf); + } + + lastanim->speed = animdefs[i].speed; + lastanim++; + } +} + + +// +// UTILITIES +// + +// +// getSide() +// Will return a side_t* +// given the number of the current sector, +// the line number, and the side (0/1) that you want. +// +side_t* getSide(int currentSector, int line, int side) +{ + return &sides[(sectors[currentSector].lines[line])->sidenum[side]]; +} + + +// +// getSector() +// Will return a sector_t* +// given the number of the current sector, +// the line number and the side (0/1) that you want. +// +sector_t* getSector(int currentSector, int line, int side) +{ + return sides[(sectors[currentSector].lines[line])->sidenum[side]].sector; +} + + +// +// twoSided() +// Given the sector number and the line number, +// it will tell you whether the line is two-sided or not. +// +int twoSided(int sector, int line) +{ + return (sectors[sector].lines[line])->flags & ML_TWOSIDED; +} + + +// +// getNextSector() +// Return sector_t * of sector next to current. +// 0 if not two-sided line +// +sector_t* getNextSector(line_t* line, sector_t* sec) +{ + if (!(line->flags & ML_TWOSIDED)) + return 0; + + if (line->frontsector == sec) + return line->backsector; + + return line->frontsector; +} + + +// +// P_FindLowestFloorSurrounding() +// FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS +// +fixed_t P_FindLowestFloorSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = sec->floorheight; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = getNextSector(check, sec); + + if (!other) + continue; + + if (other->floorheight < floor) + floor = other->floorheight; + } + + return floor; +} + + +// +// P_FindHighestFloorSurrounding() +// FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS +// +fixed_t P_FindHighestFloorSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = -500 * FRACUNIT; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = getNextSector(check, sec); + + if (!other) + continue; + + if (other->floorheight > floor) + floor = other->floorheight; + } + + return floor; +} + + +// +// P_FindNextHighestFloor +// FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS +// Note: this should be doable w/o a fixed array. +fixed_t P_FindNextHighestFloor(sector_t* sec, int currentheight) +{ + int i; + int h; + int min; + line_t* check; + sector_t* other; + fixed_t height = currentheight; + + fixed_t heightlist[MAX_ADJOINING_SECTORS]; + + for (i = 0, h = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = getNextSector(check, sec); + + if (!other) + continue; + + if (other->floorheight > height) + heightlist[h++] = other->floorheight; + + // Check for overflow. Exit. + if (h >= MAX_ADJOINING_SECTORS) + { + doom_print( + "Sector with more than 20 adjoining sectors\n"); + break; + } + } + + // Find lowest height in list + if (!h) + return currentheight; + + min = heightlist[0]; + + // Range checking? + for (i = 1; i < h; i++) + if (heightlist[i] < min) + min = heightlist[i]; + + return min; +} + + +// +// FIND LOWEST CEILING IN THE SURROUNDING SECTORS +// +fixed_t P_FindLowestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = DOOM_MAXINT; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = getNextSector(check, sec); + + if (!other) + continue; + + if (other->ceilingheight < height) + height = other->ceilingheight; + } + + return height; +} + + +// +// FIND HIGHEST CEILING IN THE SURROUNDING SECTORS +// +fixed_t P_FindHighestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = 0; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = getNextSector(check, sec); + + if (!other) + continue; + + if (other->ceilingheight > height) + height = other->ceilingheight; + } + + return height; +} + + +// +// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO +// +int P_FindSectorFromLineTag(line_t* line, int start) +{ + int i; + + for (i = start + 1; i < numsectors; i++) + if (sectors[i].tag == line->tag) + return i; + + return -1; +} + + +// +// Find minimum light from an adjacent sector +// +int P_FindMinSurroundingLight(sector_t* sector, int max) +{ + int i; + int min; + line_t* line; + sector_t* check; + + min = max; + for (i = 0; i < sector->linecount; i++) + { + line = sector->lines[i]; + check = getNextSector(line, sector); + + if (!check) + continue; + + if (check->lightlevel < min) + min = check->lightlevel; + } + + return min; +} + + +// +// EVENTS +// Events are operations triggered by using, crossing, +// or shooting special lines, or by timed thinkers. +// + +// +// P_CrossSpecialLine - TRIGGER +// Called every time a thing origin is about +// to cross a line with a non 0 special. +// +void P_CrossSpecialLine(int linenum, int side, mobj_t* thing) +{ + line_t* line; + int ok; + + line = &lines[linenum]; + + // Triggers that other things can activate + if (!thing->player) + { + // Things that should NOT trigger specials... + switch (thing->type) + { + case MT_ROCKET: + case MT_PLASMA: + case MT_BFG: + case MT_TROOPSHOT: + case MT_HEADSHOT: + case MT_BRUISERSHOT: + return; + break; + + default: break; + } + + ok = 0; + switch (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 = 1; + break; + } + if (!ok) + return; + } + + // Note: could use some const's here. + switch (line->special) + { + // TRIGGERS. + // All from here to RETRIGGERS. + case 2: + // Open Door + EV_DoDoor(line, door_open); + line->special = 0; + break; + + case 3: + // Close Door + EV_DoDoor(line, door_close); + line->special = 0; + break; + + case 4: + // Raise Door + EV_DoDoor(line, door_normal); + line->special = 0; + break; + + case 5: + // Raise Floor + EV_DoFloor(line, raiseFloor); + line->special = 0; + break; + + case 6: + // Fast Ceiling Crush & Raise + EV_DoCeiling(line, fastCrushAndRaise); + line->special = 0; + break; + + case 8: + // Build Stairs + EV_BuildStairs(line, build8); + line->special = 0; + break; + + case 10: + // PlatDownWaitUp + EV_DoPlat(line, downWaitUpStay, 0); + line->special = 0; + break; + + case 12: + // Light Turn On - brightest near + EV_LightTurnOn(line, 0); + line->special = 0; + break; + + case 13: + // Light Turn On 255 + EV_LightTurnOn(line, 255); + line->special = 0; + break; + + case 16: + // Close Door 30 + EV_DoDoor(line, close30ThenOpen); + line->special = 0; + break; + + case 17: + // Start Light Strobing + EV_StartLightStrobing(line); + line->special = 0; + break; + + case 19: + // Lower Floor + EV_DoFloor(line, lowerFloor); + line->special = 0; + break; + + case 22: + // Raise floor to nearest height and change texture + EV_DoPlat(line, raiseToNearestAndChange, 0); + line->special = 0; + break; + + case 25: + // Ceiling Crush and Raise + EV_DoCeiling(line, crushAndRaise); + line->special = 0; + break; + + case 30: + // Raise floor to shortest texture height + // on either side of lines. + EV_DoFloor(line, raiseToTexture); + line->special = 0; + break; + + case 35: + // Lights Very Dark + EV_LightTurnOn(line, 35); + line->special = 0; + break; + + case 36: + // Lower Floor (TURBO) + EV_DoFloor(line, turboLower); + line->special = 0; + break; + + case 37: + // LowerAndChange + EV_DoFloor(line, lowerAndChange); + line->special = 0; + break; + + case 38: + // Lower Floor To Lowest + EV_DoFloor(line, lowerFloorToLowest); + line->special = 0; + break; + + case 39: + // TELEPORT! + EV_Teleport(line, side, thing); + line->special = 0; + break; + + case 40: + // RaiseCeilingLowerFloor + EV_DoCeiling(line, raiseToHighest); + EV_DoFloor(line, lowerFloorToLowest); + line->special = 0; + break; + + case 44: + // Ceiling Crush + EV_DoCeiling(line, lowerAndCrush); + line->special = 0; + break; + + case 52: + // EXIT! + G_ExitLevel(); + break; + + case 53: + // Perpetual Platform Raise + EV_DoPlat(line, perpetualRaise, 0); + line->special = 0; + break; + + case 54: + // Platform Stop + EV_StopPlat(line); + line->special = 0; + break; + + case 56: + // Raise Floor Crush + EV_DoFloor(line, raiseFloorCrush); + line->special = 0; + break; + + case 57: + // Ceiling Crush Stop + EV_CeilingCrushStop(line); + line->special = 0; + break; + + case 58: + // Raise Floor 24 + EV_DoFloor(line, raiseFloor24); + line->special = 0; + break; + + case 59: + // Raise Floor 24 And Change + EV_DoFloor(line, raiseFloor24AndChange); + line->special = 0; + break; + + case 104: + // Turn lights off in sector(tag) + EV_TurnTagLightsOff(line); + line->special = 0; + break; + + case 108: + // Blazing Door Raise (faster than TURBO!) + EV_DoDoor(line, blazeRaise); + line->special = 0; + break; + + case 109: + // Blazing Door Open (faster than TURBO!) + EV_DoDoor(line, blazeOpen); + line->special = 0; + break; + + case 100: + // Build Stairs Turbo 16 + EV_BuildStairs(line, turbo16); + line->special = 0; + break; + + case 110: + // Blazing Door Close (faster than TURBO!) + EV_DoDoor(line, blazeClose); + line->special = 0; + break; + + case 119: + // Raise floor to nearest surr. floor + EV_DoFloor(line, raiseFloorToNearest); + line->special = 0; + break; + + case 121: + // Blazing PlatDownWaitUpStay + EV_DoPlat(line, blazeDWUS, 0); + line->special = 0; + break; + + case 124: + // Secret EXIT + G_SecretExitLevel(); + break; + + case 125: + // TELEPORT MonsterONLY + if (!thing->player) + { + EV_Teleport(line, side, thing); + line->special = 0; + } + break; + + case 130: + // Raise Floor Turbo + EV_DoFloor(line, raiseFloorTurbo); + line->special = 0; + break; + + case 141: + // Silent Ceiling Crush & Raise + EV_DoCeiling(line, silentCrushAndRaise); + line->special = 0; + break; + + // RETRIGGERS. All from here till end. + case 72: + // Ceiling Crush + EV_DoCeiling(line, lowerAndCrush); + break; + + case 73: + // Ceiling Crush and Raise + EV_DoCeiling(line, crushAndRaise); + break; + + case 74: + // Ceiling Crush Stop + EV_CeilingCrushStop(line); + break; + + case 75: + // Close Door + EV_DoDoor(line, door_close); + break; + + case 76: + // Close Door 30 + EV_DoDoor(line, close30ThenOpen); + break; + + case 77: + // Fast Ceiling Crush & Raise + EV_DoCeiling(line, fastCrushAndRaise); + break; + + case 79: + // Lights Very Dark + EV_LightTurnOn(line, 35); + break; + + case 80: + // Light Turn On - brightest near + EV_LightTurnOn(line, 0); + break; + + case 81: + // Light Turn On 255 + EV_LightTurnOn(line, 255); + break; + + case 82: + // Lower Floor To Lowest + EV_DoFloor(line, lowerFloorToLowest); + break; + + case 83: + // Lower Floor + EV_DoFloor(line, lowerFloor); + break; + + case 84: + // LowerAndChange + EV_DoFloor(line, lowerAndChange); + break; + + case 86: + // Open Door + EV_DoDoor(line, door_open); + break; + + case 87: + // Perpetual Platform Raise + EV_DoPlat(line, perpetualRaise, 0); + break; + + case 88: + // PlatDownWaitUp + EV_DoPlat(line, downWaitUpStay, 0); + break; + + case 89: + // Platform Stop + EV_StopPlat(line); + break; + + case 90: + // Raise Door + EV_DoDoor(line, door_normal); + break; + + case 91: + // Raise Floor + EV_DoFloor(line, raiseFloor); + break; + + case 92: + // Raise Floor 24 + EV_DoFloor(line, raiseFloor24); + break; + + case 93: + // Raise Floor 24 And Change + EV_DoFloor(line, raiseFloor24AndChange); + break; + + case 94: + // Raise Floor Crush + EV_DoFloor(line, raiseFloorCrush); + break; + + case 95: + // Raise floor to nearest height + // and change texture. + EV_DoPlat(line, raiseToNearestAndChange, 0); + break; + + case 96: + // Raise floor to shortest texture height + // on either side of lines. + EV_DoFloor(line, raiseToTexture); + break; + + case 97: + // TELEPORT! + EV_Teleport(line, side, thing); + break; + + case 98: + // Lower Floor (TURBO) + EV_DoFloor(line, turboLower); + break; + + case 105: + // Blazing Door Raise (faster than TURBO!) + EV_DoDoor(line, blazeRaise); + break; + + case 106: + // Blazing Door Open (faster than TURBO!) + EV_DoDoor(line, blazeOpen); + break; + + case 107: + // Blazing Door Close (faster than TURBO!) + EV_DoDoor(line, blazeClose); + break; + + case 120: + // Blazing PlatDownWaitUpStay. + EV_DoPlat(line, blazeDWUS, 0); + break; + + case 126: + // TELEPORT MonsterONLY. + if (!thing->player) + EV_Teleport(line, side, thing); + break; + + case 128: + // Raise To Nearest Floor + EV_DoFloor(line, raiseFloorToNearest); + break; + + case 129: + // Raise Floor Turbo + EV_DoFloor(line, raiseFloorTurbo); + break; + } +} + + +// +// P_ShootSpecialLine - IMPACT SPECIALS +// Called when a thing shoots a special line. +// +void P_ShootSpecialLine(mobj_t* thing, line_t* line) +{ + int ok; + + // Impacts that other things can activate. + if (!thing->player) + { + ok = 0; + switch (line->special) + { + case 46: + // OPEN DOOR IMPACT + ok = 1; + break; + } + if (!ok) + return; + } + + switch (line->special) + { + case 24: + // RAISE FLOOR + EV_DoFloor(line, raiseFloor); + P_ChangeSwitchTexture(line, 0); + break; + + case 46: + // OPEN DOOR + EV_DoDoor(line, door_open); + P_ChangeSwitchTexture(line, 1); + break; + + case 47: + // RAISE FLOOR NEAR AND CHANGE + EV_DoPlat(line, raiseToNearestAndChange, 0); + P_ChangeSwitchTexture(line, 0); + break; + } +} + + +// +// P_PlayerInSpecialSector +// Called every tic frame +// that the player origin is in a special sector +// +void P_PlayerInSpecialSector(player_t* player) +{ + sector_t* sector; + + sector = player->mo->subsector->sector; + + // Falling, not all the way down yet? + if (player->mo->z != sector->floorheight) + return; + + // Has hitten ground. + switch (sector->special) + { + case 5: + // HELLSLIME DAMAGE + if (!player->powers[pw_ironfeet]) + if (!(leveltime & 0x1f)) + P_DamageMobj(player->mo, 0, 0, 10); + break; + + case 7: + // NUKAGE DAMAGE + if (!player->powers[pw_ironfeet]) + if (!(leveltime & 0x1f)) + P_DamageMobj(player->mo, 0, 0, 5); + break; + + case 16: + // SUPER HELLSLIME DAMAGE + case 4: + // STROBE HURT + if (!player->powers[pw_ironfeet] + || (P_Random() < 5)) + { + if (!(leveltime & 0x1f)) + P_DamageMobj(player->mo, 0, 0, 20); + } + break; + + case 9: + // SECRET SECTOR + player->secretcount++; + sector->special = 0; + break; + + case 11: + // EXIT SUPER DAMAGE! (for E1M8 finale) + player->cheats &= ~CF_GODMODE; + + if (!(leveltime & 0x1f)) + P_DamageMobj(player->mo, 0, 0, 20); + + if (player->health <= 10) + G_ExitLevel(); + break; + + default: + { + //I_Error("Error: P_PlayerInSpecialSector: " + // "unknown special %i", + // sector->special); + + + doom_strcpy(error_buf, "Error: P_PlayerInSpecialSector: unknown special "); + doom_concat(error_buf, doom_itoa(sector->special, 10)); + I_Error(error_buf); + break; + } + }; +} + + +// +// P_UpdateSpecials +// Animate planes, scroll walls, etc. +// +void P_UpdateSpecials(void) +{ + anim_t* anim; + int pic; + int i; + line_t* line; + + // LEVEL TIMER + if (levelTimer == true) + { + levelTimeCount--; + if (!levelTimeCount) + G_ExitLevel(); + } + + // ANIMATE FLATS AND TEXTURES GLOBALLY + for (anim = anims; anim < lastanim; anim++) + { + for (i = anim->basepic; i < anim->basepic + anim->numpics; i++) + { + pic = anim->basepic + ((leveltime / anim->speed + i) % anim->numpics); + if (anim->istexture) + texturetranslation[i] = pic; + else + flattranslation[i] = pic; + } + } + + // ANIMATE LINE SPECIALS + for (i = 0; i < numlinespecials; i++) + { + line = linespeciallist[i]; + switch (line->special) + { + case 48: + // EFFECT FIRSTCOL SCROLL + + sides[line->sidenum[0]].textureoffset += FRACUNIT; + break; + } + } + + // DO BUTTONS + for (i = 0; i < MAXBUTTONS; i++) + if (buttonlist[i].btimer) + { + buttonlist[i].btimer--; + if (!buttonlist[i].btimer) + { + switch (buttonlist[i].where) + { + case top: + sides[buttonlist[i].line->sidenum[0]].toptexture = + buttonlist[i].btexture; + break; + + case middle: + sides[buttonlist[i].line->sidenum[0]].midtexture = + buttonlist[i].btexture; + break; + + case bottom: + sides[buttonlist[i].line->sidenum[0]].bottomtexture = + buttonlist[i].btexture; + break; + } + S_StartSound((mobj_t*)&buttonlist[i].soundorg, sfx_swtchn); + doom_memset(&buttonlist[i], 0, sizeof(button_t)); + } + } +} + + +// +// Special Stuff that can not be categorized +// +int EV_DoDonut(line_t* line) +{ + sector_t* s1; + sector_t* s2; + sector_t* s3; + int secnum; + int rtn; + int i; + floormove_t* floor; + + secnum = -1; + rtn = 0; + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + s1 = §ors[secnum]; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (s1->specialdata) + continue; + + rtn = 1; + s2 = getNextSector(s1->lines[0], s1); + for (i = 0; i < s2->linecount; i++) + { + if ((!(s2->lines[i]->flags & ML_TWOSIDED)) || + (s2->lines[i]->backsector == s1)) + continue; + s3 = s2->lines[i]->backsector; + + // Spawn rising slime + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + P_AddThinker(&floor->thinker); + s2->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + floor->type = donutRaise; + floor->crush = false; + floor->direction = 1; + floor->sector = s2; + floor->speed = FLOORSPEED / 2; + floor->texture = s3->floorpic; + floor->newspecial = 0; + floor->floordestheight = s3->floorheight; + + // Spawn lowering donut-hole + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + P_AddThinker(&floor->thinker); + s1->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + floor->type = lowerFloor; + floor->crush = false; + floor->direction = -1; + floor->sector = s1; + floor->speed = FLOORSPEED / 2; + floor->floordestheight = s3->floorheight; + break; + } + } + + return rtn; +} + + +// +// SPECIAL SPAWNING +// + +// +// P_SpawnSpecials +// After the map has been loaded, scan for specials +// that spawn thinkers +// + +// Parses command line parameters. +void P_SpawnSpecials(void) +{ + sector_t* sector; + int i; + int episode; + + episode = 1; + if (W_CheckNumForName("texture2") >= 0) + episode = 2; + + + // See if -TIMER needs to be used. + levelTimer = false; + + i = M_CheckParm("-avg"); + if (i && deathmatch) + { + levelTimer = true; + levelTimeCount = 20 * 60 * 35; + } + + i = M_CheckParm("-timer"); + if (i && deathmatch) + { + int time; + time = doom_atoi(myargv[i + 1]) * 60 * 35; + levelTimer = true; + levelTimeCount = time; + } + + // Init special SECTORs. + sector = sectors; + for (i = 0; i < numsectors; i++, sector++) + { + if (!sector->special) + continue; + + switch (sector->special) + { + case 1: + // FLICKERING LIGHTS + P_SpawnLightFlash(sector); + break; + + case 2: + // STROBE FAST + P_SpawnStrobeFlash(sector, FASTDARK, 0); + break; + + case 3: + // STROBE SLOW + P_SpawnStrobeFlash(sector, SLOWDARK, 0); + break; + + case 4: + // STROBE FAST/DEATH SLIME + P_SpawnStrobeFlash(sector, FASTDARK, 0); + sector->special = 4; + break; + + case 8: + // GLOWING LIGHT + P_SpawnGlowingLight(sector); + break; + case 9: + // SECRET SECTOR + totalsecret++; + break; + + case 10: + // DOOR CLOSE IN 30 SECONDS + P_SpawnDoorCloseIn30(sector); + break; + + case 12: + // SYNC STROBE SLOW + P_SpawnStrobeFlash(sector, SLOWDARK, 1); + break; + + case 13: + // SYNC STROBE FAST + P_SpawnStrobeFlash(sector, FASTDARK, 1); + break; + + case 14: + // DOOR RAISE IN 5 MINUTES + P_SpawnDoorRaiseIn5Mins(sector, i); + break; + + case 17: + P_SpawnFireFlicker(sector); + break; + } + } + + + // Init line EFFECTs + numlinespecials = 0; + for (i = 0; i < numlines; i++) + { + switch (lines[i].special) + { + case 48: + // EFFECT FIRSTCOL SCROLL+ + linespeciallist[numlinespecials] = &lines[i]; + numlinespecials++; + break; + } + } + + + // Init other misc stuff + for (i = 0; i < MAXCEILINGS; i++) + activeceilings[i] = 0; + + for (i = 0; i < MAXPLATS; i++) + activeplats[i] = 0; + + for (i = 0; i < MAXBUTTONS; i++) + doom_memset(&buttonlist[i], 0, sizeof(button_t)); +} +switchlist_t alphSwitchList[] = +{ + // Doom shareware episode 1 switches + {"SW1BRCOM", "SW2BRCOM", 1}, + {"SW1BRN1", "SW2BRN1", 1}, + {"SW1BRN2", "SW2BRN2", 1}, + {"SW1BRNGN", "SW2BRNGN", 1}, + {"SW1BROWN", "SW2BROWN", 1}, + {"SW1COMM", "SW2COMM", 1}, + {"SW1COMP", "SW2COMP", 1}, + {"SW1DIRT", "SW2DIRT", 1}, + {"SW1EXIT", "SW2EXIT", 1}, + {"SW1GRAY", "SW2GRAY", 1}, + {"SW1GRAY1", "SW2GRAY1", 1}, + {"SW1METAL", "SW2METAL", 1}, + {"SW1PIPE", "SW2PIPE", 1}, + {"SW1SLAD", "SW2SLAD", 1}, + {"SW1STARG", "SW2STARG", 1}, + {"SW1STON1", "SW2STON1", 1}, + {"SW1STON2", "SW2STON2", 1}, + {"SW1STONE", "SW2STONE", 1}, + {"SW1STRTN", "SW2STRTN", 1}, + + // Doom registered episodes 2&3 switches + {"SW1BLUE", "SW2BLUE", 2}, + {"SW1CMT", "SW2CMT", 2}, + {"SW1GARG", "SW2GARG", 2}, + {"SW1GSTON", "SW2GSTON", 2}, + {"SW1HOT", "SW2HOT", 2}, + {"SW1LION", "SW2LION", 2}, + {"SW1SATYR", "SW2SATYR", 2}, + {"SW1SKIN", "SW2SKIN", 2}, + {"SW1VINE", "SW2VINE", 2}, + {"SW1WOOD", "SW2WOOD", 2}, + + // Doom II switches + {"SW1PANEL", "SW2PANEL", 3}, + {"SW1ROCK", "SW2ROCK", 3}, + {"SW1MET2", "SW2MET2", 3}, + {"SW1WDMET", "SW2WDMET", 3}, + {"SW1BRIK", "SW2BRIK", 3}, + {"SW1MOD1", "SW2MOD1", 3}, + {"SW1ZIM", "SW2ZIM", 3}, + {"SW1STON6", "SW2STON6", 3}, + {"SW1TEK", "SW2TEK", 3}, + {"SW1MARB", "SW2MARB", 3}, + {"SW1SKULL", "SW2SKULL", 3}, + + {"\0", "\0", 0} +}; + +int switchlist[MAXSWITCHES * 2]; +int numswitches; +button_t buttonlist[MAXBUTTONS]; + + +// +// P_InitSwitchList +// Only called at game initialization. +// +void P_InitSwitchList(void) +{ + int i; + int index; + int episode; + + episode = 1; + + if (gamemode == registered) + episode = 2; + else + if (gamemode == commercial) + episode = 3; + + for (index = 0, i = 0; i < MAXSWITCHES; i++) + { + if (!alphSwitchList[i].episode) + { + numswitches = index / 2; + switchlist[index] = -1; + break; + } + + if (alphSwitchList[i].episode <= episode) + { + switchlist[index++] = R_TextureNumForName(alphSwitchList[i].name1); + switchlist[index++] = R_TextureNumForName(alphSwitchList[i].name2); + } + } +} + + +// +// Start a button counting down till it turns off. +// +void P_StartButton(line_t* line, bwhere_e w, int texture, int time) +{ + int i; + + // See if button is already pressed + for (i = 0; i < MAXBUTTONS; i++) + { + if (buttonlist[i].btimer + && buttonlist[i].line == line) + { + + return; + } + } + + for (i = 0; i < MAXBUTTONS; i++) + { + if (!buttonlist[i].btimer) + { + buttonlist[i].line = line; + buttonlist[i].where = w; + buttonlist[i].btexture = texture; + buttonlist[i].btimer = time; + buttonlist[i].soundorg = (mobj_t*)&line->frontsector->soundorg; + return; + } + } + + I_Error("Error: P_StartButton: no button slots left!"); +} + + +// +// Function that changes wall texture. +// Tell it if switch is ok to use again (1=yes, it's a button). +// +void P_ChangeSwitchTexture(line_t* line, int useAgain) +{ + int texTop; + int texMid; + int texBot; + int i; + int sound; + + if (!useAgain) + line->special = 0; + + texTop = sides[line->sidenum[0]].toptexture; + texMid = sides[line->sidenum[0]].midtexture; + texBot = sides[line->sidenum[0]].bottomtexture; + + sound = sfx_swtchn; + + // EXIT SWITCH? + if (line->special == 11) + sound = sfx_swtchx; + + for (i = 0; i < numswitches * 2; i++) + { + if (switchlist[i] == texTop) + { + S_StartSound(buttonlist->soundorg, sound); + sides[line->sidenum[0]].toptexture = switchlist[i ^ 1]; + + if (useAgain) + P_StartButton(line, top, switchlist[i], BUTTONTIME); + + return; + } + else + { + if (switchlist[i] == texMid) + { + S_StartSound(buttonlist->soundorg, sound); + sides[line->sidenum[0]].midtexture = switchlist[i ^ 1]; + + if (useAgain) + P_StartButton(line, middle, switchlist[i], BUTTONTIME); + + return; + } + else + { + if (switchlist[i] == texBot) + { + S_StartSound(buttonlist->soundorg, sound); + sides[line->sidenum[0]].bottomtexture = switchlist[i ^ 1]; + + if (useAgain) + P_StartButton(line, bottom, switchlist[i], BUTTONTIME); + + return; + } + } + } + } +} + + +// +// P_UseSpecialLine +// Called when a thing uses a special line. +// Only the front sides of lines are usable. +// +doom_boolean P_UseSpecialLine(mobj_t* thing, line_t* line, int side) +{ + // Err... + // Use the back sides of VERY SPECIAL lines... + if (side) + { + switch (line->special) + { + case 124: + // Sliding door open&close + // UNUSED? + break; + + default: + return false; + break; + } + } + + + // Switches that other things can activate. + if (!thing->player) + { + // never open secret doors + if (line->flags & ML_SECRET) + return false; + + switch (line->special) + { + case 1: // MANUAL DOOR RAISE + case 32: // MANUAL BLUE + case 33: // MANUAL RED + case 34: // MANUAL YELLOW + break; + + default: + return false; + break; + } + } + + + // do something + switch (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 + EV_VerticalDoor(line, thing); + break; + + //UNUSED - Door Slide Open&Close + // case 124: + // EV_SlidingDoor (line, thing); + // break; + + // SWITCHES + case 7: + // Build Stairs + if (EV_BuildStairs(line, build8)) + P_ChangeSwitchTexture(line, 0); + break; + + case 9: + // Change Donut + if (EV_DoDonut(line)) + P_ChangeSwitchTexture(line, 0); + break; + + case 11: + // Exit level + P_ChangeSwitchTexture(line, 0); + G_ExitLevel(); + break; + + case 14: + // Raise Floor 32 and change texture + if (EV_DoPlat(line, raiseAndChange, 32)) + P_ChangeSwitchTexture(line, 0); + break; + + case 15: + // Raise Floor 24 and change texture + if (EV_DoPlat(line, raiseAndChange, 24)) + P_ChangeSwitchTexture(line, 0); + break; + + case 18: + // Raise Floor to next highest floor + if (EV_DoFloor(line, raiseFloorToNearest)) + P_ChangeSwitchTexture(line, 0); + break; + + case 20: + // Raise Plat next highest floor and change texture + if (EV_DoPlat(line, raiseToNearestAndChange, 0)) + P_ChangeSwitchTexture(line, 0); + break; + + case 21: + // PlatDownWaitUpStay + if (EV_DoPlat(line, downWaitUpStay, 0)) + P_ChangeSwitchTexture(line, 0); + break; + + case 23: + // Lower Floor to Lowest + if (EV_DoFloor(line, lowerFloorToLowest)) + P_ChangeSwitchTexture(line, 0); + break; + + case 29: + // Raise Door + if (EV_DoDoor(line, door_normal)) + P_ChangeSwitchTexture(line, 0); + break; + + case 41: + // Lower Ceiling to Floor + if (EV_DoCeiling(line, lowerToFloor)) + P_ChangeSwitchTexture(line, 0); + break; + + case 71: + // Turbo Lower Floor + if (EV_DoFloor(line, turboLower)) + P_ChangeSwitchTexture(line, 0); + break; + + case 49: + // Ceiling Crush And Raise + if (EV_DoCeiling(line, crushAndRaise)) + P_ChangeSwitchTexture(line, 0); + break; + + case 50: + // Close Door + if (EV_DoDoor(line, door_close)) + P_ChangeSwitchTexture(line, 0); + break; + + case 51: + // Secret EXIT + P_ChangeSwitchTexture(line, 0); + G_SecretExitLevel(); + break; + + case 55: + // Raise Floor Crush + if (EV_DoFloor(line, raiseFloorCrush)) + P_ChangeSwitchTexture(line, 0); + break; + + case 101: + // Raise Floor + if (EV_DoFloor(line, raiseFloor)) + P_ChangeSwitchTexture(line, 0); + break; + + case 102: + // Lower Floor to Surrounding floor height + if (EV_DoFloor(line, lowerFloor)) + P_ChangeSwitchTexture(line, 0); + break; + + case 103: + // Open Door + if (EV_DoDoor(line, door_open)) + P_ChangeSwitchTexture(line, 0); + break; + + case 111: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor(line, blazeRaise)) + P_ChangeSwitchTexture(line, 0); + break; + + case 112: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor(line, blazeOpen)) + P_ChangeSwitchTexture(line, 0); + break; + + case 113: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor(line, blazeClose)) + P_ChangeSwitchTexture(line, 0); + break; + + case 122: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line, blazeDWUS, 0)) + P_ChangeSwitchTexture(line, 0); + break; + + case 127: + // Build Stairs Turbo 16 + if (EV_BuildStairs(line, turbo16)) + P_ChangeSwitchTexture(line, 0); + break; + + case 131: + // Raise Floor Turbo + if (EV_DoFloor(line, raiseFloorTurbo)) + P_ChangeSwitchTexture(line, 0); + break; + + case 133: + // BlzOpenDoor BLUE + case 135: + // BlzOpenDoor RED + case 137: + // BlzOpenDoor YELLOW + if (EV_DoLockedDoor(line, blazeOpen, thing)) + P_ChangeSwitchTexture(line, 0); + break; + + case 140: + // Raise Floor 512 + if (EV_DoFloor(line, raiseFloor512)) + P_ChangeSwitchTexture(line, 0); + break; + + // BUTTONS + case 42: + // Close Door + if (EV_DoDoor(line, door_close)) + P_ChangeSwitchTexture(line, 1); + break; + + case 43: + // Lower Ceiling to Floor + if (EV_DoCeiling(line, lowerToFloor)) + P_ChangeSwitchTexture(line, 1); + break; + + case 45: + // Lower Floor to Surrounding floor height + if (EV_DoFloor(line, lowerFloor)) + P_ChangeSwitchTexture(line, 1); + break; + + case 60: + // Lower Floor to Lowest + if (EV_DoFloor(line, lowerFloorToLowest)) + P_ChangeSwitchTexture(line, 1); + break; + + case 61: + // Open Door + if (EV_DoDoor(line, door_open)) + P_ChangeSwitchTexture(line, 1); + break; + + case 62: + // PlatDownWaitUpStay + if (EV_DoPlat(line, downWaitUpStay, 1)) + P_ChangeSwitchTexture(line, 1); + break; + + case 63: + // Raise Door + if (EV_DoDoor(line, door_normal)) + P_ChangeSwitchTexture(line, 1); + break; + + case 64: + // Raise Floor to ceiling + if (EV_DoFloor(line, raiseFloor)) + P_ChangeSwitchTexture(line, 1); + break; + + case 66: + // Raise Floor 24 and change texture + if (EV_DoPlat(line, raiseAndChange, 24)) + P_ChangeSwitchTexture(line, 1); + break; + + case 67: + // Raise Floor 32 and change texture + if (EV_DoPlat(line, raiseAndChange, 32)) + P_ChangeSwitchTexture(line, 1); + break; + + case 65: + // Raise Floor Crush + if (EV_DoFloor(line, raiseFloorCrush)) + P_ChangeSwitchTexture(line, 1); + break; + + case 68: + // Raise Plat to next highest floor and change texture + if (EV_DoPlat(line, raiseToNearestAndChange, 0)) + P_ChangeSwitchTexture(line, 1); + break; + + case 69: + // Raise Floor to next highest floor + if (EV_DoFloor(line, raiseFloorToNearest)) + P_ChangeSwitchTexture(line, 1); + break; + + case 70: + // Turbo Lower Floor + if (EV_DoFloor(line, turboLower)) + P_ChangeSwitchTexture(line, 1); + break; + + case 114: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor(line, blazeRaise)) + P_ChangeSwitchTexture(line, 1); + break; + + case 115: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor(line, blazeOpen)) + P_ChangeSwitchTexture(line, 1); + break; + + case 116: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor(line, blazeClose)) + P_ChangeSwitchTexture(line, 1); + break; + + case 123: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line, blazeDWUS, 0)) + P_ChangeSwitchTexture(line, 1); + break; + + case 132: + // Raise Floor Turbo + if (EV_DoFloor(line, raiseFloorTurbo)) + P_ChangeSwitchTexture(line, 1); + break; + + case 99: + // BlzOpenDoor BLUE + case 134: + // BlzOpenDoor RED + case 136: + // BlzOpenDoor YELLOW + if (EV_DoLockedDoor(line, blazeOpen, thing)) + P_ChangeSwitchTexture(line, 1); + break; + + case 138: + // Light Turn On + EV_LightTurnOn(line, 255); + P_ChangeSwitchTexture(line, 1); + break; + + case 139: + // Light Turn Off + EV_LightTurnOn(line, 35); + P_ChangeSwitchTexture(line, 1); + break; + } + + return true; +} +int EV_Teleport(line_t* line, int side, mobj_t* thing) +{ + int i; + int tag; + mobj_t* m; + mobj_t* fog; + unsigned an; + thinker_t* thinker; + sector_t* sector; + fixed_t oldx; + fixed_t oldy; + fixed_t oldz; + + // don't teleport missiles + if (thing->flags & MF_MISSILE) + return 0; + + // Don't teleport if hit back of line, + // so you can get out of teleporter. + if (side == 1) + return 0; + + tag = line->tag; + for (i = 0; i < numsectors; i++) + { + if (sectors[i].tag == tag) + { + thinker = thinkercap.next; + for (thinker = thinkercap.next; + thinker != &thinkercap; + thinker = thinker->next) + { + // not a mobj + if (thinker->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + m = (mobj_t*)thinker; + + // not a teleportman + if (m->type != MT_TELEPORTMAN) + continue; + + sector = m->subsector->sector; + // wrong sector + if (sector - sectors != i) + continue; + + oldx = thing->x; + oldy = thing->y; + oldz = thing->z; + + if (!P_TeleportMove(thing, m->x, m->y)) + return 0; + + thing->z = thing->floorz; //fixme: not needed? + if (thing->player) + thing->player->viewz = thing->z + thing->player->viewheight; + + // spawn teleport fog at source and destination + fog = P_SpawnMobj(oldx, oldy, oldz, MT_TFOG); + S_StartSound(fog, sfx_telept); + an = m->angle >> ANGLETOFINESHIFT; + fog = P_SpawnMobj(m->x + 20 * finecosine[an], m->y + 20 * finesine[an] + , thing->z, MT_TFOG); + + // emit sound, where? + S_StartSound(fog, sfx_telept); + + // don't move for a bit + if (thing->player) + thing->reactiontime = 18; + + thing->angle = m->angle; + thing->momx = thing->momy = thing->momz = 0; + return 1; + } + } + } + + return 0; +} +int leveltime; + +// +// THINKERS +// All thinkers should be allocated by Z_Malloc +// so they can be operated on uniformly. +// The actual structures will vary in size, +// but the first element must be thinker_t. +// + +// Both the head and tail of the thinker list. +thinker_t thinkercap; + + +// +// P_InitThinkers +// +void P_InitThinkers(void) +{ + thinkercap.prev = thinkercap.next = &thinkercap; +} + + +// +// P_AddThinker +// Adds a new thinker at the end of the list. +// +void P_AddThinker(thinker_t* thinker) +{ + thinkercap.prev->next = thinker; + thinker->next = &thinkercap; + thinker->prev = thinkercap.prev; + thinkercap.prev = thinker; +} + + +// +// P_RemoveThinker +// Deallocation is lazy -- it will not actually be freed +// until its thinking turn comes up. +// +void P_RemoveThinker(thinker_t* thinker) +{ + // FIXME: NOP. + thinker->function.acv = (actionf_v)(-1); +} + + +// +// P_RunThinkers +// +void P_RunThinkers(void) +{ + thinker_t* currentthinker; + + currentthinker = thinkercap.next; + while (currentthinker != &thinkercap) + { + if (currentthinker->function.acv == (actionf_v)(-1)) + { + // time to remove it + currentthinker->next->prev = currentthinker->prev; + currentthinker->prev->next = currentthinker->next; + Z_Free(currentthinker); + } + else + { + if (currentthinker->function.acp1) + currentthinker->function.acp1(currentthinker); + } + currentthinker = currentthinker->next; + } +} + + +// +// P_Ticker +// +void P_Ticker(void) +{ + int i; + + // run the tic + if (paused) + return; + + // pause if in menu and at least one tic has been run + if (!netgame + && menuactive + && !demoplayback + && players[consoleplayer].viewz != 1) + { + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + P_PlayerThink(&players[i]); + + P_RunThinkers(); + P_UpdateSpecials(); + P_RespawnSpecials(); + + // for par times + leveltime++; +} +#define INVERSECOLORMAP 32 + +// 16 pixels of bob +#define MAXBOB 0x100000 + +#define ANG5 (ANG90/18) + + +// +// Movement. +// + +doom_boolean onground; + + +// +// P_Thrust +// Moves the given origin along a given angle. +// +void P_Thrust(player_t* player, angle_t angle, fixed_t move) +{ + angle >>= ANGLETOFINESHIFT; + + player->mo->momx += FixedMul(move, finecosine[angle]); + player->mo->momy += FixedMul(move, finesine[angle]); +} + + +// +// P_CalcHeight +// Calculate the walking / running height adjustment +// +void P_CalcHeight(player_t* player) +{ + int angle; + fixed_t bob; + + // Regular movement bobbing + // (needs to be calculated for gun swing + // even if not on ground) + // OPTIMIZE: tablify angle + // Note: a LUT allows for effects + // like a ramp with low health. + player->bob = + FixedMul(player->mo->momx, player->mo->momx) + + FixedMul(player->mo->momy, player->mo->momy); + + player->bob >>= 2; + + if (player->bob > MAXBOB) + player->bob = MAXBOB; + + if ((player->cheats & CF_NOMOMENTUM) || !onground) + { + player->viewz = player->mo->z + VIEWHEIGHT; + + if (player->viewz > player->mo->ceilingz - 4 * FRACUNIT) + player->viewz = player->mo->ceilingz - 4 * FRACUNIT; + + player->viewz = player->mo->z + player->viewheight; + return; + } + + angle = (FINEANGLES / 20 * leveltime) & FINEMASK; + bob = FixedMul(player->bob / 2, finesine[angle]); + + + // move viewheight + if (player->playerstate == PST_LIVE) + { + player->viewheight += player->deltaviewheight; + + if (player->viewheight > VIEWHEIGHT) + { + player->viewheight = VIEWHEIGHT; + player->deltaviewheight = 0; + } + + if (player->viewheight < VIEWHEIGHT / 2) + { + player->viewheight = VIEWHEIGHT / 2; + if (player->deltaviewheight <= 0) + player->deltaviewheight = 1; + } + + if (player->deltaviewheight) + { + player->deltaviewheight += FRACUNIT / 4; + if (!player->deltaviewheight) + player->deltaviewheight = 1; + } + } + player->viewz = player->mo->z + player->viewheight + bob; + + if (player->viewz > player->mo->ceilingz - 4 * FRACUNIT) + player->viewz = player->mo->ceilingz - 4 * FRACUNIT; +} + + +// +// P_MovePlayer +// +void P_MovePlayer(player_t* player) +{ + ticcmd_t* cmd; + + cmd = &player->cmd; + + player->mo->angle += (cmd->angleturn << 16); + + // Do not let the player control movement + // if not onground. + onground = (player->mo->z <= player->mo->floorz); + + if (cmd->forwardmove && onground) + P_Thrust(player, player->mo->angle, cmd->forwardmove * 2048); + + if (cmd->sidemove && onground) + P_Thrust(player, player->mo->angle - ANG90, cmd->sidemove * 2048); + + if ((cmd->forwardmove || cmd->sidemove) + && player->mo->state == &states[S_PLAY]) + { + P_SetMobjState(player->mo, S_PLAY_RUN1); + } +} + + +// +// P_DeathThink +// Fall on your face when dying. +// Decrease POV height to floor height. +// +void P_DeathThink(player_t* player) +{ + angle_t angle; + angle_t delta; + + P_MovePsprites(player); + + // fall to the ground + if (player->viewheight > 6 * FRACUNIT) + player->viewheight -= FRACUNIT; + + if (player->viewheight < 6 * FRACUNIT) + player->viewheight = 6 * FRACUNIT; + + player->deltaviewheight = 0; + onground = (player->mo->z <= player->mo->floorz); + P_CalcHeight(player); + + if (player->attacker && player->attacker != player->mo) + { + angle = R_PointToAngle2(player->mo->x, + player->mo->y, + player->attacker->x, + player->attacker->y); + + delta = angle - player->mo->angle; + + if (delta < ANG5 || delta >(unsigned) - ANG5) + { + // Looking at killer, + // so fade damage flash down. + player->mo->angle = angle; + + if (player->damagecount) + player->damagecount--; + } + else if (delta < ANG180) + player->mo->angle += ANG5; + else + player->mo->angle -= ANG5; + } + else if (player->damagecount) + player->damagecount--; + + + if (player->cmd.buttons & BT_USE) + player->playerstate = PST_REBORN; +} + + +// +// P_PlayerThink +// +void P_PlayerThink(player_t* player) +{ + ticcmd_t* cmd; + weapontype_t newweapon; + + // fixme: do this in the cheat code + if (player->cheats & CF_NOCLIP) + player->mo->flags |= MF_NOCLIP; + else + player->mo->flags &= ~MF_NOCLIP; + + // chain saw run forward + cmd = &player->cmd; + if (player->mo->flags & MF_JUSTATTACKED) + { + cmd->angleturn = 0; + cmd->forwardmove = 0xc800 / 512; + cmd->sidemove = 0; + player->mo->flags &= ~MF_JUSTATTACKED; + } + + + if (player->playerstate == PST_DEAD) + { + P_DeathThink(player); + return; + } + + // Move around. + // Reactiontime is used to prevent movement + // for a bit after a teleport. + if (player->mo->reactiontime) + player->mo->reactiontime--; + else + P_MovePlayer(player); + + P_CalcHeight(player); + + if (player->mo->subsector->sector->special) + P_PlayerInSpecialSector(player); + + // Check for weapon change. + + // A special event has no other buttons. + if (cmd->buttons & BT_SPECIAL) + cmd->buttons = 0; + + if (cmd->buttons & BT_CHANGE) + { + // The actual changing of the weapon is done + // when the weapon psprite can do it + // (read: not in the middle of an attack). + newweapon = (cmd->buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT; + + if (newweapon == wp_fist + && player->weaponowned[wp_chainsaw] + && !(player->readyweapon == wp_chainsaw + && player->powers[pw_strength])) + { + newweapon = wp_chainsaw; + } + + if ((gamemode == commercial) + && newweapon == wp_shotgun + && player->weaponowned[wp_supershotgun] + && player->readyweapon != wp_supershotgun) + { + newweapon = wp_supershotgun; + } + + + if (player->weaponowned[newweapon] + && newweapon != player->readyweapon) + { + // Do not go to plasma or BFG in shareware, + // even if cheated. + if ((newweapon != wp_plasma + && newweapon != wp_bfg) + || (gamemode != shareware)) + { + player->pendingweapon = newweapon; + } + } + } + + // check for use + if (cmd->buttons & BT_USE) + { + if (!player->usedown) + { + P_UseLines(player); + player->usedown = true; + } + } + else + player->usedown = false; + + // cycle psprites + P_MovePsprites(player); + + // Counters, time dependend power ups. + + // Strength counts up to diminish fade. + if (player->powers[pw_strength]) + player->powers[pw_strength]++; + + if (player->powers[pw_invulnerability]) + player->powers[pw_invulnerability]--; + + if (player->powers[pw_invisibility]) + if (!--player->powers[pw_invisibility]) + player->mo->flags &= ~MF_SHADOW; + + if (player->powers[pw_infrared]) + player->powers[pw_infrared]--; + + if (player->powers[pw_ironfeet]) + player->powers[pw_ironfeet]--; + + if (player->damagecount) + player->damagecount--; + + if (player->bonuscount) + player->bonuscount--; + + + // Handling colormaps. + if (player->powers[pw_invulnerability]) + { + if (player->powers[pw_invulnerability] > 4 * 32 + || (player->powers[pw_invulnerability] & 8)) + player->fixedcolormap = INVERSECOLORMAP; + else + player->fixedcolormap = 0; + } + else if (player->powers[pw_infrared]) + { + if (player->powers[pw_infrared] > 4 * 32 + || (player->powers[pw_infrared] & 8)) + { + // almost full bright + player->fixedcolormap = 1; + } + else + player->fixedcolormap = 0; + } + else + player->fixedcolormap = 0; +} +#define MAXSEGS 32 + + +// +// ClipWallSegment +// Clips the given range of columns +// and includes it in the new clip list. +// +typedef struct +{ + int first; + int last; +} cliprange_t; + + +seg_t* curline; +side_t* sidedef; +line_t* linedef; +sector_t* frontsector; +sector_t* backsector; + +drawseg_t drawsegs[MAXDRAWSEGS]; +drawseg_t* ds_p; + +// newend is one past the last valid seg +cliprange_t* newend; +cliprange_t solidsegs[MAXSEGS]; + +int checkcoord[12][4] = +{ + {3,0,2,1}, + {3,0,2,0}, + {3,1,2,0}, + {0}, + {2,0,2,1}, + {0,0,0,0}, + {3,1,3,0}, + {0}, + {2,0,3,1}, + {2,1,3,1}, + {2,1,3,0} +}; + + +void R_StoreWallRange(int start, int stop); + + +// +// R_ClearDrawSegs +// +void R_ClearDrawSegs(void) +{ + ds_p = drawsegs; +} + + +// +// R_ClipSolidWallSegment +// Does handle solid walls, +// e.g. single sided LineDefs (middle texture) +// that entirely block the view. +// +void R_ClipSolidWallSegment(int first, int last) +{ + cliprange_t* next; + cliprange_t* start; + + // Find the first range that touches the range + // (adjacent pixels are touching). + start = solidsegs; + while (start->last < first - 1) + start++; + + if (first < start->first) + { + if (last < start->first - 1) + { + // Post is entirely visible (above start), + // so insert a new clippost. + R_StoreWallRange(first, last); + next = newend; + newend++; + + while (next != start) + { + *next = *(next - 1); + next--; + } + next->first = first; + next->last = last; + return; + } + + // There is a fragment above *start. + R_StoreWallRange(first, start->first - 1); + // Now adjust the clip size. + start->first = first; + } + + // Bottom contained in start? + if (last <= start->last) + return; + + next = start; + while (last >= (next + 1)->first - 1) + { + // There is a fragment between two posts. + R_StoreWallRange(next->last + 1, (next + 1)->first - 1); + next++; + + if (last <= next->last) + { + // Bottom is contained in next. + // Adjust the clip size. + start->last = next->last; + goto crunch; + } + } + + // There is a fragment after *next. + R_StoreWallRange(next->last + 1, last); + // Adjust the clip size. + start->last = last; + + // 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++ != newend) + { + // Remove a post. + *++start = *next; + } + + newend = start + 1; +} + + +// +// R_ClipPassWallSegment +// Clips the given range of columns, +// but does not includes it in the clip list. +// Does handle windows, +// e.g. LineDefs with upper and lower texture. +// +void R_ClipPassWallSegment(int first, int last) +{ + cliprange_t* start; + + // Find the first range that touches the range + // (adjacent pixels are touching). + start = solidsegs; + while (start->last < first - 1) + start++; + + if (first < start->first) + { + if (last < start->first - 1) + { + // Post is entirely visible (above start). + R_StoreWallRange(first, last); + return; + } + + // There is a fragment above *start. + R_StoreWallRange(first, start->first - 1); + } + + // Bottom contained in start? + if (last <= start->last) + return; + + while (last >= (start + 1)->first - 1) + { + // There is a fragment between two posts. + R_StoreWallRange(start->last + 1, (start + 1)->first - 1); + start++; + + if (last <= start->last) + return; + } + + // There is a fragment after *next. + R_StoreWallRange(start->last + 1, last); +} + + +// +// R_ClearClipSegs +// +void R_ClearClipSegs(void) +{ + solidsegs[0].first = -0x7fffffff; + solidsegs[0].last = -1; + solidsegs[1].first = viewwidth; + solidsegs[1].last = 0x7fffffff; + newend = solidsegs + 2; +} + + +// +// R_AddLine +// Clips the given segment +// and adds any visible pieces to the line list. +// +void R_AddLine(seg_t* line) +{ + int x1; + int x2; + angle_t angle1; + angle_t angle2; + angle_t span; + angle_t tspan; + + curline = line; + + // OPTIMIZE: quickly reject orthogonal back sides. + angle1 = R_PointToAngle(line->v1->x, line->v1->y); + angle2 = R_PointToAngle(line->v2->x, line->v2->y); + + // Clip to view edges. + // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW). + span = angle1 - angle2; + + // Back side? I.e. backface culling? + if (span >= ANG180) + return; + + // Global angle needed by segcalc. + rw_angle1 = angle1; + angle1 -= viewangle; + angle2 -= viewangle; + + tspan = angle1 + clipangle; + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + // Totally off the left edge? + if (tspan >= span) + return; + + angle1 = clipangle; + } + tspan = clipangle - angle2; + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + // Totally off the left edge? + if (tspan >= span) + return; +#pragma warning(push) +#pragma warning(disable : 4146) + angle2 = -clipangle; +#pragma warning(pop) + } + + // The seg is in the view range, + // but not necessarily visible. + angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT; + angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT; + x1 = viewangletox[angle1]; + x2 = viewangletox[angle2]; + + // Does not cross a pixel? + if (x1 == x2) + return; + + backsector = line->backsector; + + // Single sided line? + if (!backsector) + goto clipsolid; + + // Closed door. + if (backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight) + goto clipsolid; + + // Window. + if (backsector->ceilingheight != frontsector->ceilingheight + || backsector->floorheight != frontsector->floorheight) + goto clippass; + + // 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->ceilingpic == frontsector->ceilingpic + && backsector->floorpic == frontsector->floorpic + && backsector->lightlevel == frontsector->lightlevel + && curline->sidedef->midtexture == 0) + { + return; + } + + +clippass: + R_ClipPassWallSegment(x1, x2 - 1); + return; + +clipsolid: + R_ClipSolidWallSegment(x1, x2 - 1); +} + + +// +// R_CheckBBox +// Checks BSP node/subtree bounding box. +// Returns true +// if some part of the bbox might be visible. +// +doom_boolean R_CheckBBox(fixed_t* bspcoord) +{ + int boxx; + int boxy; + int boxpos; + + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + angle_t angle1; + angle_t angle2; + angle_t span; + angle_t tspan; + + cliprange_t* start; + + int sx1; + int sx2; + + // Find the corners of the box + // that define the edges from current viewpoint. + if (viewx <= bspcoord[BOXLEFT]) + boxx = 0; + else if (viewx < bspcoord[BOXRIGHT]) + boxx = 1; + else + boxx = 2; + + if (viewy >= bspcoord[BOXTOP]) + boxy = 0; + else if (viewy > bspcoord[BOXBOTTOM]) + boxy = 1; + else + boxy = 2; + + boxpos = (boxy << 2) + boxx; + if (boxpos == 5) + return true; + + x1 = bspcoord[checkcoord[boxpos][0]]; + y1 = bspcoord[checkcoord[boxpos][1]]; + x2 = bspcoord[checkcoord[boxpos][2]]; + y2 = bspcoord[checkcoord[boxpos][3]]; + + // check clip list for an open space + angle1 = R_PointToAngle(x1, y1) - viewangle; + angle2 = R_PointToAngle(x2, y2) - viewangle; + + span = angle1 - angle2; + + // Sitting on a line? + if (span >= ANG180) + return true; + + tspan = angle1 + clipangle; + + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + // Totally off the left edge? + if (tspan >= span) + return false; + + angle1 = clipangle; + } + tspan = clipangle - angle2; + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + // Totally off the left edge? + if (tspan >= span) + return false; + +#pragma warning(push) +#pragma warning(disable : 4146) + angle2 = -clipangle; +#pragma warning(pop) + } + + + // Find the first clippost + // that touches the source post + // (adjacent pixels are touching). + angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT; + angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT; + sx1 = viewangletox[angle1]; + sx2 = viewangletox[angle2]; + + // Does not cross a pixel. + if (sx1 == sx2) + return false; + sx2--; + + start = solidsegs; + while (start->last < sx2) + start++; + + if (sx1 >= start->first + && sx2 <= start->last) + { + // The clippost contains the new span. + return false; + } + + return true; +} + + +// +// R_Subsector +// Determine floor/ceiling planes. +// Add sprites of things in sector. +// Draw one or more line segments. +// +void R_Subsector(int num) +{ + int count; + seg_t* line; + subsector_t* sub; + +#ifdef RANGECHECK + if (num >= numsubsectors) + { + //I_Error("Error: R_Subsector: ss %i with numss = %i", + // num, + // numsubsectors); + + + doom_strcpy(error_buf, "Error: R_Subsector: ss "); + doom_concat(error_buf, doom_itoa(num, 10)); + doom_concat(error_buf, " with numss = "); + doom_concat(error_buf, doom_itoa(numsubsectors, 10)); + I_Error(error_buf); + } +#endif + + sscount++; + sub = &subsectors[num]; + frontsector = sub->sector; + count = sub->numlines; + line = &segs[sub->firstline]; + + if (frontsector->floorheight < viewz) + { + floorplane = R_FindPlane(frontsector->floorheight, + frontsector->floorpic, + frontsector->lightlevel); + } + else + floorplane = 0; + + if (frontsector->ceilingheight > viewz + || frontsector->ceilingpic == skyflatnum) + { + ceilingplane = R_FindPlane(frontsector->ceilingheight, + frontsector->ceilingpic, + frontsector->lightlevel); + } + else + ceilingplane = 0; + + R_AddSprites(frontsector); + + while (count--) + { + R_AddLine(line); + line++; + } +} + + +// +// RenderBSPNode +// Renders all subsectors below a given node, +// traversing subtree recursively. +// Just call with BSP root. +void R_RenderBSPNode(int bspnum) +{ + node_t* bsp; + int side; + + // Found a subsector? + if (bspnum & NF_SUBSECTOR) + { + if (bspnum == -1) + R_Subsector(0); + else + R_Subsector(bspnum & (~NF_SUBSECTOR)); + return; + } + + bsp = &nodes[bspnum]; + + // Decide which side the view point is on. + side = R_PointOnSide(viewx, viewy, bsp); + + // Recursively divide front space. + R_RenderBSPNode(bsp->children[side]); + + // Possibly divide back space. + if (R_CheckBBox(bsp->bbox[side ^ 1])) + R_RenderBSPNode(bsp->children[side ^ 1]); +} +#if defined(DOOM_WIN32) + +// #define strncasecmp strnicmp +#elif defined(DOOM_APPLE) + +#else +#ifdef LINUX +//#include +#endif +#endif +//#include "r_data.h" + + +// +// Graphics. +// DOOM graphics for walls and sprites +// is stored in vertical runs of opaque pixels (posts). +// A column is composed of zero or more posts, +// a patch or sprite is composed of zero or more columns. +// + + +// +// Texture definition. +// Each texture is composed of one or more patches, +// with patches being lumps stored in the WAD. +// The lumps are referenced by number, and patched +// into the rectangular texture space using origin +// and possibly other attributes. +// +typedef struct +{ + short originx; + short originy; + short patch; + short stepdir; + short colormap; +} mappatch_t; + + +// +// Texture definition. +// A DOOM wall texture is a list of patches +// which are to be combined in a predefined order. +// +typedef struct +{ + char name[8]; + doom_boolean masked; + short width; + short height; + //void **columndirectory; // OBSOLETE + int columndirectory; // [pd] If it's not used, at least make sure it's the right size! Pointers are 8 bytes in x64 + short patchcount; + mappatch_t patches[1]; +} maptexture_t; + + +// A single patch from a texture definition, +// basically a rectangular area within +// the texture rectangle. +typedef struct +{ + // Block origin (allways UL), + // which has allready accounted + // for the internal origin of the patch. + int originx; + int originy; + int patch; +} texpatch_t; + + +// A maptexturedef_t describes a rectangular texture, +// which is composed of one or more mappatch_t structures +// that arrange graphic patches. +typedef struct +{ + // Keep name for switch changing, etc. + char name[8]; + short width; + short height; + + // All the patches[patchcount] + // are drawn back to front into the cached texture. + short patchcount; + texpatch_t patches[1]; +} texture_t; + + +int firstflat; +int lastflat; +int numflats; + +int firstpatch; +int lastpatch; +int numpatches; + +int firstspritelump; +int lastspritelump; +int numspritelumps; + +int numtextures; +texture_t** textures; + +int* texturewidthmask; +// needed for texture pegging +fixed_t* textureheight; +int* texturecompositesize; +short** texturecolumnlump; +unsigned short** texturecolumnofs; +byte** texturecomposite; + +// for global animation +int* flattranslation; +int* texturetranslation; + +// needed for pre rendering +fixed_t* spritewidth; +fixed_t* spriteoffset; +fixed_t* spritetopoffset; + +lighttable_t* colormaps; + +int flatmemory; +int texturememory; +int spritememory; + + +// +// MAPTEXTURE_T CACHING +// When a texture is first needed, +// it counts the number of composite columns +// required in the texture and allocates space +// for a column directory and any new columns. +// The directory will simply point inside other patches +// if there is only one patch in a given column, +// but any columns with multiple patches +// will have new column_ts generated. +// + + +// +// R_DrawColumnInCache +// Clip and draw a column +// from a patch into a cached post. +// +void R_DrawColumnInCache(column_t* patch, byte* cache, int originy, int cacheheight) +{ + int count; + int position; + byte* source; + byte* dest; + + dest = (byte*)cache + 3; + + while (patch->topdelta != 0xff) + { + source = (byte*)patch + 3; + count = patch->length; + position = originy + patch->topdelta; + + if (position < 0) + { + count += position; + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + if (count > 0) + doom_memcpy(cache + position, source, count); + + patch = (column_t*)((byte*)patch + patch->length + 4); + } +} + + +// +// R_GenerateComposite +// Using the texture definition, +// the composite texture is created from the patches, +// and each column is cached. +// +void R_GenerateComposite(int texnum) +{ + byte* block; + texture_t* texture; + texpatch_t* patch; + patch_t* realpatch; + int x; + int x1; + int x2; + int i; + column_t* patchcol; + short* collump; + unsigned short* colofs; + + texture = textures[texnum]; + + block = Z_Malloc(texturecompositesize[texnum], + PU_STATIC, + &texturecomposite[texnum]); + + collump = texturecolumnlump[texnum]; + colofs = texturecolumnofs[texnum]; + + // Composite the columns together. + patch = texture->patches; + + for (i = 0, patch = texture->patches; + i < texture->patchcount; + i++, patch++) + { + realpatch = W_CacheLumpNum(patch->patch, PU_CACHE); + x1 = patch->originx; + x2 = x1 + SHORT(realpatch->width); + + if (x1 < 0) + x = 0; + else + x = x1; + + if (x2 > texture->width) + x2 = texture->width; + + for (; x < x2; x++) + { + // Column does not have multiple patches? + if (collump[x] >= 0) + continue; + + patchcol = (column_t*)((byte*)realpatch + + LONG(realpatch->columnofs[x - x1])); + R_DrawColumnInCache(patchcol, + block + colofs[x], + patch->originy, + texture->height); + } + } + + // Now that the texture has been built in column cache, + // it is purgable from zone memory. + Z_ChangeTag(block, PU_CACHE); +} + + +// +// R_GenerateLookup +// +void R_GenerateLookup(int texnum) +{ + texture_t* texture; + byte* patchcount; // patchcount[texture->width] + texpatch_t* patch; + patch_t* realpatch; + int x; + int x1; + int x2; + int i; + short* collump; + unsigned short* colofs; + + texture = textures[texnum]; + + // Composited texture not created yet. + texturecomposite[texnum] = 0; + + texturecompositesize[texnum] = 0; + collump = texturecolumnlump[texnum]; + colofs = texturecolumnofs[texnum]; + + // Now count the number of columns + // that are covered by more than one patch. + // Fill in the lump / offset, so columns + // with only a single patch are all done. + patchcount = (byte*)doom_malloc(texture->width); + doom_memset(patchcount, 0, texture->width); + patch = texture->patches; + + for (i = 0, patch = texture->patches; + i < texture->patchcount; + i++, patch++) + { + realpatch = W_CacheLumpNum(patch->patch, PU_CACHE); + x1 = patch->originx; + x2 = x1 + SHORT(realpatch->width); + + if (x1 < 0) + x = 0; + else + x = x1; + + if (x2 > texture->width) + x2 = texture->width; + for (; x < x2; x++) + { + patchcount[x]++; + collump[x] = patch->patch; + colofs[x] = LONG(realpatch->columnofs[x - x1]) + 3; + } + } + + for (x = 0; x < texture->width; x++) + { + if (!patchcount[x]) + { + //doom_print("R_GenerateLookup: column without a patch (%s)\n", + // texture->name); + doom_print("R_GenerateLookup: column without a patch ("); + doom_print(texture->name); + doom_print(")\n"); + return; + } + // I_Error ("R_GenerateLookup: column without a patch"); + + if (patchcount[x] > 1) + { + // Use the cached block. + collump[x] = -1; + colofs[x] = texturecompositesize[texnum]; + + if (texturecompositesize[texnum] > 0x10000 - texture->height) + { + //I_Error("Error: R_GenerateLookup: texture %i is >64k", + // texnum); + + doom_strcpy(error_buf, "Error: R_GenerateLookup: texture "); + doom_concat(error_buf, doom_itoa(texnum, 10)); + doom_concat(error_buf, " is >64k"); + I_Error(error_buf); + } + + texturecompositesize[texnum] += texture->height; + } + } + + doom_free(patchcount); +} + + +// +// R_GetColumn +// +byte* R_GetColumn(int tex, int col) +{ + int lump; + int ofs; + + col &= texturewidthmask[tex]; + lump = texturecolumnlump[tex][col]; + ofs = texturecolumnofs[tex][col]; + + if (lump > 0) + return (byte*)W_CacheLumpNum(lump, PU_CACHE) + ofs; + + if (!texturecomposite[tex]) + R_GenerateComposite(tex); + + return texturecomposite[tex] + ofs; +} + + +// +// R_InitTextures +// Initializes the texture list +// with the textures from the world map. +// +void R_InitTextures(void) +{ + maptexture_t* mtexture; + texture_t* texture; + mappatch_t* mpatch; + texpatch_t* patch; + + int i; + int j; + + int* maptex; + int* maptex2; + int* maptex1; + + char name[9]; + char* names; + char* name_p; + + int* patchlookup; + + int totalwidth; + int nummappatches; + int offset; + int maxoff; + int maxoff2; + int numtextures1; + int numtextures2; + + int* directory; + + int temp1; + int temp2; + int temp3; + + // Load the patch names from pnames.lmp. + name[8] = 0; + names = W_CacheLumpName("PNAMES", PU_STATIC); + nummappatches = LONG(*((int*)names)); + name_p = names + 4; + patchlookup = doom_malloc(nummappatches * sizeof(*patchlookup)); + + for (i = 0; i < nummappatches; i++) + { + doom_strncpy(name, name_p + i * 8, 8); + patchlookup[i] = W_CheckNumForName(name); + } + Z_Free(names); + + // Load the map texture definitions from textures.lmp. + // The data is contained in one or two lumps, + // TEXTURE1 for shareware, plus TEXTURE2 for commercial. + maptex = maptex1 = W_CacheLumpName("TEXTURE1", PU_STATIC); + numtextures1 = LONG(*maptex); + maxoff = W_LumpLength(W_GetNumForName("TEXTURE1")); + directory = maptex + 1; + + if (W_CheckNumForName("TEXTURE2") != -1) + { + maptex2 = W_CacheLumpName("TEXTURE2", PU_STATIC); + numtextures2 = LONG(*maptex2); + maxoff2 = W_LumpLength(W_GetNumForName("TEXTURE2")); + } + else + { + maptex2 = 0; + numtextures2 = 0; + maxoff2 = 0; + } + numtextures = numtextures1 + numtextures2; + + textures = Z_Malloc(numtextures * sizeof(texture_t*), PU_STATIC, 0); + texturecolumnlump = Z_Malloc(numtextures * sizeof(short*), PU_STATIC, 0); + texturecolumnofs = Z_Malloc(numtextures * sizeof(unsigned short*), PU_STATIC, 0); + texturecomposite = Z_Malloc(numtextures * sizeof(byte*), PU_STATIC, 0); + texturecompositesize = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0); + texturewidthmask = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0); + textureheight = Z_Malloc(numtextures * sizeof(fixed_t), PU_STATIC, 0); + + totalwidth = 0; + + // Really complex printing shit... + temp1 = W_GetNumForName("S_START"); // P_??????? + temp2 = W_GetNumForName("S_END") - 1; + temp3 = ((temp2 - temp1 + 63) / 64) + ((numtextures + 63) / 64); + doom_print("["); + for (i = 0; i < temp3; i++) + doom_print(" "); + doom_print(" ]"); + for (i = 0; i < temp3; i++) + doom_print("\x8"); + doom_print("\x8\x8\x8\x8\x8\x8\x8\x8\x8\x8"); + + for (i = 0; i < numtextures; i++, directory++) + { + if (!(i & 63)) + doom_print("."); + + if (i == numtextures1) + { + // Start looking in second texture file. + maptex = maptex2; + maxoff = maxoff2; + directory = maptex + 1; + } + + offset = LONG(*directory); + + if (offset > maxoff) + I_Error("Error: R_InitTextures: bad texture directory"); + + mtexture = (maptexture_t*)((byte*)maptex + offset); + + texture = textures[i] = + Z_Malloc(sizeof(texture_t) + + sizeof(texpatch_t) * (SHORT(mtexture->patchcount) - 1), + PU_STATIC, 0); + + texture->width = SHORT(mtexture->width); + texture->height = SHORT(mtexture->height); + texture->patchcount = SHORT(mtexture->patchcount); + + doom_memcpy(texture->name, mtexture->name, sizeof(texture->name)); + mpatch = &mtexture->patches[0]; + patch = &texture->patches[0]; + + for (j = 0; j < texture->patchcount; j++, mpatch++, patch++) + { + patch->originx = SHORT(mpatch->originx); + patch->originy = SHORT(mpatch->originy); + patch->patch = patchlookup[SHORT(mpatch->patch)]; + if (patch->patch == -1) + { + //I_Error("Error: R_InitTextures: Missing patch in texture %s", + // texture->name); + + doom_strcpy(error_buf, "Error: R_InitTextures: Missing patch in texture "); + doom_concat(error_buf, texture->name); + I_Error(error_buf); + } + } + texturecolumnlump[i] = Z_Malloc(texture->width * sizeof(short), PU_STATIC, 0); + texturecolumnofs[i] = Z_Malloc(texture->width * sizeof(unsigned short), PU_STATIC, 0); + + j = 1; + while (j * 2 <= texture->width) + j <<= 1; + + texturewidthmask[i] = j - 1; + textureheight[i] = texture->height << FRACBITS; + + totalwidth += texture->width; + } + + Z_Free(maptex1); + if (maptex2) + Z_Free(maptex2); + + // Precalculate whatever possible. + for (i = 0; i < numtextures; i++) + R_GenerateLookup(i); + + // Create translation table for global animation. + texturetranslation = Z_Malloc((numtextures + 1) * sizeof(int), PU_STATIC, 0); + + for (i = 0; i < numtextures; i++) + texturetranslation[i] = i; + + doom_free(patchlookup); +} + + +// +// R_InitFlats +// +void R_InitFlats(void) +{ + int i; + + firstflat = W_GetNumForName("F_START") + 1; + lastflat = W_GetNumForName("F_END") - 1; + numflats = lastflat - firstflat + 1; + + // Create translation table for global animation. + flattranslation = Z_Malloc((numflats + 1) * sizeof(int), PU_STATIC, 0); + + for (i = 0; i < numflats; i++) + flattranslation[i] = i; +} + + +// +// R_InitSpriteLumps +// Finds the width and hoffset of all sprites in the wad, +// so the sprite does not need to be cached completely +// just for having the header info ready during rendering. +// +void R_InitSpriteLumps(void) +{ + int i; + patch_t* patch; + + firstspritelump = W_GetNumForName("S_START") + 1; + lastspritelump = W_GetNumForName("S_END") - 1; + + numspritelumps = lastspritelump - firstspritelump + 1; + spritewidth = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); + spriteoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); + spritetopoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); + + for (i = 0; i < numspritelumps; i++) + { + if (!(i & 63)) + doom_print("."); + + patch = W_CacheLumpNum(firstspritelump + i, PU_CACHE); + spritewidth[i] = SHORT(patch->width) << FRACBITS; + spriteoffset[i] = SHORT(patch->leftoffset) << FRACBITS; + spritetopoffset[i] = SHORT(patch->topoffset) << FRACBITS; + } +} + + +// +// R_InitColormaps +// +void R_InitColormaps(void) +{ + int lump, length; + + // Load in the light tables, + // 256 byte align tables. + lump = W_GetNumForName("COLORMAP"); + length = W_LumpLength(lump) + 255; + colormaps = Z_Malloc(length, PU_STATIC, 0); + colormaps = (byte*)(((unsigned long long)colormaps + 255) & ~0xff); + W_ReadLump(lump, colormaps); +} + + +// +// R_InitData +// Locates all the lumps +// that will be used by all views +// Must be called after W_Init. +// +void R_InitData(void) +{ + R_InitTextures(); + doom_print("\nInitTextures"); + R_InitFlats(); + doom_print("\nInitFlats"); + R_InitSpriteLumps(); + doom_print("\nInitSprites"); + R_InitColormaps(); + doom_print("\nInitColormaps"); +} + + +// +// R_FlatNumForName +// Retrieval, get a flat number for a flat name. +// +int R_FlatNumForName(char* name) +{ + int i; + char namet[9]; + + i = W_CheckNumForName(name); + + if (i == -1) + { + namet[8] = 0; + doom_memcpy(namet, name, 8); + //I_Error("Error: R_FlatNumForName: %s not found", namet); + + doom_strcpy(error_buf, "Error: R_FlatNumForName: "); + doom_concat(error_buf, namet); + doom_concat(error_buf, " not found"); + I_Error(error_buf); + } + return i - firstflat; +} + + +// +// R_CheckTextureNumForName +// Check whether texture is available. +// Filter out NoTexture indicator. +// +int R_CheckTextureNumForName(char* name) +{ + int i; + + // "NoTexture" marker. + if (name[0] == '-') + return 0; + + for (i = 0; i < numtextures; i++) + if (!doom_strncasecmp(textures[i]->name, name, 8)) + return i; + + return -1; +} + + +// +// R_TextureNumForName +// Calls R_CheckTextureNumForName, +// aborts with error message. +// +int R_TextureNumForName(char* name) +{ + int i; + + i = R_CheckTextureNumForName(name); + + if (i == -1) + { + //I_Error("Error: R_TextureNumForName: %s not found", + // name); + + doom_strcpy(error_buf, "Error: R_TextureNumForName: "); + doom_concat(error_buf, name); + doom_concat(error_buf, " not found"); + I_Error(error_buf); + } + return i; +} + + +// +// R_PrecacheLevel +// Preloads all relevant graphics for the level. +// +void R_PrecacheLevel(void) +{ + char* flatpresent; + char* texturepresent; + char* spritepresent; + + int i; + int j; + int k; + int lump; + + texture_t* texture; + thinker_t* th; + spriteframe_t* sf; + + if (demoplayback) + return; + + // Precache flats. + flatpresent = doom_malloc(numflats); + doom_memset(flatpresent, 0, numflats); + + for (i = 0; i < numsectors; i++) + { + flatpresent[sectors[i].floorpic] = 1; + flatpresent[sectors[i].ceilingpic] = 1; + } + + flatmemory = 0; + + for (i = 0; i < numflats; i++) + { + if (flatpresent[i]) + { + lump = firstflat + i; + flatmemory += lumpinfo[lump].size; + W_CacheLumpNum(lump, PU_CACHE); + } + } + + // Precache textures. + texturepresent = doom_malloc(numtextures); + doom_memset(texturepresent, 0, numtextures); + + for (i = 0; i < numsides; i++) + { + texturepresent[sides[i].toptexture] = 1; + texturepresent[sides[i].midtexture] = 1; + texturepresent[sides[i].bottomtexture] = 1; + } + + // Sky texture is always present. + // Note that F_SKY1 is the name used to + // indicate a sky floor/ceiling as a flat, + // while the sky texture is stored like + // a wall texture, with an episode dependend + // name. + texturepresent[skytexture] = 1; + + texturememory = 0; + for (i = 0; i < numtextures; i++) + { + if (!texturepresent[i]) + continue; + + texture = textures[i]; + + for (j = 0; j < texture->patchcount; j++) + { + lump = texture->patches[j].patch; + texturememory += lumpinfo[lump].size; + W_CacheLumpNum(lump, PU_CACHE); + } + } + + // Precache sprites. + spritepresent = doom_malloc(numsprites); + doom_memset(spritepresent, 0, numsprites); + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_MobjThinker) + spritepresent[((mobj_t*)th)->sprite] = 1; + } + + spritememory = 0; + for (i = 0; i < numsprites; i++) + { + if (!spritepresent[i]) + continue; + + for (j = 0; j < sprites[i].numframes; j++) + { + sf = &sprites[i].spriteframes[j]; + for (k = 0; k < 8; k++) + { + lump = firstspritelump + sf->lump[k]; + spritememory += lumpinfo[lump].size; + W_CacheLumpNum(lump, PU_CACHE); + } + } + } + + doom_free(texturepresent); + doom_free(flatpresent); + doom_free(spritepresent); +} +#define MAXWIDTH 1120 +#define MAXHEIGHT 832 + +// status bar height at bottom of screen +#define SBARHEIGHT 32 + +#define FUZZTABLE 50 +#define FUZZOFF (SCREENWIDTH) + + +// +// All drawing to the view buffer is accomplished in this file. +// The other refresh files only know about ccordinates, +// not the architecture of the frame buffer. +// Conveniently, the frame buffer is a linear one, +// and we need only the base address, +// and the total size == width*height*depth/8., +// + +byte* viewimage; +int viewwidth; +int scaledviewwidth; +int viewheight; +int viewwindowx; +int viewwindowy; +byte* ylookup[MAXHEIGHT]; +int columnofs[MAXWIDTH]; + +// Color tables for different players, +// translate a limited part to another +// (color ramps used for suit colors). +// +byte translations[3][256]; + +// +// R_DrawColumn +// Source is the top of the column to scale. +// +lighttable_t* dc_colormap; +int dc_x; +int dc_yl; +int dc_yh; +fixed_t dc_iscale; +fixed_t dc_texturemid; + +// first pixel in a column (possibly virtual) +byte* dc_source; + +// just for profiling +int dccount; + +int fuzzoffset[FUZZTABLE] = +{ + FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF +}; + +int fuzzpos = 0; + +byte* dc_translation; +byte* translationtables; + + +// +// A column is a vertical slice/span from a wall texture that, +// given the DOOM style restrictions on the view orientation, +// will always have constant z depth. +// Thus a special case loop for very fast rendering can +// be used. It has also been used with Wolfenstein 3D. +// +void R_DrawColumn(void) +{ + int count; + byte* dest; + fixed_t frac; + fixed_t fracstep; + + count = dc_yh - dc_yl; + + // Zero length, column does not exceed a pixel. + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH + || dc_yl < 0 + || dc_yh >= SCREENHEIGHT) + { + //I_Error("Error: R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); + + doom_strcpy(error_buf, "Error: R_DrawColumn: "); + doom_concat(error_buf, doom_itoa(dc_yl, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(dc_yh, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(dc_x, 10)); + I_Error(error_buf); + } +#endif + + // Framebuffer destination address. + // Use ylookup LUT to avoid multiply with ScreenWidth. + // Use columnofs LUT for subwindows? + dest = ylookup[dc_yl] + columnofs[dc_x]; + + // Determine scaling, + // which is the only mapping to be done. + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + // Inner loop that does the actual texture mapping, + // e.g. a DDA-lile scaling. + // This is as fast as it gets. + do + { + // Re-map color indices from wall texture column + // using a lighting/special effects LUT. + *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]]; + + dest += SCREENWIDTH; + frac += fracstep; + + } while (count--); +} + + +void R_DrawColumnLow(void) +{ + int count; + byte* dest; + byte* dest2; + fixed_t frac; + fixed_t fracstep; + + count = dc_yh - dc_yl; + + // Zero length. + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH + || dc_yl < 0 + || dc_yh >= SCREENHEIGHT) + { + //I_Error("Error: R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); + + doom_strcpy(error_buf, "Error: R_DrawColumn: "); + doom_concat(error_buf, doom_itoa(dc_yl, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(dc_yh, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(dc_x, 10)); + I_Error(error_buf); + } +#endif + // Blocky mode, need to multiply by 2. + dc_x <<= 1; + + dest = ylookup[dc_yl] + columnofs[dc_x]; + dest2 = ylookup[dc_yl] + columnofs[dc_x + 1]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + do + { + // Hack. Does not work corretly. + *dest2 = *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]]; + dest += SCREENWIDTH; + dest2 += SCREENWIDTH; + frac += fracstep; + + } while (count--); +} + + +// +// Spectre/Invisibility. +// + +// +// Framebuffer postprocessing. +// Creates a fuzzy image by copying pixels +// from adjacent ones to left and right. +// Used with an all black colormap, this +// could create the SHADOW effect, +// i.e. spectres and invisible players. +// +void R_DrawFuzzColumn(void) +{ + int count; + byte* dest; + fixed_t frac; + fixed_t fracstep; + + // Adjust borders. Low... + if (!dc_yl) + dc_yl = 1; + + // .. and high. + if (dc_yh == viewheight - 1) + dc_yh = viewheight - 2; + + count = dc_yh - dc_yl; + + // Zero length. + if (count < 0) + return; + + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH + || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + //I_Error("Error: R_DrawFuzzColumn: %i to %i at %i", + // dc_yl, dc_yh, dc_x); + + doom_strcpy(error_buf, "Error: R_DrawFuzzColumn: "); + doom_concat(error_buf, doom_itoa(dc_yl, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(dc_yh, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(dc_x, 10)); + I_Error(error_buf); + } +#endif + + // Does not work with blocky mode. + dest = ylookup[dc_yl] + columnofs[dc_x]; + + // Looks familiar. + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + // Looks like an attempt at dithering, + // using the colormap #6 (of 0-31, a bit + // brighter than average). + do + { + // Lookup framebuffer, and retrieve + // a pixel that is either one column + // left or right of the current one. + // Add index from colormap to index. + *dest = colormaps[6 * 256 + dest[fuzzoffset[fuzzpos]]]; + + // Clamp table lookup index. + if (++fuzzpos == FUZZTABLE) + fuzzpos = 0; + + dest += SCREENWIDTH; + + frac += fracstep; + } while (count--); +} + + +// +// R_DrawTranslatedColumn +// Used to draw player sprites +// with the green colorramp mapped to others. +// Could be used with different translation +// tables, e.g. the lighter colored version +// of the BaronOfHell, the HellKnight, uses +// identical sprites, kinda brightened up. +// +void R_DrawTranslatedColumn(void) +{ + int count; + byte* dest; + fixed_t frac; + fixed_t fracstep; + + count = dc_yh - dc_yl; + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH + || dc_yl < 0 + || dc_yh >= SCREENHEIGHT) + { + //I_Error("Error: R_DrawColumn: %i to %i at %i", + // dc_yl, dc_yh, dc_x); + + doom_strcpy(error_buf, "Error: R_DrawColumn: "); + doom_concat(error_buf, doom_itoa(dc_yl, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(dc_yh, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(dc_x, 10)); + I_Error(error_buf); + } +#endif + + // FIXME. As above. + dest = ylookup[dc_yl] + columnofs[dc_x]; + + // Looks familiar. + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + // Here we do an additional index re-mapping. + do + { + // Translation tables are used + // to map certain colorramps to other ones, + // used with PLAY sprites. + // Thus the "green" ramp of the player 0 sprite + // is mapped to gray, red, black/indigo. + *dest = dc_colormap[dc_translation[dc_source[frac >> FRACBITS]]]; + dest += SCREENWIDTH; + + frac += fracstep; + } while (count--); +} + + +// +// R_InitTranslationTables +// Creates the translation tables to map +// the green color ramp to gray, brown, red. +// Assumes a given structure of the PLAYPAL. +// Could be read from a lump instead. +// +void R_InitTranslationTables(void) +{ + int i; + + translationtables = Z_Malloc(256 * 3 + 255, PU_STATIC, 0); + translationtables = (byte*)(((unsigned long long)translationtables + 255) & ~255); + + // translate just the 16 green colors + for (i = 0; i < 256; i++) + { + if (i >= 0x70 && i <= 0x7f) + { + // map green ramp to gray, brown, red + translationtables[i] = 0x60 + (i & 0xf); + translationtables[i + 256] = 0x40 + (i & 0xf); + translationtables[i + 512] = 0x20 + (i & 0xf); + } + else + { + // Keep all other colors as is. + translationtables[i] = translationtables[i + 256] + = translationtables[i + 512] = i; + } + } +} + + +// +// R_DrawSpan +// With DOOM style restrictions on view orientation, +// the floors and ceilings consist of horizontal slices +// or spans with constant z depth. +// However, rotation around the world z axis is possible, +// thus this mapping, while simpler and faster than +// perspective correct texture mapping, has to traverse +// the texture at an angle in all but a few cases. +// In consequence, flats are not stored by column (like walls), +// and the inner loop has to step in texture space u and v. +// +int ds_y; +int ds_x1; +int ds_x2; + +lighttable_t* ds_colormap; + +fixed_t ds_xfrac; +fixed_t ds_yfrac; +fixed_t ds_xstep; +fixed_t ds_ystep; + +// start of a 64*64 tile image +byte* ds_source; + +// just for profiling +int dscount; + + +// +// Draws the actual span. +void R_DrawSpan(void) +{ + fixed_t xfrac; + fixed_t yfrac; + byte* dest; + int count; + int spot; + +#ifdef RANGECHECK + if (ds_x2 < ds_x1 + || ds_x1<0 + || ds_x2 >= SCREENWIDTH + || (unsigned)ds_y>SCREENHEIGHT) + { + //I_Error("Error: R_DrawSpan: %i to %i at %i", + // ds_x1, ds_x2, ds_y); + + doom_strcpy(error_buf, "Error: R_DrawSpan: "); + doom_concat(error_buf, doom_itoa(ds_x1, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(ds_x2, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(ds_y, 10)); + I_Error(error_buf); + } +#endif + + xfrac = ds_xfrac; + yfrac = ds_yfrac; + + dest = ylookup[ds_y] + columnofs[ds_x1]; + + // We do not check for zero spans here? + count = ds_x2 - ds_x1; + + do + { + // Current texture index in u,v. + spot = ((yfrac >> (16 - 6)) & (63 * 64)) + ((xfrac >> 16) & 63); + + // Lookup pixel from flat texture tile, + // re-index using light/colormap. + *dest++ = ds_colormap[ds_source[spot]]; + + // Next step in u,v. + xfrac += ds_xstep; + yfrac += ds_ystep; + + } while (count--); +} + + +// +// Again.. +// +void R_DrawSpanLow(void) +{ + fixed_t xfrac; + fixed_t yfrac; + byte* dest; + int count; + int spot; + +#ifdef RANGECHECK + if (ds_x2 < ds_x1 + || ds_x1<0 + || ds_x2 >= SCREENWIDTH + || (unsigned)ds_y>SCREENHEIGHT) + { + //I_Error("Error: R_DrawSpan: %i to %i at %i", + // ds_x1, ds_x2, ds_y); + + + doom_strcpy(error_buf, "Error: R_DrawSpan: "); + doom_concat(error_buf, doom_itoa(ds_x1, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(ds_x2, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(ds_y, 10)); + I_Error(error_buf); + } +#endif + + xfrac = ds_xfrac; + yfrac = ds_yfrac; + + // Blocky mode, need to multiply by 2. + ds_x1 <<= 1; + ds_x2 <<= 1; + + dest = ylookup[ds_y] + columnofs[ds_x1]; + + count = ds_x2 - ds_x1; + do + { + spot = ((yfrac >> (16 - 6)) & (63 * 64)) + ((xfrac >> 16) & 63); + // Lowres/blocky mode does it twice, + // while scale is adjusted appropriately. + *dest++ = ds_colormap[ds_source[spot]]; + *dest++ = ds_colormap[ds_source[spot]]; + + xfrac += ds_xstep; + yfrac += ds_ystep; + + } while (count--); +} + + +// +// R_InitBuffer +// Creats lookup tables that avoid +// multiplies and other hazzles +// for getting the framebuffer address +// of a pixel to draw. +// +void R_InitBuffer(int width, int height) +{ + int i; + + // Handle resize, + // e.g. smaller view windows + // with border and/or status bar. + viewwindowx = (SCREENWIDTH - width) >> 1; + + // Column offset. For windows. + for (i = 0; i < width; i++) + columnofs[i] = viewwindowx + i; + + // Samw with base row offset. + if (width == SCREENWIDTH) + viewwindowy = 0; + else + viewwindowy = (SCREENHEIGHT - SBARHEIGHT - height) >> 1; + + // Preclaculate all row offsets. + for (i = 0; i < height; i++) + ylookup[i] = screens[0] + (i + viewwindowy) * SCREENWIDTH; +} + + +// +// R_FillBackScreen +// Fills the back screen with a pattern +// for variable screen sizes +// Also draws a beveled edge. +// +void R_FillBackScreen(void) +{ + byte* src; + byte* dest; + int x; + int y; + patch_t* patch; + + // DOOM border patch. + char name1[] = "FLOOR7_2"; + + // DOOM II border patch. + char name2[] = "GRNROCK"; + + char* name; + + if (scaledviewwidth == 320) + return; + + if (gamemode == commercial) + name = name2; + else + name = name1; + + src = W_CacheLumpName(name, PU_CACHE); + dest = screens[1]; + + for (y = 0; y < SCREENHEIGHT - SBARHEIGHT; y++) + { + for (x = 0; x < SCREENWIDTH / 64; x++) + { + doom_memcpy(dest, src + ((y & 63) << 6), 64); + dest += 64; + } + + if (SCREENWIDTH & 63) + { + doom_memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); + dest += (SCREENWIDTH & 63); + } + } + + patch = W_CacheLumpName("brdr_t", PU_CACHE); + + for (x = 0; x < scaledviewwidth; x += 8) + V_DrawPatch(viewwindowx + x, viewwindowy - 8, 1, patch); + patch = W_CacheLumpName("brdr_b", PU_CACHE); + + for (x = 0; x < scaledviewwidth; x += 8) + V_DrawPatch(viewwindowx + x, viewwindowy + viewheight, 1, patch); + patch = W_CacheLumpName("brdr_l", PU_CACHE); + + for (y = 0; y < viewheight; y += 8) + V_DrawPatch(viewwindowx - 8, viewwindowy + y, 1, patch); + patch = W_CacheLumpName("brdr_r", PU_CACHE); + + for (y = 0; y < viewheight; y += 8) + V_DrawPatch(viewwindowx + scaledviewwidth, viewwindowy + y, 1, patch); + + + // Draw beveled edge. + V_DrawPatch(viewwindowx - 8, + viewwindowy - 8, + 1, + W_CacheLumpName("brdr_tl", PU_CACHE)); + + V_DrawPatch(viewwindowx + scaledviewwidth, + viewwindowy - 8, + 1, + W_CacheLumpName("brdr_tr", PU_CACHE)); + + V_DrawPatch(viewwindowx - 8, + viewwindowy + viewheight, + 1, + W_CacheLumpName("brdr_bl", PU_CACHE)); + + V_DrawPatch(viewwindowx + scaledviewwidth, + viewwindowy + viewheight, + 1, + W_CacheLumpName("brdr_br", PU_CACHE)); +} + + +// +// Copy a screen buffer. +// +void R_VideoErase(unsigned ofs, int count) +{ + // LFB copy. + // This might not be a good idea if memcpy + // is not optiomal, e.g. byte by byte on + // a 32bit CPU, as GNU GCC/Linux libc did + // at one point. + doom_memcpy(screens[0] + ofs, screens[1] + ofs, count); +} + + +// +// R_DrawViewBorder +// Draws the border around the view +// for different size windows? +// +void V_MarkRect(int x, int y, int width, int height); + +void R_DrawViewBorder(void) +{ + int top; + int side; + int ofs; + int i; + + if (scaledviewwidth == SCREENWIDTH) + return; + + top = ((SCREENHEIGHT - SBARHEIGHT) - viewheight) / 2; + side = (SCREENWIDTH - scaledviewwidth) / 2; + + // copy top and one line of left side + R_VideoErase(0, top * SCREENWIDTH + side); + + // copy one line of right side and bottom + ofs = (viewheight + top) * SCREENWIDTH - side; + R_VideoErase(ofs, top * SCREENWIDTH + side); + + // copy sides using wraparound + ofs = top * SCREENWIDTH + SCREENWIDTH - side; + side <<= 1; + + for (i = 1; i < viewheight; i++) + { + R_VideoErase(ofs, side); + ofs += SCREENWIDTH; + } + + // ? + V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT - SBARHEIGHT); +} +#define FIELDOFVIEW 2048 // Fineangles in the SCREENWIDTH wide window. +#define DISTMAP 2 + + +int viewangleoffset; + +// increment every time a check is made +int validcount = 1; + +lighttable_t* fixedcolormap; + +int centerx; +int centery; + +fixed_t centerxfrac; +fixed_t centeryfrac; +fixed_t projection; + +// just for profiling purposes +int framecount; + +int sscount; +int linecount; +int loopcount; + +fixed_t viewx; +fixed_t viewy; +fixed_t viewz; + +angle_t viewangle; + +fixed_t viewcos; +fixed_t viewsin; + +player_t* viewplayer; + +// 0 = high, 1 = low +int detailshift; + +// +// precalculated math tables +// +angle_t clipangle; + +// The viewangletox[viewangle + FINEANGLES/4] lookup +// maps the visible view angles to screen X coordinates, +// flattening the arc to a flat projection plane. +// There will be many angles mapped to the same X. +int viewangletox[FINEANGLES / 2]; + +// The xtoviewangleangle[] table maps a screen pixel +// to the lowest viewangle that maps back to x ranges +// from clipangle to -clipangle. +angle_t xtoviewangle[SCREENWIDTH + 1]; + +fixed_t* finecosine = &finesine[FINEANGLES / 4]; + +lighttable_t* scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; +lighttable_t* scalelightfixed[MAXLIGHTSCALE]; +lighttable_t* zlight[LIGHTLEVELS][MAXLIGHTZ]; + +// bumped light from gun blasts +int extralight; + +doom_boolean setsizeneeded; +int setblocks; +int setdetail; + + +extern lighttable_t** walllights; +extern int detailLevel; +extern int screenblocks; + + +void (*colfunc) (void); +void (*basecolfunc) (void); +void (*fuzzcolfunc) (void); +void (*transcolfunc) (void); +void (*spanfunc) (void); + + +// +// R_AddPointToBox +// Expand a given bbox +// so that it encloses a given point. +// +void R_AddPointToBox(int x, int y, fixed_t* box) +{ + if (x < box[BOXLEFT]) + box[BOXLEFT] = x; + if (x > box[BOXRIGHT]) + box[BOXRIGHT] = x; + if (y < box[BOXBOTTOM]) + box[BOXBOTTOM] = y; + if (y > box[BOXTOP]) + box[BOXTOP] = y; +} + + +// +// R_PointOnSide +// Traverse BSP (sub) tree, +// check point against partition plane. +// Returns side 0 (front) or 1 (back). +// +int R_PointOnSide(fixed_t x, fixed_t y, node_t* node) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!node->dx) + { + if (x <= node->x) + return node->dy > 0; + + return node->dy < 0; + } + if (!node->dy) + { + if (y <= node->y) + return node->dx < 0; + + return node->dx > 0; + } + + dx = (x - node->x); + dy = (y - node->y); + + // Try to quickly decide by looking at sign bits. + if ((node->dy ^ node->dx ^ dx ^ dy) & 0x80000000) + { + if ((node->dy ^ dx) & 0x80000000) + { + // (left is negative) + return 1; + } + return 0; + } + + left = FixedMul(node->dy >> FRACBITS, dx); + right = FixedMul(dy, node->dx >> FRACBITS); + + if (right < left) + { + // front side + return 0; + } + // back side + return 1; +} + + +int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t* line) +{ + fixed_t lx; + fixed_t ly; + fixed_t ldx; + fixed_t ldy; + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + lx = line->v1->x; + ly = line->v1->y; + + ldx = line->v2->x - lx; + ldy = line->v2->y - ly; + + if (!ldx) + { + if (x <= lx) + return ldy > 0; + + return ldy < 0; + } + if (!ldy) + { + if (y <= ly) + return ldx < 0; + + return ldx > 0; + } + + dx = (x - lx); + dy = (y - ly); + + // Try to quickly decide by looking at sign bits. + if ((ldy ^ ldx ^ dx ^ dy) & 0x80000000) + { + if ((ldy ^ dx) & 0x80000000) + { + // (left is negative) + return 1; + } + return 0; + } + + left = FixedMul(ldy >> FRACBITS, dx); + right = FixedMul(dy, ldx >> FRACBITS); + + if (right < left) + { + // front side + return 0; + } + // back side + return 1; +} + + +// +// R_PointToAngle +// To get a global angle from cartesian coordinates, +// the coordinates are flipped until they are in +// the first octant of the coordinate system, then +// the y (<=x) is scaled and divided by x to get a +// tangent (slope) value which is looked up in the +// tantoangle[] table. + +angle_t R_PointToAngle(fixed_t x, fixed_t y) +{ + x -= viewx; + y -= viewy; + + if ((!x) && (!y)) + return 0; + + if (x >= 0) + { + // x >=0 + if (y >= 0) + { + // y>= 0 + + if (x > y) + { + // octant 0 + return tantoangle[SlopeDiv(y, x)]; + } + else + { + // octant 1 + return ANG90 - 1 - tantoangle[SlopeDiv(x, y)]; + } + } + else + { + // y<0 + y = -y; + + if (x > y) + { + // octant 8 +#pragma warning(push) +#pragma warning(disable : 4146) + return -tantoangle[SlopeDiv(y, x)]; +#pragma warning(pop) + } + else + { + // octant 7 + return ANG270 + tantoangle[SlopeDiv(x, y)]; + } + } + } + else + { + // x<0 + x = -x; + + if (y >= 0) + { + // y>= 0 + if (x > y) + { + // octant 3 + return ANG180 - 1 - tantoangle[SlopeDiv(y, x)]; + } + else + { + // octant 2 + return ANG90 + tantoangle[SlopeDiv(x, y)]; + } + } + else + { + // y<0 + y = -y; + + if (x > y) + { + // octant 4 + return ANG180 + tantoangle[SlopeDiv(y, x)]; + } + else + { + // octant 5 + return ANG270 - 1 - tantoangle[SlopeDiv(x, y)]; + } + } + } + return 0; +} + + +angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + viewx = x1; + viewy = y1; + + return R_PointToAngle(x2, y2); +} + + +fixed_t R_PointToDist(fixed_t x, fixed_t y) +{ + int angle; + fixed_t dx; + fixed_t dy; + fixed_t temp; + fixed_t dist; + + dx = doom_abs(x - viewx); + dy = doom_abs(y - viewy); + + if (dy > dx) + { + temp = dx; + dx = dy; + dy = temp; + } + + angle = (tantoangle[FixedDiv(dy, dx) >> DBITS] + ANG90) >> ANGLETOFINESHIFT; + + // use as cosine + dist = FixedDiv(dx, finesine[angle]); + + return dist; +} + + +// +// R_InitPointToAngle +// +void R_InitPointToAngle(void) +{ +} + + +// +// R_ScaleFromGlobalAngle +// Returns the texture mapping scale +// for the current line (horizontal span) +// at the given angle. +// rw_distance must be calculated first. +// +fixed_t R_ScaleFromGlobalAngle(angle_t visangle) +{ + fixed_t scale; + int anglea; + int angleb; + int sinea; + int sineb; + fixed_t num; + int den; + + anglea = ANG90 + (visangle - viewangle); + angleb = ANG90 + (visangle - rw_normalangle); + + // both sines are allways positive + sinea = finesine[anglea >> ANGLETOFINESHIFT]; + sineb = finesine[angleb >> ANGLETOFINESHIFT]; + num = FixedMul(projection, sineb) << detailshift; + den = FixedMul(rw_distance, sinea); + + if (den > num >> 16) + { + scale = FixedDiv(num, den); + + if (scale > 64 * FRACUNIT) + scale = 64 * FRACUNIT; + else if (scale < 256) + scale = 256; + } + else + scale = 64 * FRACUNIT; + + return scale; +} + + +// +// R_InitTables +// +void R_InitTables(void) +{ +} + + +// +// R_InitTextureMapping +// +void R_InitTextureMapping(void) +{ + int i; + int x; + int t; + fixed_t focallength; + + // Use tangent table to generate viewangletox: + // viewangletox will give the next greatest x + // after the view angle. + // + // Calc focallength + // so FIELDOFVIEW angles covers SCREENWIDTH. + focallength = FixedDiv(centerxfrac, + finetangent[FINEANGLES / 4 + FIELDOFVIEW / 2]); + + for (i = 0; i < FINEANGLES / 2; i++) + { + if (finetangent[i] > FRACUNIT * 2) + t = -1; + else if (finetangent[i] < -FRACUNIT * 2) + t = viewwidth + 1; + else + { + t = FixedMul(finetangent[i], focallength); + t = (centerxfrac - t + FRACUNIT - 1) >> FRACBITS; + + if (t < -1) + t = -1; + else if (t > viewwidth + 1) + t = viewwidth + 1; + } + viewangletox[i] = t; + } + + // Scan viewangletox[] to generate xtoviewangle[]: + // xtoviewangle will give the smallest view angle + // that maps to x. + for (x = 0; x <= viewwidth; x++) + { + i = 0; + while (viewangletox[i] > x) + i++; + xtoviewangle[x] = (i << ANGLETOFINESHIFT) - ANG90; + } + + // Take out the fencepost cases from viewangletox. + for (i = 0; i < FINEANGLES / 2; i++) + { + t = FixedMul(finetangent[i], focallength); + t = centerx - t; + + if (viewangletox[i] == -1) + viewangletox[i] = 0; + else if (viewangletox[i] == viewwidth + 1) + viewangletox[i] = viewwidth; + } + + clipangle = xtoviewangle[0]; +} + + +// +// R_InitLightTables +// Only inits the zlight table, +// because the scalelight table changes with view size. +// + +void R_InitLightTables(void) +{ + int i; + int j; + int level; + int startmap; + int scale; + + // Calculate the light levels to use + // for each level / distance combination. + for (i = 0; i < LIGHTLEVELS; i++) + { + startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; + for (j = 0; j < MAXLIGHTZ; j++) + { + scale = FixedDiv((SCREENWIDTH / 2 * FRACUNIT), (j + 1) << LIGHTZSHIFT); + scale >>= LIGHTSCALESHIFT; + level = startmap - scale / DISTMAP; + + if (level < 0) + level = 0; + + if (level >= NUMCOLORMAPS) + level = NUMCOLORMAPS - 1; + + zlight[i][j] = colormaps + level * 256; + } + } +} + + +// +// R_SetViewSize +// Do not really change anything here, +// because it might be in the middle of a refresh. +// The change will take effect next refresh. +// + +void R_SetViewSize(int blocks, int detail) +{ + setsizeneeded = true; + setblocks = blocks; + setdetail = detail; +} + + +// +// R_ExecuteSetViewSize +// +void R_ExecuteSetViewSize(void) +{ + fixed_t cosadj; + fixed_t dy; + int i; + int j; + int level; + int startmap; + + setsizeneeded = false; + + if (setblocks == 11) + { + scaledviewwidth = SCREENWIDTH; + viewheight = SCREENHEIGHT; + } + else + { + scaledviewwidth = setblocks * 32; + viewheight = (setblocks * 168 / 10) & ~7; + } + + detailshift = setdetail; + viewwidth = scaledviewwidth >> detailshift; + + centery = viewheight / 2; + centerx = viewwidth / 2; + centerxfrac = centerx << FRACBITS; + centeryfrac = centery << FRACBITS; + projection = centerxfrac; + + if (!detailshift) + { + colfunc = basecolfunc = R_DrawColumn; + fuzzcolfunc = R_DrawFuzzColumn; + transcolfunc = R_DrawTranslatedColumn; + spanfunc = R_DrawSpan; + } + else + { + colfunc = basecolfunc = R_DrawColumnLow; + fuzzcolfunc = R_DrawFuzzColumn; + transcolfunc = R_DrawTranslatedColumn; + spanfunc = R_DrawSpanLow; + } + + R_InitBuffer(scaledviewwidth, viewheight); + + R_InitTextureMapping(); + + // psprite scales + pspritescale = FRACUNIT * viewwidth / SCREENWIDTH; + pspriteiscale = FRACUNIT * SCREENWIDTH / viewwidth; + + // thing clipping + for (i = 0; i < viewwidth; i++) + screenheightarray[i] = viewheight; + + // planes + for (i = 0; i < viewheight; i++) + { + dy = ((i - viewheight / 2) << FRACBITS) + FRACUNIT / 2; + dy = doom_abs(dy); + yslope[i] = FixedDiv((viewwidth << detailshift) / 2 * FRACUNIT, dy); + } + + for (i = 0; i < viewwidth; i++) + { + cosadj = doom_abs(finecosine[xtoviewangle[i] >> ANGLETOFINESHIFT]); + distscale[i] = FixedDiv(FRACUNIT, cosadj); + } + + // Calculate the light levels to use + // for each level / scale combination. + for (i = 0; i < LIGHTLEVELS; i++) + { + startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; + for (j = 0; j < MAXLIGHTSCALE; j++) + { + level = startmap - j * SCREENWIDTH / (viewwidth << detailshift) / DISTMAP; + + if (level < 0) + level = 0; + + if (level >= NUMCOLORMAPS) + level = NUMCOLORMAPS - 1; + + scalelight[i][j] = colormaps + level * 256; + } + } +} + + +// +// R_Init +// +void R_Init(void) +{ + R_InitData(); + doom_print("\nR_InitData"); + R_InitPointToAngle(); + doom_print("\nR_InitPointToAngle"); + R_InitTables(); + // viewwidth / viewheight / detailLevel are set by the defaults + doom_print("\nR_InitTables"); + + R_SetViewSize(screenblocks, detailLevel); + R_InitPlanes(); + doom_print("\nR_InitPlanes"); + R_InitLightTables(); + doom_print("\nR_InitLightTables"); + R_InitSkyMap(); + doom_print("\nR_InitSkyMap"); + R_InitTranslationTables(); + doom_print("\nR_InitTranslationsTables"); + + framecount = 0; +} + + +// +// R_PointInSubsector +// +subsector_t* R_PointInSubsector(fixed_t x, fixed_t y) +{ + node_t* node; + int side; + int nodenum; + + // single subsector is a special case + if (!numnodes) + return subsectors; + + nodenum = numnodes - 1; + + while (!(nodenum & NF_SUBSECTOR)) + { + node = &nodes[nodenum]; + side = R_PointOnSide(x, y, node); + nodenum = node->children[side]; + } + + return &subsectors[nodenum & ~NF_SUBSECTOR]; +} + + +// +// R_SetupFrame +// +void R_SetupFrame(player_t* player) +{ + int i; + + viewplayer = player; + viewx = player->mo->x; + viewy = player->mo->y; + viewangle = player->mo->angle + viewangleoffset; + extralight = player->extralight; + + viewz = player->viewz; + + viewsin = finesine[viewangle >> ANGLETOFINESHIFT]; + viewcos = finecosine[viewangle >> ANGLETOFINESHIFT]; + + sscount = 0; + + if (player->fixedcolormap) + { + fixedcolormap = + colormaps + + player->fixedcolormap * 256 * sizeof(lighttable_t); + + walllights = scalelightfixed; + + for (i = 0; i < MAXLIGHTSCALE; i++) + scalelightfixed[i] = fixedcolormap; + } + else + fixedcolormap = 0; + + framecount++; + validcount++; +} + + +// +// R_RenderView +// +void R_RenderPlayerView(player_t* player) +{ + R_SetupFrame(player); + + // Clear buffers. + R_ClearClipSegs(); + R_ClearDrawSegs(); + R_ClearPlanes(); + R_ClearSprites(); + + // check for new console commands. + NetUpdate(); + + // The head node is the last node output. + R_RenderBSPNode(numnodes - 1); + + // Check for new console commands. + NetUpdate(); + + R_DrawPlanes(); + + // Check for new console commands. + NetUpdate(); + + R_DrawMasked(); + + // Check for new console commands. + NetUpdate(); +} +#define MAXVISPLANES 128 +#define MAXOPENINGS SCREENWIDTH*64 + + +planefunction_t floorfunc; +planefunction_t ceilingfunc; + +// +// opening +// + +// Here comes the obnoxious "visplane". +visplane_t visplanes[MAXVISPLANES]; +visplane_t* lastvisplane; +visplane_t* floorplane; +visplane_t* ceilingplane; + +// ? +short openings[MAXOPENINGS]; +short* lastopening; + +// +// Clip values are the solid pixel bounding the range. +// floorclip starts out SCREENHEIGHT +// ceilingclip starts out -1 +// +short floorclip[SCREENWIDTH]; +short ceilingclip[SCREENWIDTH]; + +// +// spanstart holds the start of a plane span +// initialized to 0 at start +// +int spanstart[SCREENHEIGHT]; +int spanstop[SCREENHEIGHT]; + +// +// texture mapping +// +lighttable_t** planezlight; +fixed_t planeheight; + +fixed_t yslope[SCREENHEIGHT]; +fixed_t distscale[SCREENWIDTH]; +fixed_t basexscale; +fixed_t baseyscale; + +fixed_t cachedheight[SCREENHEIGHT]; +fixed_t cacheddistance[SCREENHEIGHT]; +fixed_t cachedxstep[SCREENHEIGHT]; +fixed_t cachedystep[SCREENHEIGHT]; + + +// +// R_InitPlanes +// Only at game startup. +// +void R_InitPlanes(void) +{ + // Doh! +} + + +// +// R_MapPlane +// +// Uses global vars: +// planeheight +// ds_source +// basexscale +// baseyscale +// viewx +// viewy +// +// BASIC PRIMITIVE +// +void R_MapPlane(int y, int x1, int x2) +{ + angle_t angle; + fixed_t distance; + fixed_t length; + unsigned index; + +#ifdef RANGECHECK + if (x2 < x1 + || x1<0 + || x2 >= viewwidth + || (unsigned)y>(unsigned)viewheight) + { + //I_Error("Error: R_MapPlane: %i, %i at %i", x1, x2, y); + + doom_strcpy(error_buf, "Error: R_MapPlane: "); + doom_concat(error_buf, doom_itoa(x1, 10)); + doom_concat(error_buf, ", "); + doom_concat(error_buf, doom_itoa(x2, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(y, 10)); + I_Error(error_buf); + } +#endif + + if (planeheight != cachedheight[y]) + { + cachedheight[y] = planeheight; + distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]); + ds_xstep = cachedxstep[y] = FixedMul(distance, basexscale); + ds_ystep = cachedystep[y] = FixedMul(distance, baseyscale); + } + else + { + distance = cacheddistance[y]; + ds_xstep = cachedxstep[y]; + ds_ystep = cachedystep[y]; + } + + length = FixedMul(distance, distscale[x1]); + angle = (viewangle + xtoviewangle[x1]) >> ANGLETOFINESHIFT; + ds_xfrac = viewx + FixedMul(finecosine[angle], length); + ds_yfrac = -viewy - FixedMul(finesine[angle], length); + + if (fixedcolormap) + ds_colormap = fixedcolormap; + else + { + index = distance >> LIGHTZSHIFT; + + if (index >= MAXLIGHTZ) + index = MAXLIGHTZ - 1; + + ds_colormap = planezlight[index]; + } + + ds_y = y; + ds_x1 = x1; + ds_x2 = x2; + + // high or low detail + spanfunc(); +} + + +// +// R_ClearPlanes +// At begining of frame. +// +void R_ClearPlanes(void) +{ + int i; + angle_t angle; + + // opening / clipping determination + for (i = 0; i < viewwidth; i++) + { + floorclip[i] = viewheight; + ceilingclip[i] = -1; + } + + lastvisplane = visplanes; + lastopening = openings; + + // texture calculation + doom_memset(cachedheight, 0, sizeof(cachedheight)); + + // left to right mapping + angle = (viewangle - ANG90) >> ANGLETOFINESHIFT; + + // scale will be unit scale at SCREENWIDTH/2 distance + basexscale = FixedDiv(finecosine[angle], centerxfrac); + baseyscale = -FixedDiv(finesine[angle], centerxfrac); +} + + +// +// R_FindPlane +// +visplane_t* R_FindPlane(fixed_t height, int picnum, int lightlevel) +{ + visplane_t* check; + + if (picnum == skyflatnum) + { + height = 0; // all skys map together + lightlevel = 0; + } + + for (check = visplanes; check < lastvisplane; check++) + { + if (height == check->height + && picnum == check->picnum + && lightlevel == check->lightlevel) + { + break; + } + } + + if (check < lastvisplane) + return check; + + if (lastvisplane - visplanes == MAXVISPLANES) + I_Error("Error: R_FindPlane: no more visplanes"); + + lastvisplane++; + + check->height = height; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->minx = SCREENWIDTH; + check->maxx = -1; + + doom_memset(check->top, 0xff, sizeof(check->top)); + + return check; +} + + +// +// R_CheckPlane +// +visplane_t* R_CheckPlane(visplane_t* pl, int start, int stop) +{ + int intrl; + int intrh; + int unionl; + int unionh; + int x; + + if (start < pl->minx) + { + intrl = pl->minx; + unionl = start; + } + else + { + unionl = pl->minx; + intrl = start; + } + + if (stop > pl->maxx) + { + intrh = pl->maxx; + unionh = stop; + } + else + { + unionh = pl->maxx; + intrh = stop; + } + + for (x = intrl; x <= intrh; x++) + if (pl->top[x] != 0xff) + break; + + if (x > intrh) + { + pl->minx = unionl; + pl->maxx = unionh; + + // use the same one + return pl; + } + + // make a new visplane + lastvisplane->height = pl->height; + lastvisplane->picnum = pl->picnum; + lastvisplane->lightlevel = pl->lightlevel; + + pl = lastvisplane++; + pl->minx = start; + pl->maxx = stop; + + doom_memset(pl->top, 0xff, sizeof(pl->top)); + + return pl; +} + + +// +// R_MakeSpans +// +void R_MakeSpans(int x, int t1, int b1, int t2, int b2) +{ + while (t1 < t2 && t1 <= b1) + { + R_MapPlane(t1, spanstart[t1], x - 1); + t1++; + } + while (b1 > b2 && b1 >= t1) + { + R_MapPlane(b1, spanstart[b1], x - 1); + b1--; + } + + while (t2 < t1 && t2 <= b2) + { + spanstart[t2] = x; + t2++; + } + while (b2 > b1 && b2 >= t2) + { + spanstart[b2] = x; + b2--; + } +} + + +// +// R_DrawPlanes +// At the end of each frame. +// +void R_DrawPlanes(void) +{ + visplane_t* pl; + int light; + int x; + int stop; + int angle; + +#ifdef RANGECHECK + if (ds_p - drawsegs > MAXDRAWSEGS) + { + //I_Error("Error: R_DrawPlanes: drawsegs overflow (%i)", + // ds_p - drawsegs); + + doom_strcpy(error_buf, "Error: R_DrawPlanes: drawsegs overflow ("); + doom_concat(error_buf, doom_itoa((int)(ds_p - drawsegs), 10)); + doom_concat(error_buf, ")"); + I_Error(error_buf); + } + + if (lastvisplane - visplanes > MAXVISPLANES) + { + //I_Error("Error: R_DrawPlanes: visplane overflow (%i)", + // lastvisplane - visplanes); + + doom_strcpy(error_buf, "Error: R_DrawPlanes: visplane overflow ("); + doom_concat(error_buf, doom_itoa((int)(lastvisplane - visplanes), 10)); + doom_concat(error_buf, ")"); + I_Error(error_buf); + } + + if (lastopening - openings > MAXOPENINGS) + { + //I_Error("Error: R_DrawPlanes: opening overflow (%i)", + // lastopening - openings); + + doom_strcpy(error_buf, "Error: R_DrawPlanes: opening overflow ("); + doom_concat(error_buf, doom_itoa((int)(lastopening - openings), 10)); + doom_concat(error_buf, ")"); + I_Error(error_buf); + } +#endif + + for (pl = visplanes; pl < lastvisplane; pl++) + { + if (pl->minx > pl->maxx) + continue; + + + // sky flat + if (pl->picnum == skyflatnum) + { + dc_iscale = pspriteiscale >> detailshift; + + // Sky is allways drawn full bright, + // i.e. colormaps[0] is used. + // Because of this hack, sky is not affected + // by INVUL inverse mapping. + dc_colormap = colormaps; + dc_texturemid = skytexturemid; + for (x = pl->minx; x <= pl->maxx; x++) + { + dc_yl = pl->top[x]; + dc_yh = pl->bottom[x]; + + if (dc_yl <= dc_yh) + { + angle = (viewangle + xtoviewangle[x]) >> ANGLETOSKYSHIFT; + dc_x = x; + dc_source = R_GetColumn(skytexture, angle); + colfunc(); + } + } + continue; + } + + // regular flat + ds_source = W_CacheLumpNum(firstflat + + flattranslation[pl->picnum], + PU_STATIC); + + planeheight = doom_abs(pl->height - viewz); + light = (pl->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (light >= LIGHTLEVELS) + light = LIGHTLEVELS - 1; + + if (light < 0) + light = 0; + + planezlight = zlight[light]; + + pl->top[pl->maxx + 1] = 0xff; + pl->top[pl->minx - 1] = 0xff; + + stop = pl->maxx + 1; + + for (x = pl->minx; x <= stop; x++) + { + R_MakeSpans(x, pl->top[x - 1], + pl->bottom[x - 1], + pl->top[x], + pl->bottom[x]); + } + + Z_ChangeTag(ds_source, PU_CACHE); + } +} +#define HEIGHTBITS 12 +#define HEIGHTUNIT (1<curline; + frontsector = curline->frontsector; + backsector = curline->backsector; + texnum = texturetranslation[curline->sidedef->midtexture]; + + lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (curline->v1->y == curline->v2->y) + lightnum--; + else if (curline->v1->x == curline->v2->x) + lightnum++; + + if (lightnum < 0) + walllights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + walllights = scalelight[LIGHTLEVELS - 1]; + else + walllights = scalelight[lightnum]; + + maskedtexturecol = ds->maskedtexturecol; + + rw_scalestep = ds->scalestep; + spryscale = ds->scale1 + (x1 - ds->x1) * rw_scalestep; + mfloorclip = ds->sprbottomclip; + mceilingclip = ds->sprtopclip; + + // find positioning + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + dc_texturemid = frontsector->floorheight > backsector->floorheight + ? frontsector->floorheight : backsector->floorheight; + dc_texturemid = dc_texturemid + textureheight[texnum] - viewz; + } + else + { + dc_texturemid = frontsector->ceilingheight < backsector->ceilingheight + ? frontsector->ceilingheight : backsector->ceilingheight; + dc_texturemid = dc_texturemid - viewz; + } + dc_texturemid += curline->sidedef->rowoffset; + + if (fixedcolormap) + dc_colormap = fixedcolormap; + + // draw the columns + for (dc_x = x1; dc_x <= x2; dc_x++) + { + // calculate lighting + if (maskedtexturecol[dc_x] != DOOM_MAXSHORT) + { + if (!fixedcolormap) + { + index = spryscale >> LIGHTSCALESHIFT; + + if (index >= MAXLIGHTSCALE) + index = MAXLIGHTSCALE - 1; + + dc_colormap = walllights[index]; + } + + sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); + dc_iscale = 0xffffffffu / (unsigned)spryscale; + + // draw the texture + col = (column_t*)( + (byte*)R_GetColumn(texnum, maskedtexturecol[dc_x]) - 3); + + R_DrawMaskedColumn(col); + maskedtexturecol[dc_x] = DOOM_MAXSHORT; + } + spryscale += rw_scalestep; + } +} + + +// +// R_RenderSegLoop +// Draws zero, one, or two textures (and possibly a masked +// texture) for walls. +// Can draw or mark the starting pixel of floor and ceiling +// textures. +// CALLED: CORE LOOPING ROUTINE. +// +void R_RenderSegLoop(void) +{ + angle_t angle; + unsigned index; + int yl; + int yh; + int mid; + fixed_t texturecolumn; + int top; + int bottom; + + for (; rw_x < rw_stopx; rw_x++) + { + // mark floor / ceiling areas + yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS; + + // no space above wall? + if (yl < ceilingclip[rw_x] + 1) + yl = ceilingclip[rw_x] + 1; + + if (markceiling) + { + top = ceilingclip[rw_x] + 1; + bottom = yl - 1; + + if (bottom >= floorclip[rw_x]) + bottom = floorclip[rw_x] - 1; + + if (top <= bottom) + { + ceilingplane->top[rw_x] = top; + ceilingplane->bottom[rw_x] = bottom; + } + } + + yh = bottomfrac >> HEIGHTBITS; + + if (yh >= floorclip[rw_x]) + yh = floorclip[rw_x] - 1; + + if (markfloor) + { + top = yh + 1; + bottom = floorclip[rw_x] - 1; + if (top <= ceilingclip[rw_x]) + top = ceilingclip[rw_x] + 1; + if (top <= bottom) + { + floorplane->top[rw_x] = top; + floorplane->bottom[rw_x] = bottom; + } + } + + // texturecolumn and lighting are independent of wall tiers + if (segtextured) + { + // calculate texture offset + angle = (rw_centerangle + xtoviewangle[rw_x]) >> ANGLETOFINESHIFT; + texturecolumn = rw_offset - FixedMul(finetangent[angle], rw_distance); + texturecolumn >>= FRACBITS; + // calculate lighting + index = rw_scale >> LIGHTSCALESHIFT; + + if (index >= MAXLIGHTSCALE) + index = MAXLIGHTSCALE - 1; + + dc_colormap = walllights[index]; + dc_x = rw_x; + dc_iscale = 0xffffffffu / (unsigned)rw_scale; + } + + // draw the wall tiers + if (midtexture) + { + // single sided line + dc_yl = yl; + dc_yh = yh; + dc_texturemid = rw_midtexturemid; + dc_source = R_GetColumn(midtexture, texturecolumn); + colfunc(); + ceilingclip[rw_x] = viewheight; + floorclip[rw_x] = -1; + } + else + { + // two sided line + if (toptexture) + { + // top wall + mid = pixhigh >> HEIGHTBITS; + pixhigh += pixhighstep; + + if (mid >= floorclip[rw_x]) + mid = floorclip[rw_x] - 1; + + if (mid >= yl) + { + dc_yl = yl; + dc_yh = mid; + dc_texturemid = rw_toptexturemid; + dc_source = R_GetColumn(toptexture, texturecolumn); + colfunc(); + ceilingclip[rw_x] = mid; + } + else + ceilingclip[rw_x] = yl - 1; + } + else + { + // no top wall + if (markceiling) + ceilingclip[rw_x] = yl - 1; + } + + if (bottomtexture) + { + // bottom wall + mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS; + pixlow += pixlowstep; + + // no space above wall? + if (mid <= ceilingclip[rw_x]) + mid = ceilingclip[rw_x] + 1; + + if (mid <= yh) + { + dc_yl = mid; + dc_yh = yh; + dc_texturemid = rw_bottomtexturemid; + dc_source = R_GetColumn(bottomtexture, + texturecolumn); + colfunc(); + floorclip[rw_x] = mid; + } + else + floorclip[rw_x] = yh + 1; + } + else + { + // no bottom wall + if (markfloor) + floorclip[rw_x] = yh + 1; + } + + if (maskedtexture) + { + // save texturecol + // for backdrawing of masked mid texture + maskedtexturecol[rw_x] = texturecolumn; + } + } + + rw_scale += rw_scalestep; + topfrac += topstep; + bottomfrac += bottomstep; + } +} + + +// +// R_StoreWallRange +// A wall segment will be drawn +// between start and stop pixels (inclusive). +// +void R_StoreWallRange(int start, int stop) +{ + fixed_t hyp; + fixed_t sineval; + angle_t distangle, offsetangle; + fixed_t vtop; + int lightnum; + + // don't overflow and crash + if (ds_p == &drawsegs[MAXDRAWSEGS]) + return; + +#ifdef RANGECHECK + if (start >= viewwidth || start > stop) + { + //I_Error("Error: Bad R_RenderWallRange: %i to %i", start, stop); + + doom_strcpy(error_buf, "Error: Bad R_RenderWallRange: "); + doom_concat(error_buf, doom_itoa(start, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(stop, 10)); + I_Error(error_buf); + } +#endif + + sidedef = curline->sidedef; + linedef = curline->linedef; + + // mark the segment as visible for auto map + linedef->flags |= ML_MAPPED; + + // calculate rw_distance for scale calculation + rw_normalangle = curline->angle + ANG90; + offsetangle = (angle_t)doom_abs((int)rw_normalangle - (int)rw_angle1); + + if (offsetangle > ANG90) + offsetangle = ANG90; + + distangle = ANG90 - offsetangle; + hyp = R_PointToDist(curline->v1->x, curline->v1->y); + sineval = finesine[distangle >> ANGLETOFINESHIFT]; + rw_distance = FixedMul(hyp, sineval); + + + ds_p->x1 = rw_x = start; + ds_p->x2 = stop; + ds_p->curline = curline; + rw_stopx = stop + 1; + + // calculate scale at both ends and step + ds_p->scale1 = rw_scale = + R_ScaleFromGlobalAngle(viewangle + xtoviewangle[start]); + + if (stop > start) + { + ds_p->scale2 = R_ScaleFromGlobalAngle(viewangle + xtoviewangle[stop]); + ds_p->scalestep = rw_scalestep = + (ds_p->scale2 - rw_scale) / (stop - start); + } + else + { + // UNUSED: try to fix the stretched line bug +#if 0 + if (rw_distance < FRACUNIT / 2) + { + fixed_t trx, try; + fixed_t gxt, gyt; + + trx = curline->v1->x - viewx; + try = curline->v1->y - viewy; + + gxt = FixedMul(trx, viewcos); + gyt = -FixedMul(try, viewsin); + ds_p->scale1 = FixedDiv(projection, gxt - gyt) << detailshift; + } +#endif + ds_p->scale2 = ds_p->scale1; + } + + // calculate texture boundaries + // and decide if floor / ceiling marks are needed + worldtop = frontsector->ceilingheight - viewz; + worldbottom = frontsector->floorheight - viewz; + + midtexture = toptexture = bottomtexture = maskedtexture = 0; + ds_p->maskedtexturecol = 0; + + if (!backsector) + { + // single sided line + midtexture = texturetranslation[sidedef->midtexture]; + // a single sided line is terminal, so it must mark ends + markfloor = markceiling = true; + if (linedef->flags & ML_DONTPEGBOTTOM) + { + vtop = frontsector->floorheight + + textureheight[sidedef->midtexture]; + // bottom of texture at bottom + rw_midtexturemid = vtop - viewz; + } + else + { + // top of texture at top + rw_midtexturemid = worldtop; + } + rw_midtexturemid += sidedef->rowoffset; + + ds_p->silhouette = SIL_BOTH; + ds_p->sprtopclip = screenheightarray; + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = DOOM_MAXINT; + ds_p->tsilheight = DOOM_MININT; + } + else + { + // two sided line + ds_p->sprtopclip = ds_p->sprbottomclip = 0; + ds_p->silhouette = 0; + + if (frontsector->floorheight > backsector->floorheight) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = frontsector->floorheight; + } + else if (backsector->floorheight > viewz) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = DOOM_MAXINT; + // ds_p->sprbottomclip = negonearray; + } + + if (frontsector->ceilingheight < backsector->ceilingheight) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = frontsector->ceilingheight; + } + else if (backsector->ceilingheight < viewz) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = DOOM_MININT; + // ds_p->sprtopclip = screenheightarray; + } + + if (backsector->ceilingheight <= frontsector->floorheight) + { + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = DOOM_MAXINT; + ds_p->silhouette |= SIL_BOTTOM; + } + + if (backsector->floorheight >= frontsector->ceilingheight) + { + ds_p->sprtopclip = screenheightarray; + ds_p->tsilheight = DOOM_MININT; + ds_p->silhouette |= SIL_TOP; + } + + worldhigh = backsector->ceilingheight - viewz; + worldlow = backsector->floorheight - viewz; + + // hack to allow height changes in outdoor areas + if (frontsector->ceilingpic == skyflatnum + && backsector->ceilingpic == skyflatnum) + { + worldtop = worldhigh; + } + + if (worldlow != worldbottom + || backsector->floorpic != frontsector->floorpic + || backsector->lightlevel != frontsector->lightlevel) + { + markfloor = true; + } + else + { + // same plane on both sides + markfloor = false; + } + + if (worldhigh != worldtop + || backsector->ceilingpic != frontsector->ceilingpic + || backsector->lightlevel != frontsector->lightlevel) + { + markceiling = true; + } + else + { + // same plane on both sides + markceiling = false; + } + + if (backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight) + { + // closed door + markceiling = markfloor = true; + } + + if (worldhigh < worldtop) + { + // top texture + toptexture = texturetranslation[sidedef->toptexture]; + if (linedef->flags & ML_DONTPEGTOP) + { + // top of texture at top + rw_toptexturemid = worldtop; + } + else + { + vtop = + backsector->ceilingheight + + textureheight[sidedef->toptexture]; + + // bottom of texture + rw_toptexturemid = vtop - viewz; + } + } + if (worldlow > worldbottom) + { + // bottom texture + bottomtexture = texturetranslation[sidedef->bottomtexture]; + + if (linedef->flags & ML_DONTPEGBOTTOM) + { + // bottom of texture at bottom + // top of texture at top + rw_bottomtexturemid = worldtop; + } + else // top of texture at top + rw_bottomtexturemid = worldlow; + } + rw_toptexturemid += sidedef->rowoffset; + rw_bottomtexturemid += sidedef->rowoffset; + + // allocate space for masked texture tables + if (sidedef->midtexture) + { + // masked midtexture + maskedtexture = true; + ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x; + lastopening += rw_stopx - rw_x; + } + } + + // calculate rw_offset (only needed for textured lines) + segtextured = midtexture | toptexture | bottomtexture | maskedtexture; + + if (segtextured) + { + offsetangle = rw_normalangle - rw_angle1; + + if (offsetangle > ANG180) +#pragma warning(push) +#pragma warning(disable : 4146) + offsetangle = -offsetangle; +#pragma warning(pop) + + if (offsetangle > ANG90) + offsetangle = ANG90; + + sineval = finesine[offsetangle >> ANGLETOFINESHIFT]; + rw_offset = FixedMul(hyp, sineval); + + if (rw_normalangle - rw_angle1 < ANG180) + rw_offset = -rw_offset; + + rw_offset += sidedef->textureoffset + curline->offset; + rw_centerangle = ANG90 + viewangle - rw_normalangle; + + // calculate light table + // use different light tables + // for horizontal / vertical / diagonal + // OPTIMIZE: get rid of LIGHTSEGSHIFT globally + if (!fixedcolormap) + { + lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (curline->v1->y == curline->v2->y) + lightnum--; + else if (curline->v1->x == curline->v2->x) + lightnum++; + + if (lightnum < 0) + walllights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + walllights = scalelight[LIGHTLEVELS - 1]; + else + walllights = scalelight[lightnum]; + } + } + + // if a floor / ceiling plane is on the wrong side + // of the view plane, it is definitely invisible + // and doesn't need to be marked. + + if (frontsector->floorheight >= viewz) + { + // above view plane + markfloor = false; + } + + if (frontsector->ceilingheight <= viewz + && frontsector->ceilingpic != skyflatnum) + { + // below view plane + markceiling = false; + } + + + // calculate incremental stepping values for texture edges + worldtop >>= 4; + worldbottom >>= 4; + + topstep = -FixedMul(rw_scalestep, worldtop); + topfrac = (centeryfrac >> 4) - FixedMul(worldtop, rw_scale); + + bottomstep = -FixedMul(rw_scalestep, worldbottom); + bottomfrac = (centeryfrac >> 4) - FixedMul(worldbottom, rw_scale); + + if (backsector) + { + worldhigh >>= 4; + worldlow >>= 4; + + if (worldhigh < worldtop) + { + pixhigh = (centeryfrac >> 4) - FixedMul(worldhigh, rw_scale); + pixhighstep = -FixedMul(rw_scalestep, worldhigh); + } + + if (worldlow > worldbottom) + { + pixlow = (centeryfrac >> 4) - FixedMul(worldlow, rw_scale); + pixlowstep = -FixedMul(rw_scalestep, worldlow); + } + } + + // render it + if (markceiling) + ceilingplane = R_CheckPlane(ceilingplane, rw_x, rw_stopx - 1); + + if (markfloor) + floorplane = R_CheckPlane(floorplane, rw_x, rw_stopx - 1); + + R_RenderSegLoop(); + + // save sprite clipping info + if (((ds_p->silhouette & SIL_TOP) || maskedtexture) + && !ds_p->sprtopclip) + { + doom_memcpy(lastopening, ceilingclip + start, 2 * (rw_stopx - start)); + ds_p->sprtopclip = lastopening - start; + lastopening += rw_stopx - start; + } + + if (((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) + && !ds_p->sprbottomclip) + { + doom_memcpy(lastopening, floorclip + start, 2 * (rw_stopx - start)); + ds_p->sprbottomclip = lastopening - start; + lastopening += rw_stopx - start; + } + + if (maskedtexture && !(ds_p->silhouette & SIL_TOP)) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = DOOM_MININT; + } + if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM)) + { + ds_p->silhouette |= SIL_BOTTOM; + ds_p->bsilheight = DOOM_MAXINT; + } + ds_p++; +} +int skyflatnum; +int skytexture; +int skytexturemid; + + +// +// R_InitSkyMap +// Called whenever the view size changes. +// +void R_InitSkyMap(void) +{ + skytexturemid = 100 * FRACUNIT; +} +#define MINZ (FRACUNIT*4) +#define BASEYCENTER 100 + + +typedef struct +{ + int x1; + int x2; + + int column; + int topclip; + int bottomclip; +} maskdraw_t; + + +// +// Sprite rotation 0 is facing the viewer, +// rotation 1 is one angle turn CLOCKWISE around the axis. +// This is not the same as the angle, +// which increases counter clockwise (protractor). +// There was a lot of stuff grabbed wrong, so I changed it... +// +fixed_t pspritescale; +fixed_t pspriteiscale; + +lighttable_t** spritelights; + +// constant arrays +// used for psprite clipping and initializing clipping +short negonearray[SCREENWIDTH]; +short screenheightarray[SCREENWIDTH]; + +// variables used to look up +// and range check thing_t sprites patches +spritedef_t* sprites; +int numsprites; + +spriteframe_t sprtemp[29]; +int maxframe; +char* spritename; +vissprite_t vissprites[MAXVISSPRITES]; +vissprite_t* vissprite_p; +int newvissprite; +vissprite_t vsprsortedhead; + + +// +// INITIALIZATION FUNCTIONS +// + +// +// R_InstallSpriteLump +// Local function for R_InitSprites. +// +void R_InstallSpriteLump(int lump, unsigned frame, unsigned rotation, doom_boolean flipped) +{ + int r; + + if (frame >= 29 || rotation > 8) + { + //I_Error("Error: R_InstallSpriteLump: " + // "Bad frame characters in lump %i", lump); + doom_strcpy(error_buf, "Error: R_InstallSpriteLump: Bad frame characters in lump "); + doom_concat(error_buf, doom_itoa(lump, 10)); + I_Error(error_buf); + } + + if ((int)frame > maxframe) + maxframe = frame; + + if (rotation == 0) + { + // the lump should be used for all rotations + if (sprtemp[frame].rotate == false) + { + //I_Error("Error: R_InitSprites: Sprite %s frame %c has " + // "multip rot=0 lump", spritename, 'A' + frame); + doom_strcpy(error_buf, "Error: R_InitSprites: Sprite "); + doom_concat(error_buf, spritename); + doom_concat(error_buf, " frame "); + doom_concat(error_buf, doom_ctoa('A' + frame)); + doom_concat(error_buf, " has multip rot=0 lump"); + I_Error(error_buf); + } + + if (sprtemp[frame].rotate == true) + { + //I_Error("Error: R_InitSprites: Sprite %s frame %c has rotations " + // "and a rot=0 lump", spritename, 'A' + frame); + doom_strcpy(error_buf, "Error: R_InitSprites: Sprite "); + doom_concat(error_buf, spritename); + doom_concat(error_buf, " frame "); + doom_concat(error_buf, doom_ctoa('A' + frame)); + doom_concat(error_buf, " has rotations "); + I_Error(error_buf); + } + + sprtemp[frame].rotate = false; + for (r = 0; r < 8; r++) + { + sprtemp[frame].lump[r] = lump - firstspritelump; + sprtemp[frame].flip[r] = (byte)flipped; + } + return; + } + + // the lump is only used for one rotation + if (sprtemp[frame].rotate == false) + { + //I_Error("Error: R_InitSprites: Sprite %s frame %c has rotations " + // "and a rot=0 lump", spritename, 'A' + frame); + doom_strcpy(error_buf, "Error: R_InitSprites: Sprite "); + doom_concat(error_buf, spritename); + doom_concat(error_buf, " frame "); + doom_concat(error_buf, doom_ctoa('A' + frame)); + doom_concat(error_buf, " has rotations "); + I_Error(error_buf); + } + + sprtemp[frame].rotate = true; + + // make 0 based + rotation--; + if (sprtemp[frame].lump[rotation] != -1) + { + //I_Error("Error: R_InitSprites: Sprite %s : %c : %c " + // "has two lumps mapped to it", + // spritename, 'A' + frame, '1' + rotation); + doom_strcpy(error_buf, "Error: R_InitSprites: Sprite "); + doom_concat(error_buf, spritename); + doom_concat(error_buf, " : "); + doom_concat(error_buf, doom_ctoa('A' + frame)); + doom_concat(error_buf, " : "); + doom_concat(error_buf, doom_ctoa('1' + rotation)); + doom_concat(error_buf, " "); + I_Error(error_buf); + } + + sprtemp[frame].lump[rotation] = lump - firstspritelump; + sprtemp[frame].flip[rotation] = (byte)flipped; +} + + +// +// R_InitSpriteDefs +// Pass a null terminated list of sprite names +// (4 chars exactly) to be used. +// Builds the sprite rotation matrixes to account +// for horizontally flipped sprites. +// Will report an error if the lumps are inconsistant. +// Only called at startup. +// +// Sprite lump names are 4 characters for the actor, +// a letter for the frame, and a number for the rotation. +// A sprite that is flippable will have an additional +// letter/number appended. +// The rotation character can be 0 to signify no rotations. +// +void R_InitSpriteDefs(char** namelist) +{ + char** check; + int i; + int l; + int intname; + int frame; + int rotation; + int start; + int end; + int patched; + + // count the number of sprite names + check = namelist; + while (*check != 0) + check++; + + numsprites = (int)(check - namelist); + + if (!numsprites) + return; + + sprites = Z_Malloc(numsprites * sizeof(*sprites), PU_STATIC, 0); + + start = firstspritelump - 1; + end = lastspritelump + 1; + + // scan all the lump names for each of the names, + // noting the highest frame letter. + // Just compare 4 characters as ints + for (i = 0; i < numsprites; i++) + { + spritename = namelist[i]; + doom_memset(sprtemp, -1, sizeof(sprtemp)); + + maxframe = -1; + intname = *(int*)namelist[i]; + + // scan the lumps, + // filling in the frames for whatever is found + for (l = start + 1; l < end; l++) + { + if (*(int*)lumpinfo[l].name == intname) + { + frame = lumpinfo[l].name[4] - 'A'; + rotation = lumpinfo[l].name[5] - '0'; + + if (modifiedgame) + patched = W_GetNumForName(lumpinfo[l].name); + else + patched = l; + + R_InstallSpriteLump(patched, frame, rotation, false); + + if (lumpinfo[l].name[6]) + { + frame = lumpinfo[l].name[6] - 'A'; + rotation = lumpinfo[l].name[7] - '0'; + R_InstallSpriteLump(l, frame, rotation, true); + } + } + } + + // check the frames that were found for completeness + if (maxframe == -1) + { + sprites[i].numframes = 0; + continue; + } + + maxframe++; + + for (frame = 0; frame < maxframe; frame++) + { + switch ((int)sprtemp[frame].rotate) + { + case -1: + { + // no rotations were found for that frame at all + //I_Error("Error: R_InitSprites: No patches found " + // "for %s frame %c", namelist[i], frame + 'A'); + doom_strcpy(error_buf, "Error: R_InitSprites: No patches found for "); + doom_concat(error_buf, namelist[i]); + doom_concat(error_buf, " frame "); + doom_concat(error_buf, doom_ctoa(frame + 'A')); + I_Error(error_buf); + break; + } + + case 0: + // only the first rotation is needed + break; + + case 1: + // must have all 8 frames + for (rotation = 0; rotation < 8; rotation++) + if (sprtemp[frame].lump[rotation] == -1) + { + //I_Error("Error: R_InitSprites: Sprite %s frame %c " + // "is missing rotations", + // namelist[i], frame + 'A'); + doom_strcpy(error_buf, "Error: R_InitSprites: Sprite "); + doom_concat(error_buf, namelist[i]); + doom_concat(error_buf, " frame "); + doom_concat(error_buf, doom_ctoa(frame + 'A')); + doom_concat(error_buf, " is missing rotations"); + I_Error(error_buf); + } + break; + } + } + + // allocate space for the frames present and copy sprtemp to it + sprites[i].numframes = maxframe; + sprites[i].spriteframes = + Z_Malloc(maxframe * sizeof(spriteframe_t), PU_STATIC, 0); + doom_memcpy(sprites[i].spriteframes, sprtemp, maxframe * sizeof(spriteframe_t)); + } +} + + +// +// GAME FUNCTIONS +// + +// +// R_InitSprites +// Called at program start. +// +void R_InitSprites(char** namelist) +{ + int i; + + for (i = 0; i < SCREENWIDTH; i++) + { + negonearray[i] = -1; + } + + R_InitSpriteDefs(namelist); +} + + +// +// R_ClearSprites +// Called at frame start. +// +void R_ClearSprites(void) +{ + vissprite_p = vissprites; +} + + +// +// R_NewVisSprite +// +vissprite_t overflowsprite; + +vissprite_t* R_NewVisSprite(void) +{ + if (vissprite_p == &vissprites[MAXVISSPRITES]) + return &overflowsprite; + + vissprite_p++; + return vissprite_p - 1; +} + + +// +// R_DrawMaskedColumn +// Used for sprites and masked mid textures. +// Masked means: partly transparent, i.e. stored +// in posts/runs of opaque pixels. +// +short* mfloorclip; +short* mceilingclip; + +fixed_t spryscale; +fixed_t sprtopscreen; + +void R_DrawMaskedColumn(column_t* column) +{ + int topscreen; + int bottomscreen; + fixed_t basetexturemid; + + basetexturemid = dc_texturemid; + + for (; column->topdelta != 0xff; ) + { + // calculate unclipped screen coordinates + // for post + topscreen = sprtopscreen + spryscale * column->topdelta; + bottomscreen = topscreen + spryscale * column->length; + + dc_yl = (topscreen + FRACUNIT - 1) >> FRACBITS; + dc_yh = (bottomscreen - 1) >> FRACBITS; + + if (dc_yh >= mfloorclip[dc_x]) + dc_yh = mfloorclip[dc_x] - 1; + if (dc_yl <= mceilingclip[dc_x]) + dc_yl = mceilingclip[dc_x] + 1; + + if (dc_yl <= dc_yh) + { + dc_source = (byte*)column + 3; + dc_texturemid = basetexturemid - (column->topdelta << FRACBITS); + // dc_source = (byte *)column + 3 - column->topdelta; + + // Drawn by either R_DrawColumn + // or (SHADOW) R_DrawFuzzColumn. + colfunc(); + } + column = (column_t*)((byte*)column + column->length + 4); + } + + dc_texturemid = basetexturemid; +} + + +// +// R_DrawVisSprite +// mfloorclip and mceilingclip should also be set. +// +void R_DrawVisSprite(vissprite_t* vis, int x1, int x2) +{ + column_t* column; + int texturecolumn; + fixed_t frac; + patch_t* patch; + + patch = W_CacheLumpNum(vis->patch + firstspritelump, PU_CACHE); + + dc_colormap = vis->colormap; + + if (!dc_colormap) + { + // 0 colormap = shadow draw + colfunc = fuzzcolfunc; + } + else if (vis->mobjflags & MF_TRANSLATION) + { + colfunc = R_DrawTranslatedColumn; + dc_translation = translationtables - 256 + + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8)); + } + + dc_iscale = doom_abs(vis->xiscale) >> detailshift; + dc_texturemid = vis->texturemid; + frac = vis->startfrac; + spryscale = vis->scale; + sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); + + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) + { + texturecolumn = frac >> FRACBITS; +#ifdef RANGECHECK + if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) + I_Error("Error: R_DrawSpriteRange: bad texturecolumn"); +#endif + column = (column_t*)((byte*)patch + + LONG(patch->columnofs[texturecolumn])); + R_DrawMaskedColumn(column); + } + + colfunc = basecolfunc; +} + + +// +// R_ProjectSprite +// Generates a vissprite for a thing +// if it might be visible. +// +void R_ProjectSprite(mobj_t* thing) +{ + fixed_t tr_x; + fixed_t tr_y; + + fixed_t gxt; + fixed_t gyt; + + fixed_t tx; + fixed_t tz; + + fixed_t xscale; + + int x1; + int x2; + + spritedef_t* sprdef; + spriteframe_t* sprframe; + int lump; + + unsigned rot; + doom_boolean flip; + + int index; + + vissprite_t* vis; + + angle_t ang; + fixed_t iscale; + + // transform the origin point + tr_x = thing->x - viewx; + tr_y = thing->y - viewy; + + gxt = FixedMul(tr_x, viewcos); + gyt = -FixedMul(tr_y, viewsin); + + tz = gxt - gyt; + + // thing is behind view plane? + if (tz < MINZ) + return; + + xscale = FixedDiv(projection, tz); + + gxt = -FixedMul(tr_x, viewsin); + gyt = FixedMul(tr_y, viewcos); + tx = -(gyt + gxt); + + // too far off the side? + if (doom_abs(tx) > (tz << 2)) + return; + + // decide which patch to use for sprite relative to player +#ifdef RANGECHECK + if ((unsigned)thing->sprite >= (unsigned)numsprites) + { + //I_Error("Error: R_ProjectSprite: invalid sprite number %i ", + // thing->sprite); + + doom_strcpy(error_buf, "Error: R_ProjectSprite: invalid sprite number "); + doom_concat(error_buf, doom_itoa(thing->sprite, 10)); + doom_concat(error_buf, " "); + I_Error(error_buf); + } +#endif + sprdef = &sprites[thing->sprite]; +#ifdef RANGECHECK + if ((thing->frame & FF_FRAMEMASK) >= sprdef->numframes) + { + //I_Error("Error: R_ProjectSprite: invalid sprite frame %i : %i ", + // thing->sprite, thing->frame); + + doom_strcpy(error_buf, "Error: R_ProjectSprite: invalid sprite frame "); + doom_concat(error_buf, doom_itoa(thing->sprite, 10)); + doom_concat(error_buf, " : "); + doom_concat(error_buf, doom_itoa(thing->frame, 10)); + doom_concat(error_buf, " "); + I_Error(error_buf); + } +#endif + sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; + + if (sprframe->rotate) + { + // choose a different rotation based on player view + ang = R_PointToAngle(thing->x, thing->y); + rot = (ang - thing->angle + (unsigned)(ANG45 / 2) * 9) >> 29; + lump = sprframe->lump[rot]; + flip = (doom_boolean)sprframe->flip[rot]; + } + else + { + // use single rotation for all views + lump = sprframe->lump[0]; + flip = (doom_boolean)sprframe->flip[0]; + } + + // calculate edges of the shape + tx -= spriteoffset[lump]; + x1 = (centerxfrac + FixedMul(tx, xscale)) >> FRACBITS; + + // off the right side? + if (x1 > viewwidth) + return; + + tx += spritewidth[lump]; + x2 = ((centerxfrac + FixedMul(tx, xscale)) >> FRACBITS) - 1; + + // off the left side + if (x2 < 0) + return; + + // store information in a vissprite + vis = R_NewVisSprite(); + vis->mobjflags = thing->flags; + vis->scale = xscale << detailshift; + vis->gx = thing->x; + vis->gy = thing->y; + vis->gz = thing->z; + vis->gzt = thing->z + spritetopoffset[lump]; + vis->texturemid = vis->gzt - viewz; + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2; + iscale = FixedDiv(FRACUNIT, xscale); + + if (flip) + { + vis->startfrac = spritewidth[lump] - 1; + vis->xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale * (vis->x1 - x1); + vis->patch = lump; + + // get light level + if (thing->flags & MF_SHADOW) + { + // shadow draw + vis->colormap = 0; + } + else if (fixedcolormap) + { + // fixed map + vis->colormap = fixedcolormap; + } + else if (thing->frame & FF_FULLBRIGHT) + { + // full bright + vis->colormap = colormaps; + } + + else + { + // diminished light + index = xscale >> (LIGHTSCALESHIFT - detailshift); + + if (index >= MAXLIGHTSCALE) + index = MAXLIGHTSCALE - 1; + + vis->colormap = spritelights[index]; + } +} + + +// +// R_AddSprites +// During BSP traversal, this adds sprites by sector. +// +void R_AddSprites(sector_t* sec) +{ + mobj_t* thing; + int lightnum; + + // 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 (sec->validcount == validcount) + return; + + // Well, now it will be done. + sec->validcount = validcount; + + lightnum = (sec->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (lightnum < 0) + spritelights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + spritelights = scalelight[LIGHTLEVELS - 1]; + else + spritelights = scalelight[lightnum]; + + // Handle all things in sector. + for (thing = sec->thinglist; thing; thing = thing->snext) + R_ProjectSprite(thing); +} + + +// +// R_DrawPSprite +// +void R_DrawPSprite(pspdef_t* psp) +{ + fixed_t tx; + int x1; + int x2; + spritedef_t* sprdef; + spriteframe_t* sprframe; + int lump; + doom_boolean flip; + vissprite_t* vis; + vissprite_t avis; + + // decide which patch to use +#ifdef RANGECHECK + if ((unsigned)psp->state->sprite >= (unsigned)numsprites) + { + //I_Error("Error: R_ProjectSprite: invalid sprite number %i ", + // psp->state->sprite); + doom_strcpy(error_buf, "Error: R_ProjectSprite: invalid sprite number "); + doom_concat(error_buf, doom_itoa(psp->state->sprite, 10)); + doom_concat(error_buf, " "); + I_Error(error_buf); + } +#endif + sprdef = &sprites[psp->state->sprite]; +#ifdef RANGECHECK + if ((psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes) + { + //I_Error("Error: R_ProjectSprite: invalid sprite frame %i : %i ", + // psp->state->sprite, psp->state->frame); + doom_strcpy(error_buf, "Error: R_ProjectSprite: invalid sprite frame "); + doom_concat(error_buf, doom_itoa(psp->state->sprite, 10)); + doom_concat(error_buf, " : "); + doom_concat(error_buf, doom_itoa(psp->state->frame, 10)); + doom_concat(error_buf, " "); + I_Error(error_buf); + } +#endif + sprframe = &sprdef->spriteframes[psp->state->frame & FF_FRAMEMASK]; + + lump = sprframe->lump[0]; + flip = (doom_boolean)sprframe->flip[0]; + + // calculate edges of the shape + tx = psp->sx - 160 * FRACUNIT; + + tx -= spriteoffset[lump]; + x1 = (centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS; + + // off the right side + if (x1 > viewwidth) + return; + + tx += spritewidth[lump]; + x2 = ((centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS) - 1; + + // off the left side + if (x2 < 0) + return; + + // store information in a vissprite + vis = &avis; + vis->mobjflags = 0; + vis->texturemid = (BASEYCENTER << FRACBITS) + FRACUNIT / 2 - (psp->sy - spritetopoffset[lump]); + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2; + vis->scale = pspritescale << detailshift; + + if (flip) + { + vis->xiscale = -pspriteiscale; + vis->startfrac = spritewidth[lump] - 1; + } + else + { + vis->xiscale = pspriteiscale; + vis->startfrac = 0; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale * (vis->x1 - x1); + + vis->patch = lump; + + if (viewplayer->powers[pw_invisibility] > 4 * 32 + || viewplayer->powers[pw_invisibility] & 8) + { + // shadow draw + vis->colormap = 0; + } + else if (fixedcolormap) + { + // fixed color + vis->colormap = fixedcolormap; + } + else if (psp->state->frame & FF_FULLBRIGHT) + { + // full bright + vis->colormap = colormaps; + } + else + { + // local light + vis->colormap = spritelights[MAXLIGHTSCALE - 1]; + } + + R_DrawVisSprite(vis, vis->x1, vis->x2); +} + + +// +// R_DrawPlayerSprites +// +void R_DrawPlayerSprites(void) +{ + int i; + int lightnum; + pspdef_t* psp; + + // get light level + lightnum = + (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) + + extralight; + + if (lightnum < 0) + spritelights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + spritelights = scalelight[LIGHTLEVELS - 1]; + else + spritelights = scalelight[lightnum]; + + // clip to screen bounds + mfloorclip = screenheightarray; + mceilingclip = negonearray; + + // add all active psprites + for (i = 0, psp = viewplayer->psprites; + i < NUMPSPRITES; + i++, psp++) + { + if (psp->state) + R_DrawPSprite(psp); + } +} + + +// +// R_SortVisSprites +// +void R_SortVisSprites(void) +{ + int i; + int count; + vissprite_t* ds; + vissprite_t* best; + vissprite_t unsorted; + fixed_t bestscale; + + count = (int)(vissprite_p - vissprites); + + unsorted.next = unsorted.prev = &unsorted; + + if (!count) + return; + + for (ds = vissprites; ds < vissprite_p; ds++) + { + ds->next = ds + 1; + ds->prev = ds - 1; + } + + vissprites[0].prev = &unsorted; + unsorted.next = &vissprites[0]; + (vissprite_p - 1)->next = &unsorted; + unsorted.prev = vissprite_p - 1; + + // pull the vissprites out by scale + //best = 0; // shut up the compiler warning + vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; + for (i = 0; i < count; i++) + { + bestscale = DOOM_MAXINT; + for (ds = unsorted.next; ds != &unsorted; ds = ds->next) + { + if (ds->scale < bestscale) + { + bestscale = ds->scale; + best = ds; + } + } + best->next->prev = best->prev; + best->prev->next = best->next; + best->next = &vsprsortedhead; + best->prev = vsprsortedhead.prev; + vsprsortedhead.prev->next = best; + vsprsortedhead.prev = best; + } +} + + +// +// R_DrawSprite +// +void R_DrawSprite(vissprite_t* spr) +{ + drawseg_t* ds; + short clipbot[SCREENWIDTH]; + short cliptop[SCREENWIDTH]; + int x; + int r1; + int r2; + fixed_t scale; + fixed_t lowscale; + int silhouette; + + for (x = spr->x1; x <= spr->x2; x++) + clipbot[x] = cliptop[x] = -2; + + // Scan drawsegs from end to start for obscuring segs. + // The first drawseg that has a greater scale + // is the clip seg. + for (ds = ds_p - 1; ds >= drawsegs; ds--) + { + // determine if the drawseg obscures the sprite + if (ds->x1 > spr->x2 + || ds->x2 < spr->x1 + || (!ds->silhouette + && !ds->maskedtexturecol)) + { + // does not cover sprite + continue; + } + + r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; + r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; + + if (ds->scale1 > ds->scale2) + { + lowscale = ds->scale2; + scale = ds->scale1; + } + else + { + lowscale = ds->scale1; + scale = ds->scale2; + } + + if (scale < spr->scale + || (lowscale < spr->scale + && !R_PointOnSegSide(spr->gx, spr->gy, ds->curline))) + { + // masked mid texture? + if (ds->maskedtexturecol) + R_RenderMaskedSegRange(ds, r1, r2); + // seg is behind sprite + continue; + } + + + // clip this piece of the sprite + silhouette = ds->silhouette; + + if (spr->gz >= ds->bsilheight) + silhouette &= ~SIL_BOTTOM; + + if (spr->gzt <= ds->tsilheight) + silhouette &= ~SIL_TOP; + + if (silhouette == 1) + { + // bottom sil + for (x = r1; x <= r2; x++) + if (clipbot[x] == -2) + clipbot[x] = ds->sprbottomclip[x]; + } + else if (silhouette == 2) + { + // top sil + for (x = r1; x <= r2; x++) + if (cliptop[x] == -2) + cliptop[x] = ds->sprtopclip[x]; + } + else if (silhouette == 3) + { + // both + for (x = r1; x <= r2; x++) + { + if (clipbot[x] == -2) + clipbot[x] = ds->sprbottomclip[x]; + if (cliptop[x] == -2) + cliptop[x] = ds->sprtopclip[x]; + } + } + } + + // all clipping has been performed, so draw the sprite + + // check for unclipped columns + for (x = spr->x1; x <= spr->x2; x++) + { + if (clipbot[x] == -2) + clipbot[x] = viewheight; + + if (cliptop[x] == -2) + cliptop[x] = -1; + } + + mfloorclip = clipbot; + mceilingclip = cliptop; + R_DrawVisSprite(spr, spr->x1, spr->x2); +} + + +// +// R_DrawMasked +// +void R_DrawMasked(void) +{ + vissprite_t* spr; + drawseg_t* ds; + + R_SortVisSprites(); + + if (vissprite_p > vissprites) + { + // draw all vissprites back to front + for (spr = vsprsortedhead.next; + spr != &vsprsortedhead; + spr = spr->next) + { + + R_DrawSprite(spr); + } + } + + // render any remaining masked mid textures + for (ds = ds_p - 1; ds >= drawsegs; ds--) + if (ds->maskedtexturecol) + R_RenderMaskedSegRange(ds, ds->x1, ds->x2); + + // draw the psprites on top of everything + // but does not draw on side views + if (!viewangleoffset) + R_DrawPlayerSprites(); +} +#define S_MAX_VOLUME 127 + +// when to clip out sounds +// Does not fit the large outdoor areas. +#define S_CLIPPING_DIST (1200*0x10000) + +// Distance tp origin when sounds should be maxed out. +// This should relate to movement clipping resolution +// (see BLOCKMAP handling). +// Originally: (200*0x10000). +#define S_CLOSE_DIST (160*0x10000) + +#define S_ATTENUATOR ((S_CLIPPING_DIST-S_CLOSE_DIST)>>FRACBITS) + +// Adjustable by menu. +#define NORM_VOLUME snd_MaxVolume + +#define NORM_PITCH 128 +#define NORM_PRIORITY 64 +#define NORM_SEP 128 + +#define S_PITCH_PERTURB 1 +#define S_STEREO_SWING (96*0x10000) + +// percent attenuation from front to back +#define S_IFRACVOL 30 + +#define NA 0 +#define S_NUMCHANNELS 2 + + +// Current music/sfx card - index useless +// w/o a reference LUT in a sound module. +extern int snd_MusicDevice; +extern int snd_SfxDevice; +// Config file? Same disclaimer as above. +extern int snd_DesiredMusicDevice; +extern int snd_DesiredSfxDevice; + + +typedef struct +{ + // sound information (if null, channel avail.) + sfxinfo_t* sfxinfo; + + // origin of sound + void* origin; + + // handle of the sound being played + int handle; +} channel_t; + + +// the set of channels available +static channel_t* channels_s_sound; + +// whether songs are mus_paused +static doom_boolean mus_paused; + +// music currently being played +static musicinfo_t* mus_playing_s_sound = 0; + +static int nextcleanup; + + +// These are not used, but should be (menu). +// Maximum volume of a sound effect. +// Internal default is max out of 0-15. +int snd_SfxVolume = 15; + +// Maximum volume of music. Useless so far. +int snd_MusicVolume = 15; + +// following is set +// by the defaults code in M_misc: +// number of channels available +int numChannels; + + +// +// Prototypes +// +int S_getChannel(void* origin, sfxinfo_t* sfxinfo); +int S_AdjustSoundParams(mobj_t* listener, mobj_t* source, int* vol, int* sep, int* pitch); +void S_StopChannel(int cnum); + + +// +// Initializes sound stuff, including volume +// Sets channels, SFX and music volume, +// allocates channel buffer, sets S_sfx lookup. +// +void S_Init(int sfxVolume, int musicVolume) +{ + int i; + + //doom_print("S_Init: default sfx volume %d\n", sfxVolume); + doom_print("S_Init: default sfx volume "); + doom_print(doom_itoa(sfxVolume, 10)); + doom_print("\n"); + + // Whatever these did with DMX, these are rather dummies now. + I_SetChannels(); + + S_SetSfxVolume(sfxVolume); + // No music with Linux - another dummy. + S_SetMusicVolume(musicVolume); + + // Allocating the internal channels for mixing + // (the maximum numer of sounds rendered + // simultaneously) within zone memory. + channels_s_sound = + (channel_t*)Z_Malloc(numChannels * sizeof(channel_t), PU_STATIC, 0); + + // Free all channels for use + for (i = 0; i < numChannels; i++) + channels_s_sound[i].sfxinfo = 0; + + // no sounds are playing, and they are not mus_paused + mus_paused = 0; + + // Note that sounds have not been cached (yet). + for (i = 1; i < NUMSFX; i++) + S_sfx[i].lumpnum = S_sfx[i].usefulness = -1; +} + + +// +// Per level startup code. +// Kills playing sounds at start of level, +// determines music if any, changes music. +// +void S_Start(void) +{ + int cnum; + int mnum; + + // kill all playing sounds at start of level + // (trust me - a good idea) + for (cnum = 0; cnum < numChannels; cnum++) + if (channels_s_sound[cnum].sfxinfo) + S_StopChannel(cnum); + + // start new music for the level + mus_paused = 0; + + if (gamemode == commercial) + mnum = mus_runnin + gamemap - 1; + else + { + int spmus[] = + { + // Song - Who? - Where? + + mus_e3m4, // American e4m1 + mus_e3m2, // Romero e4m2 + mus_e3m3, // Shawn e4m3 + mus_e1m5, // American e4m4 + mus_e2m7, // Tim e4m5 + mus_e2m4, // Romero e4m6 + mus_e2m6, // J.Anderson e4m7 CHIRON.WAD + mus_e2m5, // Shawn e4m8 + mus_e1m9 // Tim e4m9 + }; + + if (gameepisode < 4) + mnum = mus_e1m1 + (gameepisode - 1) * 9 + gamemap - 1; + else + mnum = spmus[gamemap - 1]; + } + + // HACK FOR COMMERCIAL + // if (commercial && mnum > mus_e3m9) + // mnum -= mus_e3m9; + + S_ChangeMusic(mnum, true); + + nextcleanup = 15; +} + + +void S_StartSoundAtVolume(void* origin_p, int sfx_id, int volume) +{ + int rc; + int sep; + int pitch; + int priority; + sfxinfo_t* sfx; + int cnum; + + mobj_t* origin = (mobj_t*)origin_p; + + // check for bogus sound # + if (sfx_id < 1 || sfx_id > NUMSFX) + { + //I_Error("Error: Bad sfx #: %d", sfx_id); + doom_strcpy(error_buf, "Error: Bad sfx #: "); + doom_concat(error_buf, doom_itoa(sfx_id, 10)); + I_Error(error_buf); + } + + sfx = &S_sfx[sfx_id]; + + // Initialize sound parameters + if (sfx->link) + { + pitch = sfx->pitch; + priority = sfx->priority; + volume += sfx->volume; + + if (volume < 1) + return; + + if (volume > snd_SfxVolume) + volume = snd_SfxVolume; + } + else + { + pitch = NORM_PITCH; + priority = NORM_PRIORITY; + } + + // Check to see if it is audible, + // and if not, modify the params + if (origin && origin != players[consoleplayer].mo) + { + rc = S_AdjustSoundParams(players[consoleplayer].mo, + origin, + &volume, + &sep, + &pitch); + + if (origin->x == players[consoleplayer].mo->x + && origin->y == players[consoleplayer].mo->y) + { + sep = NORM_SEP; + } + + if (!rc) + return; + } + else + { + sep = NORM_SEP; + } + + // hacks to vary the sfx pitches + if (sfx_id >= sfx_sawup + && sfx_id <= sfx_sawhit) + { + pitch += 8 - (M_Random() & 15); + + if (pitch < 0) + pitch = 0; + else if (pitch > 255) + pitch = 255; + } + else if (sfx_id != sfx_itemup + && sfx_id != sfx_tink) + { + pitch += 16 - (M_Random() & 31); + + if (pitch < 0) + pitch = 0; + else if (pitch > 255) + pitch = 255; + } + + // kill old sound + S_StopSound(origin); + + // try to find a channel + cnum = S_getChannel(origin, sfx); + + if (cnum < 0) + return; + + // + // This is supposed to handle the loading/caching. + // For some odd reason, the caching is done nearly + // each time the sound is needed? + // + + // get lumpnum if necessary + if (sfx->lumpnum < 0) + sfx->lumpnum = I_GetSfxLumpNum(sfx); + +#ifndef SNDSRV + // cache data if necessary + if (!sfx->data) + { + doom_print( + "S_StartSoundAtVolume: 16bit and not pre-cached - wtf?\n"); + + // DOS remains, 8bit handling + //sfx->data = (void *) W_CacheLumpNum(sfx->lumpnum, PU_MUSIC); + // fprintf( stderr, + // "S_StartSoundAtVolume: loading %d (lump %d) : 0x%x\n", + // sfx_id, sfx->lumpnum, (int)sfx->data ); + + } +#endif + + // increase the usefulness + if (sfx->usefulness++ < 0) + sfx->usefulness = 1; + + // Assigns the handle to one of the channels in the + // mix/output buffer. + channels_s_sound[cnum].handle = I_StartSound(sfx_id, + /*sfx->data,*/ + volume, + sep, + pitch, + priority); +} + + +void S_StartSound(void* origin, int sfx_id) +{ + S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume); +} + + +void S_StopSound(void* origin) +{ + int cnum; + + for (cnum = 0; cnum < numChannels; cnum++) + { + if (channels_s_sound[cnum].sfxinfo && channels_s_sound[cnum].origin == origin) + { + S_StopChannel(cnum); + break; + } + } +} + + +// +// Stop and resume music, during game PAUSE. +// +void S_PauseSound(void) +{ + if (mus_playing_s_sound && !mus_paused) + { + I_PauseSong(mus_playing_s_sound->handle); + mus_paused = true; + } +} + + +void S_ResumeSound(void) +{ + if (mus_playing_s_sound && mus_paused) + { + I_ResumeSong(mus_playing_s_sound->handle); + mus_paused = false; + } +} + + +// +// Updates music & sounds +// +void S_UpdateSounds(void* listener_p) +{ + int audible; + int cnum; + int volume; + int sep; + int pitch; + sfxinfo_t* sfx; + channel_t* c; + + mobj_t* listener = (mobj_t*)listener_p; + + for (cnum = 0; cnum < numChannels; cnum++) + { + c = &channels_s_sound[cnum]; + sfx = c->sfxinfo; + + if (c->sfxinfo) + { + if (I_SoundIsPlaying(c->handle)) + { + // initialize parameters + volume = snd_SfxVolume; + pitch = NORM_PITCH; + sep = NORM_SEP; + + if (sfx->link) + { + pitch = sfx->pitch; + volume += sfx->volume; + if (volume < 1) + { + S_StopChannel(cnum); + continue; + } + else if (volume > snd_SfxVolume) + { + volume = snd_SfxVolume; + } + } + + // check non-local sounds for distance clipping + // or modify their params + if (c->origin && listener_p != c->origin) + { + audible = S_AdjustSoundParams(listener, + c->origin, + &volume, + &sep, + &pitch); + + if (!audible) + { + S_StopChannel(cnum); + } + else + I_UpdateSoundParams(c->handle, volume, sep, pitch); + } + } + else + { + // if channel is allocated but sound has stopped, + // free it + S_StopChannel(cnum); + } + } + } +} + + +void S_SetMusicVolume(int volume) +{ + if (volume < 0 || volume > 127) + { + //I_Error("Error: Attempt to set music volume at %d", + // volume); + doom_strcpy(error_buf, "Error: Attempt to set music volume at "); + doom_concat(error_buf, doom_itoa(volume, 10)); + I_Error(error_buf); + } + + I_SetMusicVolume(127); + I_SetMusicVolume(volume); + snd_MusicVolume = volume; +} + + +void S_SetSfxVolume(int volume) +{ + if (volume < 0 || volume > 127) + { + //I_Error("Error: Attempt to set sfx volume at %d", volume); + doom_strcpy(error_buf, "Error: Attempt to set sfx volume at "); + doom_concat(error_buf, doom_itoa(volume, 10)); + I_Error(error_buf); + } + + snd_SfxVolume = volume; +} + + +// +// Starts some music with the music id found in sounds.h. +// +void S_StartMusic(int m_id) +{ + S_ChangeMusic(m_id, false); +} + + +void S_ChangeMusic(int musicnum, int looping) +{ + musicinfo_t* music; + char namebuf[9]; + + if ((musicnum <= mus_None) + || (musicnum >= NUMMUSIC)) + { + //I_Error("Error: Bad music number %d", musicnum); + doom_strcpy(error_buf, "Error: Bad music number "); + doom_concat(error_buf, doom_itoa(musicnum, 10)); + I_Error(error_buf); + } + else + music = &S_music[musicnum]; + + if (mus_playing_s_sound == music) + return; + + // shutdown old music + S_StopMusic(); + + // get lumpnum if neccessary + if (!music->lumpnum) + { + //doom_sprintf(namebuf, "d_%s", music->name); + doom_strcpy(namebuf, "d_"); + doom_concat(namebuf, music->name); + music->lumpnum = W_GetNumForName(namebuf); + } + + // load & register it + music->data = (void*)W_CacheLumpNum(music->lumpnum, PU_MUSIC); + music->handle = I_RegisterSong(music->data); + + // play it + I_PlaySong(music->handle, looping); + + mus_playing_s_sound = music; +} + + +void S_StopMusic(void) +{ + if (mus_playing_s_sound) + { + if (mus_paused) + I_ResumeSong(mus_playing_s_sound->handle); + + I_StopSong(mus_playing_s_sound->handle); + I_UnRegisterSong(mus_playing_s_sound->handle); + Z_ChangeTag(mus_playing_s_sound->data, PU_CACHE); + + mus_playing_s_sound->data = 0; + mus_playing_s_sound = 0; + } +} + + +void S_StopChannel(int cnum) +{ + int i; + channel_t* c = &channels_s_sound[cnum]; + + if (c->sfxinfo) + { + // stop the sound playing + if (I_SoundIsPlaying(c->handle)) + { +#ifdef SAWDEBUG + if (c->sfxinfo == &S_sfx[sfx_sawful]) + doom_print("stopped\n"); +#endif + I_StopSound(c->handle); + } + + // check to see + // if other channels are playing the sound + for (i = 0; i < numChannels; i++) + { + if (cnum != i + && c->sfxinfo == channels_s_sound[i].sfxinfo) + { + break; + } + } + + // degrade usefulness of sound data + c->sfxinfo->usefulness--; + + c->sfxinfo = 0; + } +} + + +// +// Changes volume, stereo-separation, and pitch variables +// from the norm of a sound effect to be played. +// If the sound is not audible, returns a 0. +// Otherwise, modifies parameters and returns 1. +// +int S_AdjustSoundParams(mobj_t* listener, mobj_t* source, int* vol, int* sep, int* pitch) +{ + fixed_t approx_dist; + fixed_t adx; + fixed_t ady; + angle_t angle; + + // calculate the distance to sound origin + // and clip it if necessary + adx = doom_abs(listener->x - source->x); + ady = doom_abs(listener->y - source->y); + + // From _GG1_ p.428. Appox. eucledian distance fast. + approx_dist = adx + ady - ((adx < ady ? adx : ady) >> 1); + + if (gamemap != 8 + && approx_dist > S_CLIPPING_DIST) + { + return 0; + } + + // angle of source to listener + angle = R_PointToAngle2(listener->x, + listener->y, + source->x, + source->y); + + if (angle > listener->angle) + angle = angle - listener->angle; + else + angle = angle + (0xffffffff - listener->angle); + + angle >>= ANGLETOFINESHIFT; + + // stereo separation + *sep = 128 - (FixedMul(S_STEREO_SWING, finesine[angle]) >> FRACBITS); + + // volume calculation + if (approx_dist < S_CLOSE_DIST) + { + *vol = snd_SfxVolume; + } + else if (gamemap == 8) + { + if (approx_dist > S_CLIPPING_DIST) + approx_dist = S_CLIPPING_DIST; + + *vol = 15 + ((snd_SfxVolume - 15) + * ((S_CLIPPING_DIST - approx_dist) >> FRACBITS)) + / S_ATTENUATOR; + } + else + { + // distance effect + *vol = (snd_SfxVolume + * ((S_CLIPPING_DIST - approx_dist) >> FRACBITS)) + / S_ATTENUATOR; + } + + return (*vol > 0); +} + + +// +// S_getChannel : +// If none available, return -1. Otherwise channel #. +// +int S_getChannel(void* origin, sfxinfo_t* sfxinfo) +{ + // channel number to use + int cnum; + + channel_t* c; + + // Find an open channel + for (cnum = 0; cnum < numChannels; cnum++) + { + if (!channels_s_sound[cnum].sfxinfo) + break; + else if (origin && channels_s_sound[cnum].origin == origin) + { + S_StopChannel(cnum); + break; + } + } + + // None available + if (cnum == numChannels) + { + // Look for lower priority + for (cnum = 0; cnum < numChannels; cnum++) + if (channels_s_sound[cnum].sfxinfo->priority >= sfxinfo->priority) break; + + if (cnum == numChannels) + { + // FUCK! No lower priority. Sorry, Charlie. + return -1; + } + else + { + // Otherwise, kick out lower priority. + S_StopChannel(cnum); + } + } + + c = &channels_s_sound[cnum]; + + // channel is decided to be cnum. + c->sfxinfo = sfxinfo; + c->origin = origin; + + return cnum; +} +musicinfo_t S_music[] = +{ + { 0 }, + { "e1m1", 0 }, + { "e1m2", 0 }, + { "e1m3", 0 }, + { "e1m4", 0 }, + { "e1m5", 0 }, + { "e1m6", 0 }, + { "e1m7", 0 }, + { "e1m8", 0 }, + { "e1m9", 0 }, + { "e2m1", 0 }, + { "e2m2", 0 }, + { "e2m3", 0 }, + { "e2m4", 0 }, + { "e2m5", 0 }, + { "e2m6", 0 }, + { "e2m7", 0 }, + { "e2m8", 0 }, + { "e2m9", 0 }, + { "e3m1", 0 }, + { "e3m2", 0 }, + { "e3m3", 0 }, + { "e3m4", 0 }, + { "e3m5", 0 }, + { "e3m6", 0 }, + { "e3m7", 0 }, + { "e3m8", 0 }, + { "e3m9", 0 }, + { "inter", 0 }, + { "intro", 0 }, + { "bunny", 0 }, + { "victor", 0 }, + { "introa", 0 }, + { "runnin", 0 }, + { "stalks", 0 }, + { "countd", 0 }, + { "betwee", 0 }, + { "doom", 0 }, + { "the_da", 0 }, + { "shawn", 0 }, + { "ddtblu", 0 }, + { "in_cit", 0 }, + { "dead", 0 }, + { "stlks2", 0 }, + { "theda2", 0 }, + { "doom2", 0 }, + { "ddtbl2", 0 }, + { "runni2", 0 }, + { "dead2", 0 }, + { "stlks3", 0 }, + { "romero", 0 }, + { "shawn2", 0 }, + { "messag", 0 }, + { "count2", 0 }, + { "ddtbl3", 0 }, + { "ampie", 0 }, + { "theda3", 0 }, + { "adrian", 0 }, + { "messg2", 0 }, + { "romer2", 0 }, + { "tense", 0 }, + { "shawn3", 0 }, + { "openin", 0 }, + { "evil", 0 }, + { "ultima", 0 }, + { "read_m", 0 }, + { "dm2ttl", 0 }, + { "dm2int", 0 } +}; + + +// +// Information about all the sfx +// +sfxinfo_t S_sfx[] = +{ + // S_sfx[0] needs to be a dummy for odd reasons. + { "none", false, 0, 0, -1, -1, 0 }, + { "pistol", false, 64, 0, -1, -1, 0 }, + { "shotgn", false, 64, 0, -1, -1, 0 }, + { "sgcock", false, 64, 0, -1, -1, 0 }, + { "dshtgn", false, 64, 0, -1, -1, 0 }, + { "dbopn", false, 64, 0, -1, -1, 0 }, + { "dbcls", false, 64, 0, -1, -1, 0 }, + { "dbload", false, 64, 0, -1, -1, 0 }, + { "plasma", false, 64, 0, -1, -1, 0 }, + { "bfg", false, 64, 0, -1, -1, 0 }, + { "sawup", false, 64, 0, -1, -1, 0 }, + { "sawidl", false, 118, 0, -1, -1, 0 }, + { "sawful", false, 64, 0, -1, -1, 0 }, + { "sawhit", false, 64, 0, -1, -1, 0 }, + { "rlaunc", false, 64, 0, -1, -1, 0 }, + { "rxplod", false, 70, 0, -1, -1, 0 }, + { "firsht", false, 70, 0, -1, -1, 0 }, + { "firxpl", false, 70, 0, -1, -1, 0 }, + { "pstart", false, 100, 0, -1, -1, 0 }, + { "pstop", false, 100, 0, -1, -1, 0 }, + { "doropn", false, 100, 0, -1, -1, 0 }, + { "dorcls", false, 100, 0, -1, -1, 0 }, + { "stnmov", false, 119, 0, -1, -1, 0 }, + { "swtchn", false, 78, 0, -1, -1, 0 }, + { "swtchx", false, 78, 0, -1, -1, 0 }, + { "plpain", false, 96, 0, -1, -1, 0 }, + { "dmpain", false, 96, 0, -1, -1, 0 }, + { "popain", false, 96, 0, -1, -1, 0 }, + { "vipain", false, 96, 0, -1, -1, 0 }, + { "mnpain", false, 96, 0, -1, -1, 0 }, + { "pepain", false, 96, 0, -1, -1, 0 }, + { "slop", false, 78, 0, -1, -1, 0 }, + { "itemup", true, 78, 0, -1, -1, 0 }, + { "wpnup", true, 78, 0, -1, -1, 0 }, + { "oof", false, 96, 0, -1, -1, 0 }, + { "telept", false, 32, 0, -1, -1, 0 }, + { "posit1", true, 98, 0, -1, -1, 0 }, + { "posit2", true, 98, 0, -1, -1, 0 }, + { "posit3", true, 98, 0, -1, -1, 0 }, + { "bgsit1", true, 98, 0, -1, -1, 0 }, + { "bgsit2", true, 98, 0, -1, -1, 0 }, + { "sgtsit", true, 98, 0, -1, -1, 0 }, + { "cacsit", true, 98, 0, -1, -1, 0 }, + { "brssit", true, 94, 0, -1, -1, 0 }, + { "cybsit", true, 92, 0, -1, -1, 0 }, + { "spisit", true, 90, 0, -1, -1, 0 }, + { "bspsit", true, 90, 0, -1, -1, 0 }, + { "kntsit", true, 90, 0, -1, -1, 0 }, + { "vilsit", true, 90, 0, -1, -1, 0 }, + { "mansit", true, 90, 0, -1, -1, 0 }, + { "pesit", true, 90, 0, -1, -1, 0 }, + { "sklatk", false, 70, 0, -1, -1, 0 }, + { "sgtatk", false, 70, 0, -1, -1, 0 }, + { "skepch", false, 70, 0, -1, -1, 0 }, + { "vilatk", false, 70, 0, -1, -1, 0 }, + { "claw", false, 70, 0, -1, -1, 0 }, + { "skeswg", false, 70, 0, -1, -1, 0 }, + { "pldeth", false, 32, 0, -1, -1, 0 }, + { "pdiehi", false, 32, 0, -1, -1, 0 }, + { "podth1", false, 70, 0, -1, -1, 0 }, + { "podth2", false, 70, 0, -1, -1, 0 }, + { "podth3", false, 70, 0, -1, -1, 0 }, + { "bgdth1", false, 70, 0, -1, -1, 0 }, + { "bgdth2", false, 70, 0, -1, -1, 0 }, + { "sgtdth", false, 70, 0, -1, -1, 0 }, + { "cacdth", false, 70, 0, -1, -1, 0 }, + { "skldth", false, 70, 0, -1, -1, 0 }, + { "brsdth", false, 32, 0, -1, -1, 0 }, + { "cybdth", false, 32, 0, -1, -1, 0 }, + { "spidth", false, 32, 0, -1, -1, 0 }, + { "bspdth", false, 32, 0, -1, -1, 0 }, + { "vildth", false, 32, 0, -1, -1, 0 }, + { "kntdth", false, 32, 0, -1, -1, 0 }, + { "pedth", false, 32, 0, -1, -1, 0 }, + { "skedth", false, 32, 0, -1, -1, 0 }, + { "posact", true, 120, 0, -1, -1, 0 }, + { "bgact", true, 120, 0, -1, -1, 0 }, + { "dmact", true, 120, 0, -1, -1, 0 }, + { "bspact", true, 100, 0, -1, -1, 0 }, + { "bspwlk", true, 100, 0, -1, -1, 0 }, + { "vilact", true, 100, 0, -1, -1, 0 }, + { "noway", false, 78, 0, -1, -1, 0 }, + { "barexp", false, 60, 0, -1, -1, 0 }, + { "punch", false, 64, 0, -1, -1, 0 }, + { "hoof", false, 70, 0, -1, -1, 0 }, + { "metal", false, 70, 0, -1, -1, 0 }, + { "chgun", false, 64, &S_sfx[sfx_pistol], 150, 0, 0 }, + { "tink", false, 60, 0, -1, -1, 0 }, + { "bdopn", false, 100, 0, -1, -1, 0 }, + { "bdcls", false, 100, 0, -1, -1, 0 }, + { "itmbk", false, 100, 0, -1, -1, 0 }, + { "flame", false, 32, 0, -1, -1, 0 }, + { "flamst", false, 32, 0, -1, -1, 0 }, + { "getpow", false, 60, 0, -1, -1, 0 }, + { "bospit", false, 70, 0, -1, -1, 0 }, + { "boscub", false, 70, 0, -1, -1, 0 }, + { "bossit", false, 70, 0, -1, -1, 0 }, + { "bospn", false, 70, 0, -1, -1, 0 }, + { "bosdth", false, 70, 0, -1, -1, 0 }, + { "manatk", false, 70, 0, -1, -1, 0 }, + { "mandth", false, 70, 0, -1, -1, 0 }, + { "sssit", false, 70, 0, -1, -1, 0 }, + { "ssdth", false, 70, 0, -1, -1, 0 }, + { "keenpn", false, 70, 0, -1, -1, 0 }, + { "keendt", false, 70, 0, -1, -1, 0 }, + { "skeact", false, 70, 0, -1, -1, 0 }, + { "skesit", false, 70, 0, -1, -1, 0 }, + { "skeatk", false, 70, 0, -1, -1, 0 }, + { "radio", false, 60, 0, -1, -1, 0 } +}; +extern doom_boolean automapactive; + + +// +// Hack display negative frags. +// Loads and store the stminus lump. +// +patch_t* sttminus; + + +void STlib_init(void) +{ + sttminus = (patch_t*)W_CacheLumpName("STTMINUS", PU_STATIC); +} + +// ? +void STlib_initNum(st_number_t* n, int x, int y, patch_t** pl, int* num, doom_boolean* on, int width) +{ + n->x = x; + n->y = y; + n->oldnum = 0; + n->width = width; + n->num = num; + n->on = on; + n->p = pl; +} + +// +// A fairly efficient way to draw a number +// based on differences from the old number. +// Note: worth the trouble? +// +void STlib_drawNum(st_number_t* n, doom_boolean refresh) +{ + int numdigits = n->width; + int num = *n->num; + + int w = SHORT(n->p[0]->width); + int h = SHORT(n->p[0]->height); + int x = n->x; + + int neg; + + n->oldnum = *n->num; + + neg = num < 0; + + if (neg) + { + if (numdigits == 2 && num < -9) + num = -9; + else if (numdigits == 3 && num < -99) + num = -99; + + num = -num; + } + + // clear the area + x = n->x - numdigits * w; + + if (n->y - ST_Y < 0) + I_Error("Error: drawNum: n->y - ST_Y < 0"); + + V_CopyRect(x, n->y - ST_Y, STLIB_BG, w * numdigits, h, x, n->y, STLIB_FG); + + // if non-number, do not draw it + if (num == 1994) + return; + + x = n->x; + + // in the special case of 0, you draw 0 + if (!num) + V_DrawPatch(x - w, n->y, STLIB_FG, n->p[0]); + + // draw the new number + while (num && numdigits--) + { + x -= w; + V_DrawPatch(x, n->y, STLIB_FG, n->p[num % 10]); + num /= 10; + } + + // draw a minus sign if necessary + if (neg) + V_DrawPatch(x - 8, n->y, STLIB_FG, sttminus); +} + +// +void STlib_updateNum(st_number_t* n, doom_boolean refresh) +{ + if (*n->on) STlib_drawNum(n, refresh); +} + +// +void STlib_initPercent(st_percent_t* p, int x, int y, patch_t** pl, int* num, doom_boolean* on, patch_t* percent) +{ + STlib_initNum(&p->n, x, y, pl, num, on, 3); + p->p = percent; +} + +void STlib_updatePercent(st_percent_t* per, int refresh) +{ + if (refresh && *per->n.on) + V_DrawPatch(per->n.x, per->n.y, STLIB_FG, per->p); + + STlib_updateNum(&per->n, refresh); +} + +void STlib_initMultIcon(st_multicon_t* i, int x, int y, patch_t** il, int* inum, doom_boolean* on) +{ + i->x = x; + i->y = y; + i->oldinum = -1; + i->inum = inum; + i->on = on; + i->p = il; +} + +void STlib_updateMultIcon(st_multicon_t* mi, doom_boolean refresh) +{ + int w; + int h; + int x; + int y; + + if (*mi->on + && (mi->oldinum != *mi->inum || refresh) + && (*mi->inum != -1)) + { + if (mi->oldinum != -1) + { + x = mi->x - SHORT(mi->p[mi->oldinum]->leftoffset); + y = mi->y - SHORT(mi->p[mi->oldinum]->topoffset); + w = SHORT(mi->p[mi->oldinum]->width); + h = SHORT(mi->p[mi->oldinum]->height); + + if (y - ST_Y < 0) + I_Error("Error: updateMultIcon: y - ST_Y < 0"); + + V_CopyRect(x, y - ST_Y, STLIB_BG, w, h, x, y, STLIB_FG); + } + V_DrawPatch(mi->x, mi->y, STLIB_FG, mi->p[*mi->inum]); + mi->oldinum = *mi->inum; + } +} + +void STlib_initBinIcon(st_binicon_t* b, int x, int y, patch_t* i, doom_boolean* val, doom_boolean* on) +{ + b->x = x; + b->y = y; + b->oldval = 0; + b->val = val; + b->on = on; + b->p = i; +} + +void STlib_updateBinIcon(st_binicon_t* bi, doom_boolean refresh) +{ + int x; + int y; + int w; + int h; + + if (*bi->on && (bi->oldval != *bi->val || refresh)) + { + x = bi->x - SHORT(bi->p->leftoffset); + y = bi->y - SHORT(bi->p->topoffset); + w = SHORT(bi->p->width); + h = SHORT(bi->p->height); + + if (y - ST_Y < 0) + I_Error("Error: updateBinIcon: y - ST_Y < 0"); + + if (*bi->val) + V_DrawPatch(bi->x, bi->y, STLIB_FG, bi->p); + else + V_CopyRect(x, y - ST_Y, STLIB_BG, w, h, x, y, STLIB_FG); + + bi->oldval = *bi->val; + } +} +#define STARTREDPALS 1 +#define STARTBONUSPALS 9 +#define NUMREDPALS 8 +#define NUMBONUSPALS 4 +// Radiation suit, green shift. +#define RADIATIONPAL 13 + +// N/256*100% probability +// that the normal face state will change +#define ST_FACEPROBABILITY 96 + +// For Responder +#define ST_TOGGLECHAT KEY_ENTER + +// Location of status bar +#define ST_X 0 +#define ST_X2 104 + +#define ST_FX 143 +#define ST_FY 169 + +// Should be set to patch width +// for tall numbers later on +#define ST_TALLNUMWIDTH (tallnum[0]->width) + +// Number of status faces. +#define ST_NUMPAINFACES 5 +#define ST_NUMSTRAIGHTFACES 3 +#define ST_NUMTURNFACES 2 +#define ST_NUMSPECIALFACES 3 + +#define ST_FACESTRIDE (ST_NUMSTRAIGHTFACES + ST_NUMTURNFACES + ST_NUMSPECIALFACES) + +#define ST_NUMEXTRAFACES 2 + +#define ST_NUMFACES (ST_FACESTRIDE * ST_NUMPAINFACES + ST_NUMEXTRAFACES) + +#define ST_TURNOFFSET (ST_NUMSTRAIGHTFACES) +#define ST_OUCHOFFSET (ST_TURNOFFSET + ST_NUMTURNFACES) +#define ST_EVILGRINOFFSET (ST_OUCHOFFSET + 1) +#define ST_RAMPAGEOFFSET (ST_EVILGRINOFFSET + 1) +#define ST_GODFACE (ST_NUMPAINFACES*ST_FACESTRIDE) +#define ST_DEADFACE (ST_GODFACE+1) + +#define ST_FACESX 143 +#define ST_FACESY 168 + +#define ST_EVILGRINCOUNT (2*TICRATE) +#define ST_STRAIGHTFACECOUNT (TICRATE/2) +#define ST_TURNCOUNT (1*TICRATE) +#define ST_OUCHCOUNT (1*TICRATE) +#define ST_RAMPAGEDELAY (2*TICRATE) + +#define ST_MUCHPAIN 20 + +// Location and size of statistics, +// justified according to widget type. +// Problem is, within which space? STbar? Screen? +// Note: this could be read in by a lump. +// Problem is, is the stuff rendered +// into a buffer, +// or into the frame buffer? + +// AMMO number pos. +#define ST_AMMOWIDTH 3 +#define ST_AMMOX 44 +#define ST_AMMOY 171 + +// HEALTH number pos. +#define ST_HEALTHWIDTH 3 +#define ST_HEALTHX 90 +#define ST_HEALTHY 171 + +// Weapon pos. +#define ST_ARMSX 111 +#define ST_ARMSY 172 +#define ST_ARMSBGX 104 +#define ST_ARMSBGY 168 +#define ST_ARMSXSPACE 12 +#define ST_ARMSYSPACE 10 + +// Frags pos. +#define ST_FRAGSX 138 +#define ST_FRAGSY 171 +#define ST_FRAGSWIDTH 2 + +// ARMOR number pos. +#define ST_ARMORWIDTH 3 +#define ST_ARMORX 221 +#define ST_ARMORY 171 + +// Key icon positions. +#define ST_KEY0WIDTH 8 +#define ST_KEY0HEIGHT 5 +#define ST_KEY0X 239 +#define ST_KEY0Y 171 +#define ST_KEY1WIDTH ST_KEY0WIDTH +#define ST_KEY1X 239 +#define ST_KEY1Y 181 +#define ST_KEY2WIDTH ST_KEY0WIDTH +#define ST_KEY2X 239 +#define ST_KEY2Y 191 + +// Ammunition counter. +#define ST_AMMO0WIDTH 3 +#define ST_AMMO0HEIGHT 6 +#define ST_AMMO0X 288 +#define ST_AMMO0Y 173 +#define ST_AMMO1WIDTH ST_AMMO0WIDTH +#define ST_AMMO1X 288 +#define ST_AMMO1Y 179 +#define ST_AMMO2WIDTH ST_AMMO0WIDTH +#define ST_AMMO2X 288 +#define ST_AMMO2Y 191 +#define ST_AMMO3WIDTH ST_AMMO0WIDTH +#define ST_AMMO3X 288 +#define ST_AMMO3Y 185 + +// Indicate maximum ammunition. +// Only needed because backpack exists. +#define ST_MAXAMMO0WIDTH 3 +#define ST_MAXAMMO0HEIGHT 5 +#define ST_MAXAMMO0X 314 +#define ST_MAXAMMO0Y 173 +#define ST_MAXAMMO1WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO1X 314 +#define ST_MAXAMMO1Y 179 +#define ST_MAXAMMO2WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO2X 314 +#define ST_MAXAMMO2Y 191 +#define ST_MAXAMMO3WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO3X 314 +#define ST_MAXAMMO3Y 185 + +// pistol +#define ST_WEAPON0X 110 +#define ST_WEAPON0Y 172 + +// shotgun +#define ST_WEAPON1X 122 +#define ST_WEAPON1Y 172 + +// chain gun +#define ST_WEAPON2X 134 +#define ST_WEAPON2Y 172 + +// missile launcher +#define ST_WEAPON3X 110 +#define ST_WEAPON3Y 181 + +// plasma gun +#define ST_WEAPON4X 122 +#define ST_WEAPON4Y 181 + + // bfg +#define ST_WEAPON5X 134 +#define ST_WEAPON5Y 181 + +// WPNS title +#define ST_WPNSX 109 +#define ST_WPNSY 191 + + // DETH title +#define ST_DETHX 109 +#define ST_DETHY 191 + +//Incoming messages window location +#define ST_MSGTEXTX 0 +#define ST_MSGTEXTY 0 +// Dimensions given in characters. +#define ST_MSGWIDTH 52 +// Or shall I say, in lines? +#define ST_MSGHEIGHT 1 + +#define ST_OUTTEXTX 0 +#define ST_OUTTEXTY 6 + +// Width, in characters again. +#define ST_OUTWIDTH 52 + // Height, in lines. +#define ST_OUTHEIGHT 1 + +#define ST_MAPWIDTH (doom_strlen(mapnames[(gameepisode - 1) * 9 + (gamemap - 1)])) + +#define ST_MAPTITLEX (SCREENWIDTH - ST_MAPWIDTH * ST_CHATFONTWIDTH) + +#define ST_MAPTITLEY 0 +#define ST_MAPHEIGHT 1 + + +static player_t* plyr; // main player in game +static doom_boolean st_firsttime; // ST_Start() has just been called +static int veryfirsttime = 1; // used to execute ST_Init() only once +static int lu_palette; // lump number for PLAYPAL +static unsigned int st_clock; // used for timing +static int st_msgcounter = 0; // used for making messages go away +static st_chatstateenum_t st_chatstate; // used when in chat +static st_stateenum_t st_gamestate; // whether in automap or first-person +static doom_boolean st_statusbaron; // whether left-side main status bar is active +static doom_boolean st_chat; // whether status bar chat is active +static doom_boolean st_oldchat; // value of st_chat before message popped up +static doom_boolean st_cursoron; // whether chat window has the cursor on +static doom_boolean st_notdeathmatch; // !deathmatch +static doom_boolean st_armson; // !deathmatch && st_statusbaron +static doom_boolean st_fragson; // !deathmatch +static patch_t* sbar; // main bar left +static patch_t* tallnum[10]; // 0-9, tall numbers +static patch_t* tallpercent; // tall % sign +static patch_t* shortnum[10]; // 0-9, short, yellow (,different!) numbers +static patch_t* keys[NUMCARDS]; // 3 key-cards, 3 skulls +static patch_t* faces[ST_NUMFACES]; // face status patches +static patch_t* faceback; // face background +static patch_t* armsbg; // main bar right +static patch_t* arms[6][2]; // weapon ownership patches +static st_number_t w_ready; // ready-weapon widget +static st_number_t w_frags; // in deathmatch only, summary of frags stats +static st_percent_t w_health; // health widget +static st_binicon_t w_armsbg; // arms background +static st_multicon_t w_arms[6]; // weapon ownership widgets +static st_multicon_t w_faces; // face status widget +static st_multicon_t w_keyboxes[3]; // keycard widgets +static st_percent_t w_armor; // armor widget +static st_number_t w_ammo[4]; // ammo widgets +static st_number_t w_maxammo[4]; // max ammo widgets +static int st_fragscount; // number of frags so far in deathmatch +static int st_oldhealth = -1; // used to use appopriately pained face +static doom_boolean oldweaponsowned[NUMWEAPONS]; // used for evil grin +static int st_facecount = 0; // count until face changes +static int st_faceindex = 0; // current face index, used by w_faces +static int keyboxes[3]; // holds key-type for each key box on bar +static int st_randomnumber; // a random number per tick +static int st_palette = 0; +static doom_boolean st_stopped = true; + + +// Massive bunches of cheat shit +// to keep it from being easy to figure them out. +// Yeah, right... +unsigned char cheat_mus_seq[] = +{ + 0xb2, 0x26, 0xb6, 0xae, 0xea, 1, 0, 0, 0xff +}; + +unsigned char cheat_choppers_seq[] = +{ + 0xb2, 0x26, 0xe2, 0x32, 0xf6, 0x2a, 0x2a, 0xa6, 0x6a, 0xea, 0xff // id... +}; + +unsigned char cheat_god_seq[] = +{ + 0xb2, 0x26, 0x26, 0xaa, 0x26, 0xff // iddqd +}; + +unsigned char cheat_ammo_seq[] = +{ + 0xb2, 0x26, 0xf2, 0x66, 0xa2, 0xff // idkfa +}; + +unsigned char cheat_ammonokey_seq[] = +{ + 0xb2, 0x26, 0x66, 0xa2, 0xff // idfa +}; + +// Smashing Pumpkins Into Samml Piles Of Putried Debris. +unsigned char cheat_noclip_seq[] = +{ + 0xb2, 0x26, 0xea, 0x2a, 0xb2, // idspispopd + 0xea, 0x2a, 0xf6, 0x2a, 0x26, 0xff +}; + +// +unsigned char cheat_commercial_noclip_seq[] = +{ + 0xb2, 0x26, 0xe2, 0x36, 0xb2, 0x2a, 0xff // idclip +}; + +unsigned char cheat_powerup_seq[7][10] = +{ + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x6e, 0xff }, // beholdv + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xea, 0xff }, // beholds + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xb2, 0xff }, // beholdi + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x6a, 0xff }, // beholdr + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xa2, 0xff }, // beholda + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x36, 0xff }, // beholdl + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xff } // behold +}; + +unsigned char cheat_clev_seq[] = +{ + 0xb2, 0x26, 0xe2, 0x36, 0xa6, 0x6e, 1, 0, 0, 0xff // idclev +}; + +// my position cheat +unsigned char cheat_mypos_seq[] = +{ + 0xb2, 0x26, 0xb6, 0xba, 0x2a, 0xf6, 0xea, 0xff // idmypos +}; + +// Now what? +cheatseq_t cheat_mus = { cheat_mus_seq, 0 }; +cheatseq_t cheat_god = { cheat_god_seq, 0 }; +cheatseq_t cheat_ammo = { cheat_ammo_seq, 0 }; +cheatseq_t cheat_ammonokey = { cheat_ammonokey_seq, 0 }; +cheatseq_t cheat_noclip = { cheat_noclip_seq, 0 }; +cheatseq_t cheat_commercial_noclip = { cheat_commercial_noclip_seq, 0 }; + +cheatseq_t cheat_powerup[7] = +{ + { cheat_powerup_seq[0], 0 }, + { cheat_powerup_seq[1], 0 }, + { cheat_powerup_seq[2], 0 }, + { cheat_powerup_seq[3], 0 }, + { cheat_powerup_seq[4], 0 }, + { cheat_powerup_seq[5], 0 }, + { cheat_powerup_seq[6], 0 } +}; + +cheatseq_t cheat_choppers = { cheat_choppers_seq, 0 }; +cheatseq_t cheat_clev = { cheat_clev_seq, 0 }; +cheatseq_t cheat_mypos = { cheat_mypos_seq, 0 }; + + +extern char* mapnames[]; +extern int doom_flags; + + +void ST_Stop(void); + + +// +// STATUS BAR CODE +// + +void ST_refreshBackground(void) +{ + if (st_statusbaron) + { + V_DrawPatch(ST_X, 0, STLIB_BG, sbar); + + if (netgame) + V_DrawPatch(ST_FX, 0, STLIB_BG, faceback); + + V_CopyRect(ST_X, 0, STLIB_BG, ST_WIDTH, ST_HEIGHT, ST_X, ST_Y, STLIB_FG); + } +} + + +// Respond to keyboard input events, +// intercept cheats. +doom_boolean ST_Responder(event_t* ev) +{ + int i; + + // Filter automap on/off. + if (ev->type == ev_keyup + && ((ev->data1 & 0xffff0000) == AM_MSGHEADER)) + { + switch (ev->data1) + { + case AM_MSGENTERED: + st_gamestate = AutomapState; + st_firsttime = true; + break; + + case AM_MSGEXITED: + // doom_print("AM exited\n"); + st_gamestate = FirstPersonState; + break; + } + } + + // if a user keypress... + else if (ev->type == ev_keydown) + { + if (!netgame) + { + // b. - enabled for more debug fun. + // if (gameskill != sk_nightmare) { + + // 'dqd' cheat for toggleable god mode + if (cht_CheckCheat(&cheat_god, ev->data1)) + { + plyr->cheats ^= CF_GODMODE; + if (plyr->cheats & CF_GODMODE) + { + if (plyr->mo) + plyr->mo->health = 100; + + plyr->health = 100; + plyr->message = STSTR_DQDON; + } + else + plyr->message = STSTR_DQDOFF; + } + // 'fa' cheat for killer fucking arsenal + else if (cht_CheckCheat(&cheat_ammonokey, ev->data1)) + { + plyr->armorpoints = 200; + plyr->armortype = 2; + + for (i = 0; i < NUMWEAPONS; i++) + plyr->weaponowned[i] = true; + + for (i = 0; i < NUMAMMO; i++) + plyr->ammo[i] = plyr->maxammo[i]; + + plyr->message = STSTR_FAADDED; + } + // 'kfa' cheat for key full ammo + else if (cht_CheckCheat(&cheat_ammo, ev->data1)) + { + plyr->armorpoints = 200; + plyr->armortype = 2; + + for (i = 0; i < NUMWEAPONS; i++) + plyr->weaponowned[i] = true; + + for (i = 0; i < NUMAMMO; i++) + plyr->ammo[i] = plyr->maxammo[i]; + + for (i = 0; i < NUMCARDS; i++) + plyr->cards[i] = true; + + plyr->message = STSTR_KFAADDED; + } + // 'mus' cheat for changing music + else if (cht_CheckCheat(&cheat_mus, ev->data1)) + { + char buf[3]; + int musnum; + + plyr->message = STSTR_MUS; + cht_GetParam(&cheat_mus, buf); + + if (gamemode == commercial) + { + musnum = mus_runnin + (buf[0] - '0') * 10 + buf[1] - '0' - 1; + + if (((buf[0] - '0') * 10 + buf[1] - '0') > 35) + plyr->message = STSTR_NOMUS; + else + S_ChangeMusic(musnum, 1); + } + else + { + musnum = mus_e1m1 + (buf[0] - '1') * 9 + (buf[1] - '1'); + + if (((buf[0] - '1') * 9 + buf[1] - '1') > 31) + plyr->message = STSTR_NOMUS; + else + S_ChangeMusic(musnum, 1); + } + } + // Simplified, accepting both "noclip" and "idspispopd". + // no clipping mode cheat + else if (cht_CheckCheat(&cheat_noclip, ev->data1) + || cht_CheckCheat(&cheat_commercial_noclip, ev->data1)) + { + plyr->cheats ^= CF_NOCLIP; + + if (plyr->cheats & CF_NOCLIP) + plyr->message = STSTR_NCON; + else + plyr->message = STSTR_NCOFF; + } + // 'behold?' power-up cheats + for (i = 0; i < 6; i++) + { + if (cht_CheckCheat(&cheat_powerup[i], ev->data1)) + { + if (!plyr->powers[i]) + P_GivePower(plyr, i); + else if (i != pw_strength) + plyr->powers[i] = 1; + else + plyr->powers[i] = 0; + + plyr->message = STSTR_BEHOLDX; + } + } + + // 'behold' power-up menu + if (cht_CheckCheat(&cheat_powerup[6], ev->data1)) + { + plyr->message = STSTR_BEHOLD; + } + // 'choppers' invulnerability & chainsaw + else if (cht_CheckCheat(&cheat_choppers, ev->data1)) + { + plyr->weaponowned[wp_chainsaw] = true; + plyr->powers[pw_invulnerability] = true; + plyr->message = STSTR_CHOPPERS; + } + // 'mypos' for player position + else if (cht_CheckCheat(&cheat_mypos, ev->data1)) + { + static char buf[ST_MSGWIDTH]; + //doom_sprintf(buf, "ang=0x%x;x,y=(0x%x,0x%x)", + // players[consoleplayer].mo->angle, + // players[consoleplayer].mo->x, + // players[consoleplayer].mo->y); + doom_strcpy(buf, "ang=0x"); + doom_concat(buf, doom_itoa(players[consoleplayer].mo->angle, 16)); + doom_concat(buf, ";x,y=(0x"); + doom_concat(buf, doom_itoa(players[consoleplayer].mo->x, 16)); + doom_concat(buf, ",0x"); + doom_concat(buf, doom_itoa(players[consoleplayer].mo->y, 16)); + doom_concat(buf, ")"); + plyr->message = buf; + } + } + + // 'clev' change-level cheat + if (cht_CheckCheat(&cheat_clev, ev->data1)) + { + char buf[3]; + int epsd; + int map; + + cht_GetParam(&cheat_clev, buf); + + if (gamemode == commercial) + { + epsd = 0; + map = (buf[0] - '0') * 10 + buf[1] - '0'; + } + else + { + epsd = buf[0] - '0'; + map = buf[1] - '0'; + } + + // Catch invalid maps. + if (epsd < 1) + return false; + + if (map < 1) + return false; + + // Ohmygod - this is not going to work. + if ((gamemode == retail) + && ((epsd > 4) || (map > 9))) + return false; + + if ((gamemode == registered) + && ((epsd > 3) || (map > 9))) + return false; + + if ((gamemode == shareware) + && ((epsd > 1) || (map > 9))) + return false; + + if ((gamemode == commercial) + && ((epsd > 1) || (map > 34))) + return false; + + // So be it. + plyr->message = STSTR_CLEV; + G_DeferedInitNew(gameskill, epsd, map); + } + } + return false; +} + + +int ST_calcPainOffset(void) +{ + int health; + static int lastcalc; + static int oldhealth = -1; + + health = plyr->health > 100 ? 100 : plyr->health; + + if (health != oldhealth) + { + lastcalc = ST_FACESTRIDE * (((100 - health) * ST_NUMPAINFACES) / 101); + oldhealth = health; + } + return lastcalc; +} + + +// +// This is a not-very-pretty routine which handles +// the face states and their timing. +// the precedence of expressions is: +// dead > evil grin > turned head > straight ahead +// +void ST_updateFaceWidget(void) +{ + int i; + angle_t badguyangle; + angle_t diffang; + static int lastattackdown = -1; + static int priority = 0; + doom_boolean doevilgrin; + + if (priority < 10) + { + // dead + if (!plyr->health) + { + priority = 9; + st_faceindex = ST_DEADFACE; + st_facecount = 1; + } + } + + if (priority < 9) + { + if (plyr->bonuscount) + { + // picking up bonus + doevilgrin = false; + + for (i = 0; i < NUMWEAPONS; i++) + { + if (oldweaponsowned[i] != plyr->weaponowned[i]) + { + doevilgrin = true; + oldweaponsowned[i] = plyr->weaponowned[i]; + } + } + if (doevilgrin) + { + // evil grin if just picked up weapon + priority = 8; + st_facecount = ST_EVILGRINCOUNT; + st_faceindex = ST_calcPainOffset() + ST_EVILGRINOFFSET; + } + } + + } + + if (priority < 8) + { + if (plyr->damagecount + && plyr->attacker + && plyr->attacker != plyr->mo) + { + // being attacked + priority = 7; + + if (plyr->health - st_oldhealth > ST_MUCHPAIN) + { + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; + } + else + { + badguyangle = R_PointToAngle2(plyr->mo->x, + plyr->mo->y, + plyr->attacker->x, + plyr->attacker->y); + + if (badguyangle > plyr->mo->angle) + { + // whether right or left + diffang = badguyangle - plyr->mo->angle; + i = diffang > ANG180; + } + else + { + // whether left or right + diffang = plyr->mo->angle - badguyangle; + i = diffang <= ANG180; + } // confusing, aint it? + + + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset(); + + if (diffang < ANG45) + { + // head-on + st_faceindex += ST_RAMPAGEOFFSET; + } + else if (i) + { + // turn face right + st_faceindex += ST_TURNOFFSET; + } + else + { + // turn face left + st_faceindex += ST_TURNOFFSET + 1; + } + } + } + } + + if (priority < 7) + { + // getting hurt because of your own damn stupidity + if (plyr->damagecount) + { + if (plyr->health - st_oldhealth > ST_MUCHPAIN) + { + priority = 7; + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; + } + else + { + priority = 6; + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET; + } + + } + + } + + if (priority < 6) + { + // rapid firing + if (plyr->attackdown) + { + if (lastattackdown == -1) + lastattackdown = ST_RAMPAGEDELAY; + else if (!--lastattackdown) + { + priority = 5; + st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET; + st_facecount = 1; + lastattackdown = 1; + } + } + else + lastattackdown = -1; + + } + + if (priority < 5) + { + // invulnerability + if ((plyr->cheats & CF_GODMODE) + || plyr->powers[pw_invulnerability]) + { + priority = 4; + + st_faceindex = ST_GODFACE; + st_facecount = 1; + } + } + + // look left or look right if the facecount has timed out + if (!st_facecount) + { + st_faceindex = ST_calcPainOffset() + (st_randomnumber % 3); + st_facecount = ST_STRAIGHTFACECOUNT; + priority = 0; + } + + st_facecount--; +} + + +void ST_updateWidgets(void) +{ + static int largeammo = 1994; // means "n/a" + int i; + + if (weaponinfo[plyr->readyweapon].ammo == am_noammo) + w_ready.num = &largeammo; + else + w_ready.num = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo]; + + w_ready.data = plyr->readyweapon; + + // update keycard multiple widgets + for (i = 0; i < 3; i++) + { + keyboxes[i] = plyr->cards[i] ? i : -1; + + if (plyr->cards[i + 3]) + keyboxes[i] = i + 3; + } + + // refresh everything if this is him coming back to life + ST_updateFaceWidget(); + + // used by the w_armsbg widget + st_notdeathmatch = !deathmatch; + + // used by w_arms[] widgets + st_armson = st_statusbaron && !deathmatch; + + // used by w_frags widget + st_fragson = deathmatch && st_statusbaron; + st_fragscount = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (i != consoleplayer) + st_fragscount += plyr->frags[i]; + else + st_fragscount -= plyr->frags[i]; + } + + // get rid of chat window if up because of message + if (!--st_msgcounter) + st_chat = st_oldchat; +} + + +void ST_Ticker(void) +{ + st_clock++; + st_randomnumber = M_Random(); + ST_updateWidgets(); + st_oldhealth = plyr->health; +} + + +void ST_doPaletteStuff(void) +{ + int palette; + byte* pal; + int cnt; + int bzc; + + cnt = plyr->damagecount; + + if (plyr->powers[pw_strength]) + { + // slowly fade the berzerk out + bzc = 12 - (plyr->powers[pw_strength] >> 6); + + if (bzc > cnt) + cnt = bzc; + } + + if (cnt) + { + palette = (cnt + 7) >> 3; + + if (palette >= NUMREDPALS) + palette = NUMREDPALS - 1; + + palette += STARTREDPALS; + } + + else if (plyr->bonuscount) + { + palette = (plyr->bonuscount + 7) >> 3; + + if (palette >= NUMBONUSPALS) + palette = NUMBONUSPALS - 1; + + palette += STARTBONUSPALS; + } + + else if (plyr->powers[pw_ironfeet] > 4 * 32 + || plyr->powers[pw_ironfeet] & 8) + palette = RADIATIONPAL; + else + palette = 0; + + if (palette != st_palette) + { + st_palette = palette; + pal = (byte*)W_CacheLumpNum(lu_palette, PU_CACHE) + palette * 768; + I_SetPalette(pal); + } +} + + +void ST_drawWidgets(doom_boolean refresh) +{ + int i; + + // used by w_arms[] widgets + st_armson = st_statusbaron && !deathmatch; + + // used by w_frags widget + st_fragson = deathmatch && st_statusbaron; + + STlib_updateNum(&w_ready, refresh); + + for (i = 0; i < 4; i++) + { + STlib_updateNum(&w_ammo[i], refresh); + STlib_updateNum(&w_maxammo[i], refresh); + } + + STlib_updatePercent(&w_health, refresh); + STlib_updatePercent(&w_armor, refresh); + + STlib_updateBinIcon(&w_armsbg, refresh); + + for (i = 0; i < 6; i++) + STlib_updateMultIcon(&w_arms[i], refresh); + + STlib_updateMultIcon(&w_faces, refresh); + + for (i = 0; i < 3; i++) + STlib_updateMultIcon(&w_keyboxes[i], refresh); + + STlib_updateNum(&w_frags, refresh); +} + + +void ST_doRefresh(void) +{ + st_firsttime = false; + + // draw status bar background to off-screen buff + ST_refreshBackground(); + + // and refresh all widgets + ST_drawWidgets(true); +} + + +void ST_diffDraw(void) +{ + // update all widgets + ST_drawWidgets(false); +} + + +void ST_Drawer(doom_boolean fullscreen, doom_boolean refresh) +{ + st_statusbaron = (!fullscreen) || automapactive; + st_firsttime = st_firsttime || refresh; + + // Do red-/gold-shifts from damage/items + ST_doPaletteStuff(); + + // If just after ST_Start(), refresh all + if (doom_flags & DOOM_FLAG_MENU_DARKEN_BG) + { + ST_doRefresh(); + } + else + { + if (st_firsttime) ST_doRefresh(); + // Otherwise, update as little as possible + else ST_diffDraw(); + } +} + + +void ST_loadGraphics(void) +{ + int i; + int j; + int facenum; + + char namebuf[9]; + + // Load the numbers, tall and short + for (i = 0; i < 10; i++) + { + //doom_sprintf(namebuf, "STTNUM%d", i); + doom_strcpy(namebuf, "STTNUM"); + doom_concat(namebuf, doom_itoa(i, 10)); + tallnum[i] = (patch_t*)W_CacheLumpName(namebuf, PU_STATIC); + + //doom_sprintf(namebuf, "STYSNUM%d", i); + doom_strcpy(namebuf, "STYSNUM"); + doom_concat(namebuf, doom_itoa(i, 10)); + shortnum[i] = (patch_t*)W_CacheLumpName(namebuf, PU_STATIC); + } + + // Load percent key. + //Note: why not load STMINUS here, too? + tallpercent = (patch_t*)W_CacheLumpName("STTPRCNT", PU_STATIC); + + // key cards + for (i = 0; i < NUMCARDS; i++) + { + //doom_sprintf(namebuf, "STKEYS%d", i); + doom_strcpy(namebuf, "STKEYS"); + doom_concat(namebuf, doom_itoa(i, 10)); + keys[i] = (patch_t*)W_CacheLumpName(namebuf, PU_STATIC); + } + + // arms background + armsbg = (patch_t*)W_CacheLumpName("STARMS", PU_STATIC); + + // arms ownership widgets + for (i = 0; i < 6; i++) + { + //doom_sprintf(namebuf, "STGNUM%d", i + 2); + doom_strcpy(namebuf, "STGNUM"); + doom_concat(namebuf, doom_itoa(i + 2, 10)); + + // gray # + arms[i][0] = (patch_t*)W_CacheLumpName(namebuf, PU_STATIC); + + // yellow # + arms[i][1] = shortnum[i + 2]; + } + + // face backgrounds for different color players + //doom_sprintf(namebuf, "STFB%d", consoleplayer); + doom_strcpy(namebuf, "STFB"); + doom_concat(namebuf, doom_itoa(consoleplayer, 10)); + faceback = (patch_t*)W_CacheLumpName(namebuf, PU_STATIC); + + // status bar background bits + sbar = (patch_t*)W_CacheLumpName("STBAR", PU_STATIC); + + // face states + facenum = 0; + for (i = 0; i < ST_NUMPAINFACES; i++) + { + for (j = 0; j < ST_NUMSTRAIGHTFACES; j++) + { + //doom_sprintf(namebuf, "STFST%d%d", i, j); + doom_strcpy(namebuf, "STFST"); + doom_concat(namebuf, doom_itoa(i, 10)); + doom_concat(namebuf, doom_itoa(j, 10)); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + } + //doom_sprintf(namebuf, "STFTR%d0", i); // turn right + doom_strcpy(namebuf, "STFTR"); + doom_concat(namebuf, doom_itoa(i, 10)); + doom_concat(namebuf, "0"); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + //doom_sprintf(namebuf, "STFTL%d0", i); // turn left + doom_strcpy(namebuf, "STFTL"); + doom_concat(namebuf, doom_itoa(i, 10)); + doom_concat(namebuf, "0"); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + //doom_sprintf(namebuf, "STFOUCH%d", i); // ouch! + doom_strcpy(namebuf, "STFOUCH"); + doom_concat(namebuf, doom_itoa(i, 10)); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + //doom_sprintf(namebuf, "STFEVL%d", i); // evil grin ;) + doom_strcpy(namebuf, "STFEVL"); + doom_concat(namebuf, doom_itoa(i, 10)); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + //doom_sprintf(namebuf, "STFKILL%d", i); // pissed off + doom_strcpy(namebuf, "STFKILL"); + doom_concat(namebuf, doom_itoa(i, 10)); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + } + faces[facenum++] = W_CacheLumpName("STFGOD0", PU_STATIC); + faces[facenum++] = W_CacheLumpName("STFDEAD0", PU_STATIC); +} + +void ST_loadData(void) +{ + lu_palette = W_GetNumForName("PLAYPAL"); + ST_loadGraphics(); +} + +void ST_unloadGraphics(void) +{ + int i; + + // unload the numbers, tall and short + for (i = 0; i < 10; i++) + { + Z_ChangeTag(tallnum[i], PU_CACHE); + Z_ChangeTag(shortnum[i], PU_CACHE); + } + // unload tall percent + Z_ChangeTag(tallpercent, PU_CACHE); + + // unload arms background + Z_ChangeTag(armsbg, PU_CACHE); + + // unload gray #'s + for (i = 0; i < 6; i++) + Z_ChangeTag(arms[i][0], PU_CACHE); + + // unload the key cards + for (i = 0; i < NUMCARDS; i++) + Z_ChangeTag(keys[i], PU_CACHE); + + Z_ChangeTag(sbar, PU_CACHE); + Z_ChangeTag(faceback, PU_CACHE); + + for (i = 0; i < ST_NUMFACES; i++) + Z_ChangeTag(faces[i], PU_CACHE); + + // Note: nobody ain't seen no unloading + // of stminus yet. Dude. +} + +void ST_unloadData(void) +{ + ST_unloadGraphics(); +} + +void ST_initData(void) +{ + int i; + + st_firsttime = true; + plyr = &players[consoleplayer]; + + st_clock = 0; + st_chatstate = StartChatState; + st_gamestate = FirstPersonState; + + st_statusbaron = true; + st_oldchat = st_chat = false; + st_cursoron = false; + + st_faceindex = 0; + st_palette = -1; + + st_oldhealth = -1; + + for (i = 0; i < NUMWEAPONS; i++) + oldweaponsowned[i] = plyr->weaponowned[i]; + + for (i = 0; i < 3; i++) + keyboxes[i] = -1; + + STlib_init(); +} + +void ST_createWidgets(void) +{ + int i; + + // ready weapon ammo + STlib_initNum(&w_ready, + ST_AMMOX, + ST_AMMOY, + tallnum, + &plyr->ammo[weaponinfo[plyr->readyweapon].ammo], + &st_statusbaron, + ST_AMMOWIDTH); + + // the last weapon type + w_ready.data = plyr->readyweapon; + + // health percentage + STlib_initPercent(&w_health, + ST_HEALTHX, + ST_HEALTHY, + tallnum, + &plyr->health, + &st_statusbaron, + tallpercent); + + // arms background + STlib_initBinIcon(&w_armsbg, + ST_ARMSBGX, + ST_ARMSBGY, + armsbg, + &st_notdeathmatch, + &st_statusbaron); + + // weapons owned + for (i = 0; i < 6; i++) + { + STlib_initMultIcon(&w_arms[i], + ST_ARMSX + (i % 3) * ST_ARMSXSPACE, + ST_ARMSY + (i / 3) * ST_ARMSYSPACE, + arms[i], (int*)&plyr->weaponowned[i + 1], + &st_armson); + } + + // frags sum + STlib_initNum(&w_frags, + ST_FRAGSX, + ST_FRAGSY, + tallnum, + &st_fragscount, + &st_fragson, + ST_FRAGSWIDTH); + + // faces + STlib_initMultIcon(&w_faces, + ST_FACESX, + ST_FACESY, + faces, + &st_faceindex, + &st_statusbaron); + + // armor percentage - should be colored later + STlib_initPercent(&w_armor, + ST_ARMORX, + ST_ARMORY, + tallnum, + &plyr->armorpoints, + &st_statusbaron, tallpercent); + + // keyboxes 0-2 + STlib_initMultIcon(&w_keyboxes[0], + ST_KEY0X, + ST_KEY0Y, + keys, + &keyboxes[0], + &st_statusbaron); + + STlib_initMultIcon(&w_keyboxes[1], + ST_KEY1X, + ST_KEY1Y, + keys, + &keyboxes[1], + &st_statusbaron); + + STlib_initMultIcon(&w_keyboxes[2], + ST_KEY2X, + ST_KEY2Y, + keys, + &keyboxes[2], + &st_statusbaron); + + // ammo count (all four kinds) + STlib_initNum(&w_ammo[0], + ST_AMMO0X, + ST_AMMO0Y, + shortnum, + &plyr->ammo[0], + &st_statusbaron, + ST_AMMO0WIDTH); + + STlib_initNum(&w_ammo[1], + ST_AMMO1X, + ST_AMMO1Y, + shortnum, + &plyr->ammo[1], + &st_statusbaron, + ST_AMMO1WIDTH); + + STlib_initNum(&w_ammo[2], + ST_AMMO2X, + ST_AMMO2Y, + shortnum, + &plyr->ammo[2], + &st_statusbaron, + ST_AMMO2WIDTH); + + STlib_initNum(&w_ammo[3], + ST_AMMO3X, + ST_AMMO3Y, + shortnum, + &plyr->ammo[3], + &st_statusbaron, + ST_AMMO3WIDTH); + + // max ammo count (all four kinds) + STlib_initNum(&w_maxammo[0], + ST_MAXAMMO0X, + ST_MAXAMMO0Y, + shortnum, + &plyr->maxammo[0], + &st_statusbaron, + ST_MAXAMMO0WIDTH); + + STlib_initNum(&w_maxammo[1], + ST_MAXAMMO1X, + ST_MAXAMMO1Y, + shortnum, + &plyr->maxammo[1], + &st_statusbaron, + ST_MAXAMMO1WIDTH); + + STlib_initNum(&w_maxammo[2], + ST_MAXAMMO2X, + ST_MAXAMMO2Y, + shortnum, + &plyr->maxammo[2], + &st_statusbaron, + ST_MAXAMMO2WIDTH); + + STlib_initNum(&w_maxammo[3], + ST_MAXAMMO3X, + ST_MAXAMMO3Y, + shortnum, + &plyr->maxammo[3], + &st_statusbaron, + ST_MAXAMMO3WIDTH); +} + +void ST_Start(void) +{ + if (!st_stopped) + ST_Stop(); + + ST_initData(); + ST_createWidgets(); + st_stopped = false; +} + +void ST_Stop(void) +{ + if (st_stopped) + return; + + I_SetPalette(W_CacheLumpNum(lu_palette, PU_CACHE)); + + st_stopped = true; +} + +void ST_Init(void) +{ + veryfirsttime = 0; + ST_loadData(); + screens[4] = (byte*)Z_Malloc(ST_WIDTH * ST_HEIGHT, PU_STATIC, 0); +} +int +SlopeDiv +( unsigned num, + unsigned den) +{ + unsigned ans; + + if (den < 512) + return SLOPERANGE; + + ans = (num<<3)/(den>>8); + + return ans <= SLOPERANGE ? ans : SLOPERANGE; +} + + + + +int finetangent[4096] = +{ + -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 +}; + + +int finesine[10240] = +{ + 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 +}; + + + +angle_t tantoangle[2049] = +{ + 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 +}; + +byte* screens[5]; + +int dirtybox[4]; + +// Now where did these came from? +byte gammatable[5][256] = +{ + {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, + 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, + 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, + 65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96, + 97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112, + 113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}, + + {2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,24,25,26,27,29,30,31, + 32,33,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,54,55, + 56,57,58,59,60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76,77, + 78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98, + 99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114, + 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,129, + 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, + 146,147,148,148,149,150,151,152,153,154,155,156,157,158,159,160, + 161,162,163,163,164,165,166,167,168,169,170,171,172,173,174,175, + 175,176,177,178,179,180,181,182,183,184,185,186,186,187,188,189, + 190,191,192,193,194,195,196,196,197,198,199,200,201,202,203,204, + 205,205,206,207,208,209,210,211,212,213,214,214,215,216,217,218, + 219,220,221,222,222,223,224,225,226,227,228,229,230,230,231,232, + 233,234,235,236,237,237,238,239,240,241,242,243,244,245,245,246, + 247,248,249,250,251,252,252,253,254,255}, + + {4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,33,35,36,38,39,40,42, + 43,45,46,47,48,50,51,52,54,55,56,57,59,60,61,62,63,65,66,67,68,69, + 70,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,91,92,93, + 94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112, + 113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128, + 129,130,131,132,133,133,134,135,136,137,138,139,140,141,142,143,144, + 144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159, + 160,160,161,162,163,164,165,166,166,167,168,169,170,171,172,172,173, + 174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,187,188, + 188,189,190,191,192,193,193,194,195,196,197,197,198,199,200,201,201, + 202,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215, + 216,217,217,218,219,220,221,221,222,223,224,224,225,226,227,228,228, + 229,230,231,231,232,233,234,235,235,236,237,238,238,239,240,241,241, + 242,243,244,244,245,246,247,247,248,249,250,251,251,252,253,254,254, + 255}, + + {8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,47,49,50,52,53,55, + 57,58,60,61,63,64,65,67,68,70,71,72,74,75,76,77,79,80,81,82,84,85, + 86,87,88,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107, + 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124, + 125,126,127,128,129,130,131,132,133,134,135,135,136,137,138,139,140, + 141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155, + 155,156,157,158,159,160,160,161,162,163,164,165,165,166,167,168,169, + 169,170,171,172,173,173,174,175,176,176,177,178,179,180,180,181,182, + 183,183,184,185,186,186,187,188,189,189,190,191,192,192,193,194,195, + 195,196,197,197,198,199,200,200,201,202,202,203,204,205,205,206,207, + 207,208,209,210,210,211,212,212,213,214,214,215,216,216,217,218,219, + 219,220,221,221,222,223,223,224,225,225,226,227,227,228,229,229,230, + 231,231,232,233,233,234,235,235,236,237,237,238,238,239,240,240,241, + 242,242,243,244,244,245,246,246,247,247,248,249,249,250,251,251,252, + 253,253,254,254,255}, + + {16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,66,68,69,71,73,75,76, + 78,80,81,83,84,86,87,89,90,92,93,94,96,97,98,100,101,102,103,105,106, + 107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124, + 125,126,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141, + 142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155, + 156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169, + 169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,181, + 182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193, + 193,194,195,195,196,196,197,198,198,199,200,200,201,202,202,203,203, + 204,205,205,206,207,207,208,208,209,210,210,211,211,212,213,213,214, + 214,215,216,216,217,217,218,219,219,220,220,221,221,222,223,223,224, + 224,225,225,226,227,227,228,228,229,229,230,230,231,232,232,233,233, + 234,234,235,235,236,236,237,237,238,239,239,240,240,241,241,242,242, + 243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251, + 251,252,252,253,254,254,255,255} +}; + +int usegamma; + + +// +// V_MarkRect +// +void V_MarkRect(int x, int y, int width, int height) +{ + M_AddToBox(dirtybox, x, y); + M_AddToBox(dirtybox, x + width - 1, y + height - 1); +} + + +// +// V_CopyRect +// +void V_CopyRect(int srcx, + int srcy, + int srcscrn, + int width, + int height, + int destx, + int desty, + int destscrn) +{ + byte* src; + byte* dest; + +#ifdef RANGECHECK + if (srcx<0 + || srcx + width >SCREENWIDTH + || srcy<0 + || srcy + height>SCREENHEIGHT + || destx<0 || destx + width >SCREENWIDTH + || desty<0 + || desty + height>SCREENHEIGHT + || (unsigned)srcscrn > 4 + || (unsigned)destscrn > 4) + { + I_Error("Error: Bad V_CopyRect"); + } +#endif + V_MarkRect(destx, desty, width, height); + + src = screens[srcscrn] + SCREENWIDTH * srcy + srcx; + dest = screens[destscrn] + SCREENWIDTH * desty + destx; + + for (; height > 0; height--) + { + doom_memcpy(dest, src, width); + src += SCREENWIDTH; + dest += SCREENWIDTH; + } +} + + +// +// V_DrawPatch +// Masks a column based masked pic to the screen. +// +void V_DrawPatch(int x, int y, int scrn, patch_t* patch) +{ + int count; + int col; + column_t* column; + byte* desttop; + byte* dest; + byte* source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); +#ifdef RANGECHECK + if (x<0 + || x + SHORT(patch->width) >SCREENWIDTH + || y<0 + || y + SHORT(patch->height)>SCREENHEIGHT + || (unsigned)scrn > 4) + { + //doom_print("Patch at %d,%d exceeds LFB\n", x, y); + doom_print("Patch at "); + doom_print(doom_itoa(x, 10)); + doom_print(","); + doom_print(doom_itoa(y, 10)); + doom_print(" exceeds LFB\n"); + // No I_Error abort - what is up with TNT.WAD? + doom_print("V_DrawPatch: bad patch (ignored)\n"); + return; + } +#endif + + if (!scrn) + V_MarkRect(x, y, SHORT(patch->width), SHORT(patch->height)); + + col = 0; + desttop = screens[scrn] + y * SCREENWIDTH + x; + + w = SHORT(patch->width); + + for (; col < w; x++, col++, desttop++) + { + column = (column_t*)((byte*)patch + LONG(patch->columnofs[col])); + + // step through the posts in a column + while (column->topdelta != 0xff) + { + source = (byte*)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + column = (column_t*)((byte*)column + column->length + + 4); + } + } +} + + +// +// V_DrawPatchFlipped +// Masks a column based masked pic to the screen. +// Flips horizontally, e.g. to mirror face. +// +void V_DrawPatchFlipped(int x, int y, int scrn, patch_t* patch) +{ + int count; + int col; + column_t* column; + byte* desttop; + byte* dest; + byte* source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); +#ifdef RANGECHECK + if (x<0 + || x + SHORT(patch->width) >SCREENWIDTH + || y<0 + || y + SHORT(patch->height)>SCREENHEIGHT + || (unsigned)scrn > 4) + { + //doom_print("Patch origin %d,%d exceeds LFB\n", x, y); + doom_print("Patch origin "); + doom_print(doom_itoa(x, 10)); + doom_print(","); + doom_print(doom_itoa(y, 10)); + doom_print(" exceeds LFB\n"); + I_Error("Error: Bad V_DrawPatch in V_DrawPatchFlipped"); + } +#endif + + if (!scrn) + V_MarkRect(x, y, SHORT(patch->width), SHORT(patch->height)); + + col = 0; + desttop = screens[scrn] + y * SCREENWIDTH + x; + + w = SHORT(patch->width); + + for (; col < w; x++, col++, desttop++) + { + column = (column_t*)((byte*)patch + LONG(patch->columnofs[w - 1 - col])); + + // step through the posts in a column + while (column->topdelta != 0xff) + { + source = (byte*)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + column = (column_t*)((byte*)column + column->length + + 4); + } + } +} + + +void V_DrawPatchRectDirect(int x, int y, int scrn, patch_t* patch, int src_x, int src_w) +{ + int count; + int col; + column_t* column; + byte* desttop; + byte* dest; + byte* source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); +#ifdef RANGECHECK + if (x<0 + || x + SHORT(src_w) >SCREENWIDTH + || y<0 + || y + SHORT(patch->height)>SCREENHEIGHT + || (unsigned)scrn > 4) + { + //doom_print("Patch at %d,%d exceeds LFB\n", x, y); + doom_print("Patch at "); + doom_print(doom_itoa(x, 10)); + doom_print(","); + doom_print(doom_itoa(y, 10)); + doom_print(" exceeds LFB\n"); + // No I_Error abort - what is up with TNT.WAD? + doom_print("V_DrawPatch: bad patch (ignored)\n"); + return; + } +#endif + + if (!scrn) + V_MarkRect(x, y, SHORT(src_w), SHORT(patch->height)); + + col = 0; + desttop = screens[scrn] + y * SCREENWIDTH + x; + + w = SHORT(src_w); + + for (; col < w; x++, col++, desttop++) + { + column = (column_t*)((byte*)patch + LONG(patch->columnofs[col + src_x])); + + // step through the posts in a column + while (column->topdelta != 0xff) + { + source = (byte*)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + column = (column_t*)((byte*)column + column->length + + 4); + } + } +} + + +// +// V_DrawPatchDirect +// Draws directly to the screen on the pc. +// +void V_DrawPatchDirect(int x, int y, int scrn, patch_t* patch) +{ + V_DrawPatch(x, y, scrn, patch); +} + + +// +// V_DrawBlock +// Draw a linear block of pixels into the view buffer. +// +void V_DrawBlock(int x, int y, int scrn, int width, int height, byte* src) +{ + byte* dest; + +#ifdef RANGECHECK + if (x<0 + || x + width >SCREENWIDTH + || y<0 + || y + height>SCREENHEIGHT + || (unsigned)scrn > 4) + { + I_Error("Error: Bad V_DrawBlock"); + } +#endif + + V_MarkRect(x, y, width, height); + + dest = screens[scrn] + y * SCREENWIDTH + x; + + while (height--) + { + doom_memcpy(dest, src, width); + src += width; + dest += SCREENWIDTH; + } +} + + +// +// V_GetBlock +// Gets a linear block of pixels from the view buffer. +// +void V_GetBlock(int x, int y, int scrn, int width, int height, byte* dest) +{ + byte* src; + +#ifdef RANGECHECK + if (x<0 + || x + width >SCREENWIDTH + || y<0 + || y + height>SCREENHEIGHT + || (unsigned)scrn > 4) + { + I_Error("Error: Bad V_DrawBlock"); + } +#endif + + src = screens[scrn] + y * SCREENWIDTH + x; + + while (height--) + { + doom_memcpy(dest, src, width); + src += SCREENWIDTH; + dest += width; + } +} + + +// +// V_Init +// +void V_Init(void) +{ + int i; + byte* base; + + // stick these in low dos memory on PCs + + base = I_AllocLow(SCREENWIDTH * SCREENHEIGHT * 4); + + for (i = 0; i < 4; i++) + screens[i] = base + i * SCREENWIDTH * SCREENHEIGHT; +} +lumpinfo_t* lumpinfo; +int numlumps; +void** lumpcache; +int reloadlump; +char* reloadname; +int info[2500][10]; +int profilecount; + + +void doom_strupr(char* s) +{ + while (*s) + { + *s = doom_toupper(*s); s++; + } +} + + +void ExtractFileBase(char* path, char* dest) +{ + char* src; + int length; + + src = path + doom_strlen(path) - 1; + + // back up until a \ or the start + while (src != path + && *(src - 1) != '\\' + && *(src - 1) != '/') + { + src--; + } + + // copy up to eight characters + doom_memset(dest, 0, 8); + length = 0; + + while (*src && *src != '.') + { + if (++length == 9) + { + //I_Error("Error: Filename base of %s >8 chars", path); + doom_strcpy(error_buf, "Error: Filename base of "); + doom_concat(error_buf, path); + doom_concat(error_buf, " >8 chars"); + I_Error(error_buf); + } + + *dest++ = doom_toupper((int)*src++); + } +} + + +// +// LUMP BASED ROUTINES. +// + +// +// W_AddFile +// All files are optional, but at least one file must be +// found (PWAD, if all required lumps are present). +// Files with a .wad extension are wadlink files +// with multiple lumps. +// Other files are single lumps with the base filename +// for the lump name. +// +// If filename starts with a tilde, the file is handled +// specially to allow map reloads. +// But: the reload feature is a fragile hack... +void W_AddFile(char* filename) +{ + wadinfo_t header; + lumpinfo_t* lump_p; + unsigned i; + void* handle; + int length; + int startlump; + filelump_t* fileinfo; + filelump_t singleinfo; + void* storehandle; + void* allocated = 0; + + // open the file and add to directory + + // handle reload indicator. + if (filename[0] == '~') + { + filename++; + reloadname = filename; + reloadlump = numlumps; + } + + if ((handle = doom_open(filename, "rb")) == 0) + { + //doom_print(" couldn't open %s\n", filename); + doom_print(" couldn't open "); + doom_print(filename); + doom_print("\n"); + return; + } + + //doom_print(" adding %s\n", filename); + doom_print(" adding "); + doom_print(filename); + doom_print("\n"); + startlump = numlumps; + + if (doom_strcasecmp(filename + doom_strlen(filename) - 3, "wad")) + { + // single lump file + fileinfo = &singleinfo; + singleinfo.filepos = 0; + //singleinfo.size = LONG(filelength(handle)); + doom_seek(handle, 0, DOOM_SEEK_END); + singleinfo.size = doom_tell(handle); + doom_seek(handle, 0, DOOM_SEEK_SET); + ExtractFileBase(filename, singleinfo.name); + numlumps++; + } + else + { + // WAD file + doom_read(handle, &header, sizeof(header)); + if (doom_strncmp(header.identification, "IWAD", 4)) + { + // Homebrew levels? + if (doom_strncmp(header.identification, "PWAD", 4)) + { + //I_Error("Error: Wad file %s doesn't have IWAD " + // "or PWAD id\n", filename); + doom_strcpy(error_buf, "Error: Wad file "); + doom_concat(error_buf, filename); + doom_concat(error_buf, " doesn't have IWAD or PWAD id\n"); + I_Error(error_buf); + } + + // ???modifiedgame = true; + } + header.numlumps = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + length = header.numlumps * sizeof(filelump_t); + fileinfo = allocated = doom_malloc(length); + doom_seek(handle, header.infotableofs, DOOM_SEEK_SET); + doom_read(handle, fileinfo, length); + numlumps += header.numlumps; + } + + + // Fill in lumpinfo + static int previous_realloc_size = 1; + void* new_lumpinfo = doom_malloc(numlumps * sizeof(lumpinfo_t)); + doom_memcpy(new_lumpinfo, lumpinfo, previous_realloc_size); + previous_realloc_size = numlumps * sizeof(lumpinfo_t); + lumpinfo = new_lumpinfo; + + if (!lumpinfo) + I_Error("Error: Couldn't realloc lumpinfo"); + + lump_p = &lumpinfo[startlump]; + + storehandle = reloadname ? 0 : handle; + + for (i = startlump; i < (unsigned)numlumps; i++, lump_p++, fileinfo++) + { + lump_p->handle = storehandle; + lump_p->position = LONG(fileinfo->filepos); + lump_p->size = LONG(fileinfo->size); + doom_strncpy(lump_p->name, fileinfo->name, 8); + } + + if (reloadname) + doom_close(handle); + + if (allocated) doom_free(allocated); +} + + +// +// W_Reload +// Flushes any of the reloadable lumps in memory +// and reloads the directory. +// +void W_Reload(void) +{ + wadinfo_t header; + int lumpcount; + lumpinfo_t* lump_p; + unsigned i; + void* handle; + int length; + filelump_t* fileinfo; + + if (!reloadname) + return; + + if ((handle = doom_open(reloadname, "rb")) == 0) + { + //I_Error("Error: W_Reload: couldn't open %s", reloadname); + doom_strcpy(error_buf, "Error: W_Reload: couldn't open "); + doom_concat(error_buf, reloadname); + I_Error(error_buf); + } + + doom_read(handle, &header, sizeof(header)); + lumpcount = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + length = lumpcount * sizeof(filelump_t); + fileinfo = doom_malloc(length); + doom_seek(handle, header.infotableofs, DOOM_SEEK_SET); + doom_read(handle, fileinfo, length); + + // Fill in lumpinfo + lump_p = &lumpinfo[reloadlump]; + + for (i = reloadlump; + i < (unsigned)(reloadlump + lumpcount); + i++, lump_p++, fileinfo++) + { + if (lumpcache[i]) + Z_Free(lumpcache[i]); + + lump_p->position = LONG(fileinfo->filepos); + lump_p->size = LONG(fileinfo->size); + } + + doom_close(handle); + + doom_free(fileinfo); +} + + +// +// W_InitMultipleFiles +// Pass a null terminated list of files to use. +// All files are optional, but at least one file +// must be found. +// Files with a .wad extension are idlink files +// with multiple lumps. +// Other files are single lumps with the base filename +// for the lump name. +// Lump names can appear multiple times. +// The name searcher looks backwards, so a later file +// does override all earlier ones. +// +void W_InitMultipleFiles(char** filenames) +{ + int size; + + // open all the files, load headers, and count lumps + numlumps = 0; + + // will be realloced as lumps are added + lumpinfo = doom_malloc(1); + + for (; *filenames; filenames++) + W_AddFile(*filenames); + + if (!numlumps) + I_Error("Error: W_InitFiles: no files found"); + + // set up caching + size = numlumps * sizeof(*lumpcache); + lumpcache = doom_malloc(size); + + if (!lumpcache) + I_Error("Error: Couldn't allocate lumpcache"); + + doom_memset(lumpcache, 0, size); +} + + +// +// W_InitFile +// Just initialize from a single file. +// +void W_InitFile(char* filename) +{ + char* names[2]; + + names[0] = filename; + names[1] = 0; + W_InitMultipleFiles(names); +} + + +// +// W_NumLumps +// +int W_NumLumps(void) +{ + return numlumps; +} + + +// +// W_CheckNumForName +// Returns -1 if name not found. +// +int W_CheckNumForName(char* name) +{ + union + { + char s[9]; + int x[2]; + } name8; + + int v1; + int v2; + lumpinfo_t* lump_p; + + // make the name into two integers for easy compares + doom_strncpy(name8.s, name, 8); + + // in case the name was a fill 8 chars + name8.s[8] = 0; + + // case insensitive + doom_strupr(name8.s); + + v1 = name8.x[0]; + v2 = name8.x[1]; + + + // scan backwards so patch lump files take precedence + lump_p = lumpinfo + numlumps; + + while (lump_p-- != lumpinfo) + { + if (*(int*)lump_p->name == v1 + && *(int*)&lump_p->name[4] == v2) + { + return (int)(lump_p - lumpinfo); + } + } + + // TFB. Not found. + return -1; +} + + +// +// W_GetNumForName +// Calls W_CheckNumForName, but bombs out if not found. +// +int W_GetNumForName(char* name) +{ + int i; + + i = W_CheckNumForName(name); + + if (i == -1) + { + if (doom_strcmp(name, "HELP2") == 0) + { + name = "HELP1"; // Ultimate Doom EXE was modified to use this instead + i = W_CheckNumForName(name); + } + if (i == -1) + { + //I_Error("Error: W_GetNumForName, %s not found!", name); + doom_strcpy(error_buf, "Error: W_GetNumForName, "); + doom_concat(error_buf, name); + doom_concat(error_buf, " not found!"); + I_Error(error_buf); + } + } + + return i; +} + + +// +// W_LumpLength +// Returns the buffer size needed to load the given lump. +// +int W_LumpLength(int lump) +{ + if (lump >= numlumps) + { + //I_Error("Error: W_LumpLength: %i >= numlumps", lump); + doom_strcpy(error_buf, "Error: W_LumpLength: "); + doom_concat(error_buf, doom_itoa(lump, 10)); + doom_concat(error_buf, " >= numlumps"); + I_Error(error_buf); + } + + return lumpinfo[lump].size; +} + + +// +// W_ReadLump +// Loads the lump into the given buffer, +// which must be >= W_LumpLength(). +// +void W_ReadLump(int lump, void* dest) +{ + int c; + lumpinfo_t* l; + void* handle; + + if (lump >= numlumps) + { + //I_Error("Error: W_ReadLump: %i >= numlumps", lump); + doom_strcpy(error_buf, "Error: W_ReadLump: "); + doom_concat(error_buf, doom_itoa(lump, 10)); + doom_concat(error_buf, " >= numlump"); + I_Error(error_buf); + } + + l = lumpinfo + lump; + + // ??? I_BeginRead (); + + if (l->handle == 0) + { + // reloadable file, so use open / read / close + if ((handle = doom_open(reloadname, "rb")) == 0) + { + //I_Error("Error: W_ReadLump: couldn't open %s", reloadname); + doom_strcpy(error_buf, "Error: W_ReadLump: couldn't open "); + doom_concat(error_buf, reloadname); + I_Error(error_buf); + } + } + else + handle = l->handle; + + doom_seek(handle, l->position, DOOM_SEEK_SET); + c = doom_read(handle, dest, l->size); + + if (c < l->size) + { + //I_Error("Error: W_ReadLump: only read %i of %i on lump %i", + // c, l->size, lump); + doom_strcpy(error_buf, "Error: W_ReadLump: only read "); + doom_concat(error_buf, doom_itoa(c, 10)); + doom_concat(error_buf, " of "); + doom_concat(error_buf, doom_itoa(l->size, 10)); + doom_concat(error_buf, " on lump "); + doom_concat(error_buf, doom_itoa(lump, 10)); + I_Error(error_buf); + } + + if (l->handle == 0) + doom_close(handle); + + // ??? I_EndRead (); +} + + +// +// W_CacheLumpNum +// +void* W_CacheLumpNum(int lump, int tag) +{ + byte* ptr; + + if ((unsigned)lump >= (unsigned)numlumps) + { + //I_Error("Error: W_CacheLumpNum: %i >= numlumps", lump); + doom_strcpy(error_buf, "Error: W_CacheLumpNum: "); + doom_concat(error_buf, doom_itoa(lump, 10)); + doom_concat(error_buf, " >= numlumps"); + I_Error(error_buf); + } + + if (!lumpcache[lump]) + { + // read the lump in + + //doom_print ("cache miss on lump %i\n",lump); + ptr = Z_Malloc(W_LumpLength(lump), tag, &lumpcache[lump]); + W_ReadLump(lump, lumpcache[lump]); + } + else + { + //doom_print ("cache hit on lump %i\n",lump); + Z_ChangeTag(lumpcache[lump], tag); + } + + return lumpcache[lump]; +} + + +// +// W_CacheLumpName +// +void* W_CacheLumpName(char* name, int tag) +{ + return W_CacheLumpNum(W_GetNumForName(name), tag); +} + + +// +// W_Profile +// +void W_Profile(void) +{ + int i; + memblock_t* block; + void* ptr; + char ch; + void* f; + int j; + char name[9]; + + for (i = 0; i < numlumps; i++) + { + ptr = lumpcache[i]; + if (!ptr) + { + ch = ' '; + continue; + } + else + { + block = (memblock_t*)((byte*)ptr - sizeof(memblock_t)); + if (block->tag < PU_PURGELEVEL) + ch = 'S'; + else + ch = 'P'; + } + info[i][profilecount] = ch; + } + profilecount++; + + f = doom_open("waddump.txt", "w"); + name[8] = 0; + + for (i = 0; i < numlumps; i++) + { + doom_memcpy(name, lumpinfo[i].name, 8); + + for (j = 0; j < 8; j++) + if (!name[j]) + break; + + for (; j < 8; j++) + name[j] = ' '; + + doom_fprint(f, name); + doom_fprint(f, " "); + + for (j = 0; j < profilecount; j++) + { + //fprintf(f, " %c", info[i][j]); + doom_fprint(f, " "); + doom_fprint(f, doom_ctoa(info[i][j])); + } + + doom_fprint(f, "\n"); + } + + doom_close(f); +} +#define NUMEPISODES 4 +#define NUMMAPS 9 + +// GLOBAL LOCATIONS +#define WI_TITLEY 2 +#define WI_SPACINGY 33 + +// SINGPLE-PLAYER STUFF +#define SP_STATSX 50 +#define SP_STATSY 50 + +#define SP_TIMEX 16 +#define SP_TIMEY (SCREENHEIGHT-32) + +// NET GAME STUFF +#define NG_STATSY 50 +#define NG_STATSX (32 + SHORT(star->width)/2 + 32*!dofrags) + +#define NG_SPACINGX 64 + +// DEATHMATCH STUFF +#define DM_MATRIXX 42 +#define DM_MATRIXY 68 + +#define DM_SPACINGX 40 + +#define DM_TOTALSX 269 + +#define DM_KILLERSX 10 +#define DM_KILLERSY 100 +#define DM_VICTIMSX 5 +#define DM_VICTIMSY 50 + +#define FB 0 + +// States for single-player +#define SP_KILLS 0 +#define SP_ITEMS 2 +#define SP_SECRET 4 +#define SP_FRAGS 6 +#define SP_TIME 8 +#define SP_PAR ST_TIME + +#define SP_PAUSE 1 + +// in seconds +#define SHOWNEXTLOCDELAY 4 +//#define SHOWLASTLOCDELAY SHOWNEXTLOCDELAY + + +typedef enum +{ + ANIM_ALWAYS, + ANIM_RANDOM, + ANIM_LEVEL +} animenum_t; + + +typedef struct +{ + int x; + int y; +} point_t; + + +// +// Animation. +// There is another anim_t used in p_spec. +// +typedef struct +{ + animenum_t type; + + // period in tics between animations + int period; + + // number of animation frames + int nanims; + + // location of animation + point_t loc; + + // ALWAYS: n/a, + // RANDOM: period deviation (<256), + // LEVEL: level + int data1; + + // ALWAYS: n/a, + // RANDOM: random base period, + // LEVEL: n/a + int data2; + + // actual graphics for frames of animations + patch_t* p[3]; + + // following must be initialized to zero before use! + + // next value of bcnt (used in conjunction with period) + int nexttic; + + // last drawn animation frame + int lastdrawn; + + // next frame number to animate + int ctr; + + // used by RANDOM and LEVEL when animating + int state; +} anim_t_wi_stuff; + + +static point_t lnodes[NUMEPISODES][NUMMAPS] = +{ + // Episode 0 World Map + { + { 185, 164 }, // location of level 0 (CJ) + { 148, 143 }, // location of level 1 (CJ) + { 69, 122 }, // location of level 2 (CJ) + { 209, 102 }, // location of level 3 (CJ) + { 116, 89 }, // location of level 4 (CJ) + { 166, 55 }, // location of level 5 (CJ) + { 71, 56 }, // location of level 6 (CJ) + { 135, 29 }, // location of level 7 (CJ) + { 71, 24 } // location of level 8 (CJ) + }, + + // Episode 1 World Map should go here + { + { 254, 25 }, // location of level 0 (CJ) + { 97, 50 }, // location of level 1 (CJ) + { 188, 64 }, // location of level 2 (CJ) + { 128, 78 }, // location of level 3 (CJ) + { 214, 92 }, // location of level 4 (CJ) + { 133, 130 }, // location of level 5 (CJ) + { 208, 136 }, // location of level 6 (CJ) + { 148, 140 }, // location of level 7 (CJ) + { 235, 158 } // location of level 8 (CJ) + }, + + // Episode 2 World Map should go here + { + { 156, 168 }, // location of level 0 (CJ) + { 48, 154 }, // location of level 1 (CJ) + { 174, 95 }, // location of level 2 (CJ) + { 265, 75 }, // location of level 3 (CJ) + { 130, 48 }, // location of level 4 (CJ) + { 279, 23 }, // location of level 5 (CJ) + { 198, 48 }, // location of level 6 (CJ) + { 140, 25 }, // location of level 7 (CJ) + { 281, 136 } // location of level 8 (CJ) + } +}; + + +// +// Animation locations for episode 0 (1). +// Using patches saves a lot of space, +// as they replace 320x200 full screen frames. +// +static anim_t_wi_stuff epsd0animinfo[] = +{ + { ANIM_ALWAYS, TICRATE / 3, 3, { 224, 104 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 184, 160 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 112, 136 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 72, 112 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 88, 96 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 64, 48 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 192, 40 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 136, 16 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 80, 16 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 64, 24 } } +}; + +static anim_t_wi_stuff epsd1animinfo[] = +{ + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 1 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 2 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 3 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 4 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 5 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 6 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 7 }, + { ANIM_LEVEL, TICRATE / 3, 3, { 192, 144 }, 8 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 8 } +}; + +static anim_t_wi_stuff epsd2animinfo[] = +{ + { ANIM_ALWAYS, TICRATE / 3, 3, { 104, 168 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 40, 136 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 160, 96 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 104, 80 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 120, 32 } }, + { ANIM_ALWAYS, TICRATE / 4, 3, { 40, 0 } } +}; + +static int NUMANIMS[NUMEPISODES] = +{ + sizeof(epsd0animinfo) / sizeof(anim_t_wi_stuff), + sizeof(epsd1animinfo) / sizeof(anim_t_wi_stuff), + sizeof(epsd2animinfo) / sizeof(anim_t_wi_stuff) +}; + +static anim_t_wi_stuff* anims_wi_stuff[NUMEPISODES] = +{ + epsd0animinfo, + epsd1animinfo, + epsd2animinfo +}; + + +// +// GENERAL DATA +// + +// +// Locally used stuff. +// + +// used to accelerate or skip a stage +static int acceleratestage; + +// wbs->pnum +static int me; + +// specifies current state +static stateenum_t state; + +// contains information passed into intermission +static wbstartstruct_t* wbs; + +static wbplayerstruct_t* plrs; // wbs->plyr[] + +// used for general timing +static int cnt; + +// used for timing of background animation +static int bcnt; + +// signals to refresh everything for one frame +static int firstrefresh; + +static int cnt_kills[MAXPLAYERS]; +static int cnt_items[MAXPLAYERS]; +static int cnt_secret[MAXPLAYERS]; +static int cnt_time; +static int cnt_par; +static int cnt_pause; + +// # of commercial levels +static int NUMCMAPS; + + +// +// GRAPHICS +// + +// background (map of levels). +static patch_t* bg; + +// You Are Here graphic +static patch_t* yah[2]; + +// splat +static patch_t* splat; + +// %, : graphics +static patch_t* percent; +static patch_t* colon; + +// 0-9 graphic +static patch_t* num[10]; + +// minus sign +static patch_t* wiminus; + +// "Finished!" graphics +static patch_t* finished; + +// "Entering" graphic +static patch_t* entering; + +// "secret" +static patch_t* sp_secret; + +// "Kills", "Scrt", "Items", "Frags" +static patch_t* kills; +static patch_t* secret; +static patch_t* items; +static patch_t* frags; + +// Time sucks. +static patch_t* time_patch; +static patch_t* par; +static patch_t* sucks; + +// "killers", "victims" +static patch_t* killers; +static patch_t* victims; + +// "Total", your face, your dead face +static patch_t* total; +static patch_t* star; +static patch_t* bstar; + +// "red P[1..MAXPLAYERS]" +static patch_t* p[MAXPLAYERS]; + +// "gray P[1..MAXPLAYERS]" +static patch_t* bp[MAXPLAYERS]; + +// Name graphics of each level (centered) +static patch_t** lnames; + +static doom_boolean snl_pointeron = false; +static int dm_state; +static int dm_frags[MAXPLAYERS][MAXPLAYERS]; +static int dm_totals[MAXPLAYERS]; +static int cnt_frags[MAXPLAYERS]; +static int dofrags; +static int ng_state; +static int sp_state; + + +// +// CODE +// + +void WI_slamBackground(void) +{ + doom_memcpy(screens[0], screens[1], SCREENWIDTH * SCREENHEIGHT); + V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT); +} + + +// The ticker is used to detect keys +// because of timing issues in netgames. +doom_boolean WI_Responder(event_t* ev) +{ + return false; +} + + +// Draws " Finished!" +void WI_drawLF(void) +{ + int y = WI_TITLEY; + + // draw + V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->last]->width)) / 2, + y, FB, lnames[wbs->last]); + + // draw "Finished!" + y += (5 * SHORT(lnames[wbs->last]->height)) / 4; + + V_DrawPatch((SCREENWIDTH - SHORT(finished->width)) / 2, + y, FB, finished); +} + + +// Draws "Entering " +void WI_drawEL(void) +{ + int y = WI_TITLEY; + + // draw "Entering" + V_DrawPatch((SCREENWIDTH - SHORT(entering->width)) / 2, + y, FB, entering); + + // draw level + y += (5 * SHORT(lnames[wbs->next]->height)) / 4; + + V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->next]->width)) / 2, + y, FB, lnames[wbs->next]); +} + + +void WI_drawOnLnode(int n, patch_t* c[]) +{ + int i; + int left; + int top; + int right; + int bottom; + doom_boolean fits = false; + + i = 0; + do + { + left = lnodes[wbs->epsd][n].x - SHORT(c[i]->leftoffset); + top = lnodes[wbs->epsd][n].y - SHORT(c[i]->topoffset); + right = left + SHORT(c[i]->width); + bottom = top + SHORT(c[i]->height); + + if (left >= 0 + && right < SCREENWIDTH + && top >= 0 + && bottom < SCREENHEIGHT) + { + fits = true; + } + else + { + i++; + } + } while (!fits && i != 2); + + if (fits && i < 2) + { + V_DrawPatch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y, + FB, c[i]); + } + else + { + // DEBUG + //doom_print("Could not place patch on level %d", n + 1); + doom_print("Could not place patch on level "); + doom_print(doom_itoa(n + 1, 10)); + } +} + + +void WI_initAnimatedBack(void) +{ + int i; + anim_t_wi_stuff* a; + + if (gamemode == commercial) + return; + + if (wbs->epsd > 2) + return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims_wi_stuff[wbs->epsd][i]; + + // init variables + a->ctr = -1; + + // specify the next time to draw it + if (a->type == ANIM_ALWAYS) + a->nexttic = bcnt + 1 + (M_Random() % a->period); + else if (a->type == ANIM_RANDOM) + a->nexttic = bcnt + 1 + a->data2 + (M_Random() % a->data1); + else if (a->type == ANIM_LEVEL) + a->nexttic = bcnt + 1; + } +} + + +void WI_updateAnimatedBack(void) +{ + int i; + anim_t_wi_stuff* a; + + if (gamemode == commercial) + return; + + if (wbs->epsd > 2) + return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims_wi_stuff[wbs->epsd][i]; + + if (bcnt == a->nexttic) + { + switch (a->type) + { + case ANIM_ALWAYS: + if (++a->ctr >= a->nanims) a->ctr = 0; + a->nexttic = bcnt + a->period; + break; + + case ANIM_RANDOM: + a->ctr++; + if (a->ctr == a->nanims) + { + a->ctr = -1; + a->nexttic = bcnt + a->data2 + (M_Random() % a->data1); + } + else a->nexttic = bcnt + a->period; + break; + + case ANIM_LEVEL: + // gawd-awful hack for level anims + if (!(state == StatCount && i == 7) + && wbs->next == a->data1) + { + a->ctr++; + if (a->ctr == a->nanims) a->ctr--; + a->nexttic = bcnt + a->period; + } + break; + } + } + } +} + + +void WI_drawAnimatedBack(void) +{ + int i; + anim_t_wi_stuff* a; + + if (commercial) + return; + + if (wbs->epsd > 2) + return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims_wi_stuff[wbs->epsd][i]; + + if (a->ctr >= 0) + V_DrawPatch(a->loc.x, a->loc.y, FB, a->p[a->ctr]); + } +} + + +// +// Draws a number. +// If digits > 0, then use that many digits minimum, +// otherwise only use as many as necessary. +// Returns new x position. +// +int WI_drawNum(int x, int y, int n, int digits) +{ + int fontwidth = SHORT(num[0]->width); + int neg; + int temp; + + if (digits < 0) + { + if (!n) + { + // make variable-length zeros 1 digit long + digits = 1; + } + else + { + // figure out # of digits in # + digits = 0; + temp = n; + + while (temp) + { + temp /= 10; + digits++; + } + } + } + + neg = n < 0; + if (neg) + n = -n; + + // if non-number, do not draw it + if (n == 1994) + return 0; + + // draw the new number + while (digits--) + { + x -= fontwidth; + V_DrawPatch(x, y, FB, num[n % 10]); + n /= 10; + } + + // draw a minus sign if necessary + if (neg) + V_DrawPatch(x -= 8, y, FB, wiminus); + + return x; +} + + +void WI_drawPercent(int x, int y, int p) +{ + if (p < 0) + return; + + V_DrawPatch(x, y, FB, percent); + WI_drawNum(x, y, p, -1); +} + + +// +// Display level completion time and par, +// or "sucks" message if overflow. +// +void WI_drawTime(int x, int y, int t) +{ + int div; + int n; + + if (t < 0) + return; + + if (t <= 61 * 59) + { + div = 1; + + do + { + n = (t / div) % 60; + x = WI_drawNum(x, y, n, 2) - SHORT(colon->width); + div *= 60; + + // draw + if (div == 60 || t / div) + V_DrawPatch(x, y, FB, colon); + + } while (t / div); + } + else + { + // "sucks" + V_DrawPatch(x - SHORT(sucks->width), y, FB, sucks); + } +} + + +void WI_End(void) +{ + void WI_unloadData(void); + WI_unloadData(); +} + + +void WI_initNoState(void) +{ + state = NoState; + acceleratestage = 0; + cnt = 10; +} + + +void WI_updateNoState(void) +{ + WI_updateAnimatedBack(); + + if (!--cnt) + { + WI_End(); + G_WorldDone(); + } +} + + +void WI_initShowNextLoc(void) +{ + state = ShowNextLoc; + acceleratestage = 0; + cnt = SHOWNEXTLOCDELAY * TICRATE; + + WI_initAnimatedBack(); +} + + +void WI_updateShowNextLoc(void) +{ + WI_updateAnimatedBack(); + + if (!--cnt || acceleratestage) + WI_initNoState(); + else + snl_pointeron = (cnt & 31) < 20; +} + + +void WI_drawShowNextLoc(void) +{ + int i; + int last; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + if (gamemode != commercial) + { + if (wbs->epsd > 2) + { + WI_drawEL(); + return; + } + + last = (wbs->last == 8) ? wbs->next - 1 : wbs->last; + + // draw a splat on taken cities. + for (i = 0; i <= last; i++) + WI_drawOnLnode(i, &splat); + + // splat the secret level? + if (wbs->didsecret) + WI_drawOnLnode(8, &splat); + + // draw flashing ptr + if (snl_pointeron) + WI_drawOnLnode(wbs->next, yah); + } + + // draws which level you are entering.. + if ((gamemode != commercial) + || wbs->next != 30) + WI_drawEL(); +} + + +void WI_drawNoState(void) +{ + snl_pointeron = true; + WI_drawShowNextLoc(); +} + + +int WI_fragSum(int playernum) +{ + int i; + int frags = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] + && i != playernum) + { + frags += plrs[playernum].frags[i]; + } + } + + // JDC hack - negative frags. + frags -= plrs[playernum].frags[playernum]; + // UNUSED if (frags < 0) + // frags = 0; + + return frags; +} + + +void WI_initDeathmatchStats(void) +{ + int i; + int j; + + state = StatCount; + acceleratestage = 0; + dm_state = 1; + + cnt_pause = TICRATE; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + if (playeringame[j]) + dm_frags[i][j] = 0; + + dm_totals[i] = 0; + } + } + + WI_initAnimatedBack(); +} + + +void WI_updateDeathmatchStats(void) +{ + int i; + int j; + + doom_boolean stillticking; + + WI_updateAnimatedBack(); + + if (acceleratestage && dm_state != 4) + { + acceleratestage = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + if (playeringame[j]) + dm_frags[i][j] = plrs[i].frags[j]; + + dm_totals[i] = WI_fragSum(i); + } + } + + + S_StartSound(0, sfx_barexp); + dm_state = 4; + } + + + if (dm_state == 2) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + { + if (playeringame[j] + && dm_frags[i][j] != plrs[i].frags[j]) + { + if (plrs[i].frags[j] < 0) + dm_frags[i][j]--; + else + dm_frags[i][j]++; + + if (dm_frags[i][j] > 99) + dm_frags[i][j] = 99; + + if (dm_frags[i][j] < -99) + dm_frags[i][j] = -99; + + stillticking = true; + } + } + dm_totals[i] = WI_fragSum(i); + + if (dm_totals[i] > 99) + dm_totals[i] = 99; + + if (dm_totals[i] < -99) + dm_totals[i] = -99; + } + + } + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + dm_state++; + } + + } + else if (dm_state == 4) + { + if (acceleratestage) + { + S_StartSound(0, sfx_slop); + + if (gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (dm_state & 1) + { + if (!--cnt_pause) + { + dm_state++; + cnt_pause = TICRATE; + } + } +} + + +void WI_drawDeathmatchStats(void) +{ + int i; + int j; + int x; + int y; + int w; + + int lh; // line height + + lh = WI_SPACINGY; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + WI_drawLF(); + + // draw stat titles (top line) + V_DrawPatch(DM_TOTALSX - SHORT(total->width) / 2, + DM_MATRIXY - WI_SPACINGY + 10, + FB, + total); + + V_DrawPatch(DM_KILLERSX, DM_KILLERSY, FB, killers); + V_DrawPatch(DM_VICTIMSX, DM_VICTIMSY, FB, victims); + + // draw P? + x = DM_MATRIXX + DM_SPACINGX; + y = DM_MATRIXY; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + V_DrawPatch(x - SHORT(p[i]->width) / 2, + DM_MATRIXY - WI_SPACINGY, + FB, + p[i]); + + V_DrawPatch(DM_MATRIXX - SHORT(p[i]->width) / 2, + y, + FB, + p[i]); + + if (i == me) + { + V_DrawPatch(x - SHORT(p[i]->width) / 2, + DM_MATRIXY - WI_SPACINGY, + FB, + bstar); + + V_DrawPatch(DM_MATRIXX - SHORT(p[i]->width) / 2, + y, + FB, + star); + } + } + 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 += DM_SPACINGX; + y += WI_SPACINGY; + } + + // draw stats + y = DM_MATRIXY + 10; + w = SHORT(num[0]->width); + + for (i = 0; i < MAXPLAYERS; i++) + { + x = DM_MATRIXX + DM_SPACINGX; + + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + { + if (playeringame[j]) + WI_drawNum(x + w, y, dm_frags[i][j], 2); + + x += DM_SPACINGX; + } + WI_drawNum(DM_TOTALSX + w, y, dm_totals[i], 2); + } + y += WI_SPACINGY; + } +} + + +void WI_initNetgameStats(void) +{ + int i; + + state = StatCount; + acceleratestage = 0; + ng_state = 1; + + cnt_pause = TICRATE; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0; + + dofrags += WI_fragSum(i); + } + + dofrags = !!dofrags; + + WI_initAnimatedBack(); +} + + +void WI_updateNetgameStats(void) +{ + int i; + int fsum; + + doom_boolean stillticking; + + WI_updateAnimatedBack(); + + if (acceleratestage && ng_state != 10) + { + acceleratestage = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; + + if (dofrags) + cnt_frags[i] = WI_fragSum(i); + } + S_StartSound(0, sfx_barexp); + ng_state = 10; + } + + if (ng_state == 2) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_kills[i] += 2; + + if (cnt_kills[i] >= (plrs[i].skills * 100) / wbs->maxkills) + cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state++; + } + } + else if (ng_state == 4) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_items[i] += 2; + if (cnt_items[i] >= (plrs[i].sitems * 100) / wbs->maxitems) + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + else + stillticking = true; + } + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state++; + } + } + else if (ng_state == 6) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_secret[i] += 2; + + if (cnt_secret[i] >= (plrs[i].ssecret * 100) / wbs->maxsecret) + cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state += 1 + 2 * !dofrags; + } + } + else if (ng_state == 8) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_frags[i] += 1; + + if (cnt_frags[i] >= (fsum = WI_fragSum(i))) + cnt_frags[i] = fsum; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_pldeth); + ng_state++; + } + } + else if (ng_state == 10) + { + if (acceleratestage) + { + S_StartSound(0, sfx_sgcock); + if (gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = TICRATE; + } + } +} + + +void WI_drawNetgameStats(void) +{ + int i; + int x; + int y; + int pwidth = SHORT(percent->width); + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + WI_drawLF(); + + // draw stat titles (top line) + V_DrawPatch(NG_STATSX + NG_SPACINGX - SHORT(kills->width), + NG_STATSY, FB, kills); + + V_DrawPatch(NG_STATSX + 2 * NG_SPACINGX - SHORT(items->width), + NG_STATSY, FB, items); + + V_DrawPatch(NG_STATSX + 3 * NG_SPACINGX - SHORT(secret->width), + NG_STATSY, FB, secret); + + if (dofrags) + V_DrawPatch(NG_STATSX + 4 * NG_SPACINGX - SHORT(frags->width), + NG_STATSY, FB, frags); + + // draw stats + y = NG_STATSY + SHORT(kills->height); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + x = NG_STATSX; + V_DrawPatch(x - SHORT(p[i]->width), y, FB, p[i]); + + if (i == me) + V_DrawPatch(x - SHORT(p[i]->width), y, FB, star); + + x += NG_SPACINGX; + WI_drawPercent(x - pwidth, y + 10, cnt_kills[i]); x += NG_SPACINGX; + WI_drawPercent(x - pwidth, y + 10, cnt_items[i]); x += NG_SPACINGX; + WI_drawPercent(x - pwidth, y + 10, cnt_secret[i]); x += NG_SPACINGX; + + if (dofrags) + WI_drawNum(x, y + 10, cnt_frags[i], -1); + + y += WI_SPACINGY; + } +} + + +void WI_initStats(void) +{ + state = StatCount; + acceleratestage = 0; + sp_state = 1; + cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; + cnt_time = cnt_par = -1; + cnt_pause = TICRATE; + + WI_initAnimatedBack(); +} + + +void WI_updateStats(void) +{ + WI_updateAnimatedBack(); + + if (acceleratestage && sp_state != 10) + { + acceleratestage = 0; + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; + cnt_time = plrs[me].stime / TICRATE; + cnt_par = wbs->partime / TICRATE; + S_StartSound(0, sfx_barexp); + sp_state = 10; + } + + if (sp_state == 2) + { + cnt_kills[0] += 2; + + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) + { + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 4) + { + cnt_items[0] += 2; + + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems) + { + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 6) + { + cnt_secret[0] += 2; + + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs->maxsecret) + { + cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + + else if (sp_state == 8) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + cnt_time += 3; + + if (cnt_time >= plrs[me].stime / TICRATE) + cnt_time = plrs[me].stime / TICRATE; + + cnt_par += 3; + + if (cnt_par >= wbs->partime / TICRATE) + { + cnt_par = wbs->partime / TICRATE; + + if (cnt_time >= plrs[me].stime / TICRATE) + { + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + } + else if (sp_state == 10) + { + if (acceleratestage) + { + S_StartSound(0, sfx_sgcock); + + if (gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (sp_state & 1) + { + if (!--cnt_pause) + { + sp_state++; + cnt_pause = TICRATE; + } + } +} + + +void WI_drawStats(void) +{ + // line height + int lh; + + lh = (3 * SHORT(num[0]->height)) / 2; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + WI_drawLF(); + + V_DrawPatch(SP_STATSX, SP_STATSY, FB, kills); + WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]); + + V_DrawPatch(SP_STATSX, SP_STATSY + lh, FB, items); + WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY + lh, cnt_items[0]); + + V_DrawPatch(SP_STATSX, SP_STATSY + 2 * lh, FB, sp_secret); + WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY + 2 * lh, cnt_secret[0]); + + V_DrawPatch(SP_TIMEX, SP_TIMEY, FB, time_patch); + WI_drawTime(SCREENWIDTH / 2 - SP_TIMEX, SP_TIMEY, cnt_time); + + if (wbs->epsd < 3) + { + V_DrawPatch(SCREENWIDTH / 2 + SP_TIMEX, SP_TIMEY, FB, par); + WI_drawTime(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par); + } +} + + +void WI_checkForAccelerate(void) +{ + int i; + player_t* player; + + // check for button presses to skip delays + for (i = 0, player = players; i < MAXPLAYERS; i++, player++) + { + if (playeringame[i]) + { + if (player->cmd.buttons & BT_ATTACK) + { + if (!player->attackdown) + acceleratestage = 1; + player->attackdown = true; + } + else + player->attackdown = false; + if (player->cmd.buttons & BT_USE) + { + if (!player->usedown) + acceleratestage = 1; + player->usedown = true; + } + else + player->usedown = false; + } + } +} + + +// Updates stuff each tick +void WI_Ticker(void) +{ + // counter for general background animation + bcnt++; + + if (bcnt == 1) + { + // intermission music + if (gamemode == commercial) + S_ChangeMusic(mus_dm2int, true); + else + S_ChangeMusic(mus_inter, true); + } + + WI_checkForAccelerate(); + + switch (state) + { + case StatCount: + if (deathmatch) WI_updateDeathmatchStats(); + else if (netgame) WI_updateNetgameStats(); + else WI_updateStats(); + break; + + case ShowNextLoc: + WI_updateShowNextLoc(); + break; + + case NoState: + WI_updateNoState(); + break; + } +} + + +void WI_loadData(void) +{ + int i; + int j; + char name[9]; + anim_t_wi_stuff* a; + + if (gamemode == commercial) + doom_strcpy(name, "INTERPIC"); + else + { + //doom_sprintf(name, "WIMAP%d", wbs->epsd); + doom_strcpy(name, "WIMAP"); + doom_concat(name, doom_itoa(wbs->epsd, 10)); + } + + if (gamemode == retail) + { + if (wbs->epsd == 3) + doom_strcpy(name, "INTERPIC"); + } + + // background + bg = W_CacheLumpName(name, PU_CACHE); + V_DrawPatch(0, 0, 1, bg); + + if (gamemode == commercial) + { + NUMCMAPS = 32; + lnames = (patch_t**)Z_Malloc(sizeof(patch_t*) * NUMCMAPS, + PU_STATIC, 0); + for (i = 0; i < NUMCMAPS; i++) + { + //doom_sprintf(name, "CWILV%2.2d", i); + doom_strcpy(name, "CWILV"); + if (i < 10) doom_concat(name, "0"); + doom_concat(name, doom_itoa(i, 10)); + lnames[i] = W_CacheLumpName(name, PU_STATIC); + } + } + else + { + lnames = (patch_t**)Z_Malloc(sizeof(patch_t*) * NUMMAPS, + PU_STATIC, 0); + for (i = 0; i < NUMMAPS; i++) + { + //doom_sprintf(name, "WILV%d%d", wbs->epsd, i); + doom_strcpy(name, "WILV"); + doom_concat(name, doom_itoa(wbs->epsd, 10)); + doom_concat(name, doom_itoa(i, 10)); + lnames[i] = W_CacheLumpName(name, PU_STATIC); + } + + // you are here + yah[0] = W_CacheLumpName("WIURH0", PU_STATIC); + + // you are here (alt.) + yah[1] = W_CacheLumpName("WIURH1", PU_STATIC); + + // splat + splat = W_CacheLumpName("WISPLAT", PU_STATIC); + + if (wbs->epsd < 3) + { + for (j = 0; j < NUMANIMS[wbs->epsd]; j++) + { + a = &anims_wi_stuff[wbs->epsd][j]; + for (i = 0; i < a->nanims; i++) + { + // MONDO HACK! + if (wbs->epsd != 1 || j != 8) + { + // animations + //doom_sprintf(name, "WIA%d%.2d%.2d", wbs->epsd, j, i); + doom_strcpy(name, "WIA"); + doom_concat(name, doom_itoa(wbs->epsd, 10)); + if (j < 10) doom_concat(name, "0"); + doom_concat(name, doom_itoa(j, 10)); + if (i < 10) doom_concat(name, "0"); + doom_concat(name, doom_itoa(i, 10)); + a->p[i] = W_CacheLumpName(name, PU_STATIC); + } + else + { + // HACK ALERT! + a->p[i] = anims_wi_stuff[1][4].p[i]; + } + } + } + } + } + + // More hacks on minus sign. + wiminus = W_CacheLumpName("WIMINUS", PU_STATIC); + + for (i = 0; i < 10; i++) + { + // numbers 0-9 + //doom_sprintf(name, "WINUM%d", i); + doom_strcpy(name, "WINUM"); + doom_concat(name, doom_itoa(i, 10)); + num[i] = W_CacheLumpName(name, PU_STATIC); + } + + // percent sign + percent = W_CacheLumpName("WIPCNT", PU_STATIC); + + // "finished" + finished = W_CacheLumpName("WIF", PU_STATIC); + + // "entering" + entering = W_CacheLumpName("WIENTER", PU_STATIC); + + // "kills" + kills = W_CacheLumpName("WIOSTK", PU_STATIC); + + // "scrt" + secret = W_CacheLumpName("WIOSTS", PU_STATIC); + + // "secret" + sp_secret = W_CacheLumpName("WISCRT2", PU_STATIC); + + // Yuck. + if (french) + { + // "items" + if (netgame && !deathmatch) + items = W_CacheLumpName("WIOBJ", PU_STATIC); + else + items = W_CacheLumpName("WIOSTI", PU_STATIC); + } + else + items = W_CacheLumpName("WIOSTI", PU_STATIC); + + // "frgs" + frags = W_CacheLumpName("WIFRGS", PU_STATIC); + + // ":" + colon = W_CacheLumpName("WICOLON", PU_STATIC); + + // "time" + time_patch = W_CacheLumpName("WITIME", PU_STATIC); + + // "sucks" + sucks = W_CacheLumpName("WISUCKS", PU_STATIC); + + // "par" + par = W_CacheLumpName("WIPAR", PU_STATIC); + + // "killers" (vertical) + killers = W_CacheLumpName("WIKILRS", PU_STATIC); + + // "victims" (horiz) + victims = W_CacheLumpName("WIVCTMS", PU_STATIC); + + // "total" + total = W_CacheLumpName("WIMSTT", PU_STATIC); + + // your face + star = W_CacheLumpName("STFST01", PU_STATIC); + + // dead face + bstar = W_CacheLumpName("STFDEAD0", PU_STATIC); + + for (i = 0; i < MAXPLAYERS; i++) + { + // "1,2,3,4" + //doom_sprintf(name, "STPB%d", i); + doom_strcpy(name, "STPB"); + doom_concat(name, doom_itoa(i, 10)); + p[i] = W_CacheLumpName(name, PU_STATIC); + + // "1,2,3,4" + //doom_sprintf(name, "WIBP%d", i + 1); + doom_strcpy(name, "WIBP"); + doom_concat(name, doom_itoa(i + 1, 10)); + bp[i] = W_CacheLumpName(name, PU_STATIC); + } +} + + +void WI_unloadData(void) +{ + int i; + int j; + + Z_ChangeTag(wiminus, PU_CACHE); + + for (i = 0; i < 10; i++) + Z_ChangeTag(num[i], PU_CACHE); + + if (gamemode == commercial) + { + for (i = 0; i < NUMCMAPS; i++) + Z_ChangeTag(lnames[i], PU_CACHE); + } + else + { + Z_ChangeTag(yah[0], PU_CACHE); + Z_ChangeTag(yah[1], PU_CACHE); + + Z_ChangeTag(splat, PU_CACHE); + + for (i = 0; i < NUMMAPS; i++) + Z_ChangeTag(lnames[i], PU_CACHE); + + if (wbs->epsd < 3) + { + for (j = 0; j < NUMANIMS[wbs->epsd]; j++) + { + if (wbs->epsd != 1 || j != 8) + for (i = 0; i < anims_wi_stuff[wbs->epsd][j].nanims; i++) + Z_ChangeTag(anims_wi_stuff[wbs->epsd][j].p[i], PU_CACHE); + } + } + } + + Z_Free(lnames); + + Z_ChangeTag(percent, PU_CACHE); + Z_ChangeTag(colon, PU_CACHE); + Z_ChangeTag(finished, PU_CACHE); + Z_ChangeTag(entering, PU_CACHE); + Z_ChangeTag(kills, PU_CACHE); + Z_ChangeTag(secret, PU_CACHE); + Z_ChangeTag(sp_secret, PU_CACHE); + Z_ChangeTag(items, PU_CACHE); + Z_ChangeTag(frags, PU_CACHE); + Z_ChangeTag(time_patch, PU_CACHE); + Z_ChangeTag(sucks, PU_CACHE); + Z_ChangeTag(par, PU_CACHE); + + Z_ChangeTag(victims, PU_CACHE); + Z_ChangeTag(killers, PU_CACHE); + Z_ChangeTag(total, PU_CACHE); + // Z_ChangeTag(star, PU_CACHE); + // Z_ChangeTag(bstar, PU_CACHE); + + for (i = 0; i < MAXPLAYERS; i++) + Z_ChangeTag(p[i], PU_CACHE); + + for (i = 0; i < MAXPLAYERS; i++) + Z_ChangeTag(bp[i], PU_CACHE); +} + + +void WI_Drawer(void) +{ + switch (state) + { + case StatCount: + if (deathmatch) + WI_drawDeathmatchStats(); + else if (netgame) + WI_drawNetgameStats(); + else + WI_drawStats(); + break; + + case ShowNextLoc: + WI_drawShowNextLoc(); + break; + + case NoState: + WI_drawNoState(); + break; + } +} + + +void WI_initVariables(wbstartstruct_t* wbstartstruct) +{ + wbs = wbstartstruct; + +#ifdef RANGECHECKING + if (gamemode != commercial) + { + if (gamemode == retail) + RNGCHECK(wbs->epsd, 0, 3); + else + RNGCHECK(wbs->epsd, 0, 2); + } + else + { + RNGCHECK(wbs->last, 0, 8); + RNGCHECK(wbs->next, 0, 8); + } + RNGCHECK(wbs->pnum, 0, MAXPLAYERS); + RNGCHECK(wbs->pnum, 0, MAXPLAYERS); +#endif + + acceleratestage = 0; + cnt = bcnt = 0; + firstrefresh = 1; + me = wbs->pnum; + plrs = wbs->plyr; + + if (!wbs->maxkills) + wbs->maxkills = 1; + + if (!wbs->maxitems) + wbs->maxitems = 1; + + if (!wbs->maxsecret) + wbs->maxsecret = 1; + + if (gamemode != retail) + if (wbs->epsd > 2) + wbs->epsd -= 3; +} + + +void WI_Start(wbstartstruct_t* wbstartstruct) +{ + + WI_initVariables(wbstartstruct); + WI_loadData(); + + if (deathmatch) + WI_initDeathmatchStats(); + else if (netgame) + WI_initNetgameStats(); + else + WI_initStats(); +} +#define ZONEID 0x1d4a11 +#define MINFRAGMENT 64 + + +typedef struct +{ + // total bytes malloced, including header + int size; + + // start / end cap for linked list + memblock_t blocklist; + + memblock_t* rover; +} memzone_t; + + +memzone_t* mainzone; + + +// +// Z_ClearZone +// +void Z_ClearZone(memzone_t* zone) +{ + memblock_t* block; + + // set the entire zone to one free block + zone->blocklist.next = + zone->blocklist.prev = + block = (memblock_t*)((byte*)zone + sizeof(memzone_t)); + + zone->blocklist.user = (void*)zone; + zone->blocklist.tag = PU_STATIC; + zone->rover = block; + + block->prev = block->next = &zone->blocklist; + + // 0 indicates a free block. + block->user = 0; + + block->size = zone->size - sizeof(memzone_t); +} + + +// +// Z_Init +// +void Z_Init(void) +{ + memblock_t* block; + int size; + + mainzone = (memzone_t*)I_ZoneBase(&size); + mainzone->size = size; + + // set the entire zone to one free block + mainzone->blocklist.next = + mainzone->blocklist.prev = + block = (memblock_t*)((byte*)mainzone + sizeof(memzone_t)); + + mainzone->blocklist.user = (void*)mainzone; + mainzone->blocklist.tag = PU_STATIC; + mainzone->rover = block; + + block->prev = block->next = &mainzone->blocklist; + + // 0 indicates a free block. + block->user = 0; + + block->size = mainzone->size - sizeof(memzone_t); +} + + +// +// Z_Free +// +void Z_Free(void* ptr) +{ + memblock_t* block; + memblock_t* other; + + block = (memblock_t*)((byte*)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + I_Error("Error: Z_Free: freed a pointer without ZONEID"); + + if (block->user > (void**)0x100) + { + // smaller values are not pointers + // Note: OS-dependend? + + // clear the user's mark + *block->user = 0; + } + + // mark as free + block->user = 0; + block->tag = 0; + block->id = 0; + + other = block->prev; + + if (!other->user) + { + // merge with previous free block + other->size += block->size; + other->next = block->next; + other->next->prev = other; + + if (block == mainzone->rover) + mainzone->rover = other; + + block = other; + } + + other = block->next; + if (!other->user) + { + // merge the next free block onto the end + block->size += other->size; + block->next = other->next; + block->next->prev = block; + + if (other == mainzone->rover) + mainzone->rover = block; + } +} + + +// +// Z_Malloc +// You can pass a 0 user if the tag is < PU_PURGELEVEL. +// +void* Z_Malloc(int size, int tag, void* user) +{ + int extra; + memblock_t* start; + memblock_t* rover; + memblock_t* newblock; + memblock_t* base; + + size = (size + 3) & ~3; + + // scan through the block list, + // looking for the first free block + // of sufficient size, + // throwing out any purgable blocks along the way. + + // account for size of block header + size += sizeof(memblock_t); + + // if there is a free block behind the rover, + // back up over them + base = mainzone->rover; + + if (!base->prev->user) + base = base->prev; + + rover = base; + start = base->prev; + + do + { + if (rover == start) + { + // scanned all the way around the list + //I_Error("Error: Z_Malloc: failed on allocation of %i bytes", size); + doom_strcpy(error_buf, "Error: Z_Malloc: failed on allocation of "); + doom_concat(error_buf, doom_itoa(size, 10)); + doom_concat(error_buf, " bytes"); + I_Error(error_buf); + } + + if (rover->user) + { + if (rover->tag < PU_PURGELEVEL) + { + // hit a block that can't be purged, + // so move base past it + base = rover = rover->next; + } + else + { + // free the rover block (adding the size to base) + + // the rover can be the base block + base = base->prev; + Z_Free((byte*)rover + sizeof(memblock_t)); + base = base->next; + rover = base->next; + } + } + else + rover = rover->next; + } while (base->user || base->size < size); + + + // found a block big enough + extra = base->size - size; + + if (extra > MINFRAGMENT) + { + // there will be a free fragment after the allocated block + newblock = (memblock_t*)((byte*)base + size); + newblock->size = extra; + + // 0 indicates free block. + newblock->user = 0; + newblock->tag = 0; + newblock->prev = base; + newblock->next = base->next; + newblock->next->prev = newblock; + + base->next = newblock; + base->size = size; + } + + if (user) + { + // mark as an in use block + base->user = user; + *(void**)user = (void*)((byte*)base + sizeof(memblock_t)); + } + else + { + if (tag >= PU_PURGELEVEL) + I_Error("Error: Z_Malloc: an owner is required for purgable blocks"); + + // mark as in use, but unowned + base->user = (void*)2; + } + base->tag = tag; + + // next allocation will start looking here + mainzone->rover = base->next; + + base->id = ZONEID; + + return (void*)((byte*)base + sizeof(memblock_t)); +} + + +// +// Z_FreeTags +// +void Z_FreeTags(int lowtag, int hightag) +{ + memblock_t* block; + memblock_t* next; + + for (block = mainzone->blocklist.next; + block != &mainzone->blocklist; + block = next) + { + // get link before freeing + next = block->next; + + // free block? + if (!block->user) + continue; + + if (block->tag >= lowtag && block->tag <= hightag) + Z_Free((byte*)block + sizeof(memblock_t)); + } +} + + +// +// Z_DumpHeap +// Note: TFileDumpHeap( stdout ) ? +// +void Z_DumpHeap(int lowtag, int hightag) +{ + memblock_t* block; + + //doom_print("zone size: %i location: %p\n", + // mainzone->size, mainzone); + doom_print("zone size: "); + doom_print(doom_itoa(mainzone->size, 10)); + doom_print(" location: "); + doom_print(doom_ptoa(mainzone)); + doom_print("\n"); + + //doom_print("tag range: %i to %i\n", + // lowtag, hightag); + doom_print("tag range: "); + doom_print(doom_itoa(lowtag, 10)); + doom_print(" to "); + doom_print(doom_itoa(hightag, 10)); + doom_print("\n"); + + for (block = mainzone->blocklist.next; ; block = block->next) + { + if (block->tag >= lowtag && block->tag <= hightag) + { + //doom_print("block:%p size:%7i user:%p tag:%3i\n", block, block->size, block->user, block->tag); + doom_print("block"); + doom_print(doom_ptoa(block)); + doom_print(" size:"); + doom_print(doom_itoa(block->size, 10)); + doom_print(" user:"); + doom_print(doom_ptoa(block->user)); + doom_print(" tag:"); + doom_print(doom_itoa(block->tag, 10)); + doom_print("\n"); + } + + if (block->next == &mainzone->blocklist) + { + // all blocks have been hit + break; + } + + if ((byte*)block + block->size != (byte*)block->next) + doom_print("ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + doom_print("ERROR: next block doesn't have proper back link\n"); + + if (!block->user && !block->next->user) + doom_print("ERROR: two consecutive free blocks\n"); + } +} + + +// +// Z_FileDumpHeap +// +void Z_FileDumpHeap(void* f) +{ + memblock_t* block; + + //fprintf(f, "zone size: %i location: %p\n", mainzone->size, mainzone); + doom_fprint(f, "zone size: "); + doom_fprint(f, doom_itoa(mainzone->size, 10)); + doom_fprint(f, " location: "); + doom_fprint(f, doom_ptoa(mainzone)); + doom_fprint(f, "\n"); + + for (block = mainzone->blocklist.next; ; block = block->next) + { + //fprintf(f, "block:%p size:%7i user:%p tag:%3i\n", + // block, block->size, block->user, block->tag); + doom_fprint(f, "block:"); + doom_fprint(f, doom_ptoa(block)); + doom_fprint(f, " size:"); + doom_fprint(f, doom_itoa(block->size, 10)); // Supposed to alignt 7, this is not worth the trouble + doom_fprint(f, " user:"); + doom_fprint(f, doom_ptoa(block->user)); + doom_fprint(f, " tag:"); + doom_fprint(f, doom_itoa(block->tag, 10)); + doom_fprint(f, "\n"); + + + if (block->next == &mainzone->blocklist) + { + // all blocks have been hit + break; + } + + if ((byte*)block + block->size != (byte*)block->next) + doom_fprint(f, "ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + doom_fprint(f, "ERROR: next block doesn't have proper back link\n"); + + if (!block->user && !block->next->user) + doom_fprint(f, "ERROR: two consecutive free blocks\n"); + } +} + + +// +// Z_CheckHeap +// +void Z_CheckHeap(void) +{ + memblock_t* block; + + for (block = mainzone->blocklist.next; ; block = block->next) + { + if (block->next == &mainzone->blocklist) + { + // all blocks have been hit + break; + } + + if ((byte*)block + block->size != (byte*)block->next) + I_Error("Error: Z_CheckHeap: block size does not touch the next block\n"); + + if (block->next->prev != block) + I_Error("Error: Z_CheckHeap: next block doesn't have proper back link\n"); + + if (!block->user && !block->next->user) + I_Error("Error: Z_CheckHeap: two consecutive free blocks\n"); + } +} + + +// +// Z_ChangeTag +// +void Z_ChangeTag2(void* ptr, int tag) +{ + memblock_t* block; + + block = (memblock_t*)((byte*)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + I_Error("Error: Z_ChangeTag: freed a pointer without ZONEID"); + + if (tag >= PU_PURGELEVEL && (unsigned long long)block->user < 0x100) + I_Error("Error: Z_ChangeTag: an owner is required for purgable blocks"); + + block->tag = tag; +} + + +// +// Z_FreeMemory +// +int Z_FreeMemory(void) +{ + memblock_t* block; + int free; + + free = 0; + + for (block = mainzone->blocklist.next; + block != &mainzone->blocklist; + block = block->next) + { + if (!block->user || block->tag >= PU_PURGELEVEL) + free += block->size; + } + return free; +} + +#endif // DOOM_IMPLEMENTATION diff --git a/README.md b/README.md new file mode 100644 index 0000000..51f1c43 --- /dev/null +++ b/README.md @@ -0,0 +1,266 @@ +![](images/PureDOOM.png) + +# Pure DOOM +Header only, no dependencies DOOM source port. Designed to run on any devices. + +Primarily of interest to people that want to "run DOOM on their microwave". + +## LICENSE +See end of file for license information. + +## Main Features: +- Single header +- Pure-C, No includes dependencies: no stdlib, stdio, etc. +- Supports 32 bits and 64 bits + +## Other Features: +- Menu option to disable mouse move Forward/Backward +- Crosshair option +- Always-Run option + +## TODOs +- Custom resolution +- Remove exit and have update return 0 on quit. Add doom_get_exit_code() +- Implement Sockets and Multiplayer +- Print message when discovered a secret + +## Cool TODOs to have +- Rebindable keys +- Unlocked FPS +- Release mouse in menus and use it for clicking +- Wipe screen freezes menu +- Use floats instead of fixed_t +- French and german +- Full color mode (Don't use COLORMAPS, use full 24 bits RGB) + +## Usage +Call `doom_init()`, then call `doom_update()` every frame, or as often as you can. This will run DOOM, without any video, inputs, sounds or music. + +```c +#define DOOM_IMPLEMENTATION +#include "PureDOOM.h" + +int main(int argc, char** argv) +{ + doom_init(argc, argv, 0); + while (true) + { + doom_update(); + } +} +``` + +## Enable features +Most standard headers are available on most platforms. Define these preprocessors to toggle these features. + +- `DOOM_IMPLEMENT_PRINT`. Allows printf, requires `` +- `DOOM_IMPLEMENT_MALLOC`. Allows malloc/free, requires `` +- `DOOM_IMPLEMENT_FILE_IO`. Allows FILE, requires `` +- `DOOM_IMPLEMENT_GETTIME`. Requires `` or `` +- `DOOM_IMPLEMENT_EXIT`. Allows exit(), requires `` +- `DOOM_IMPLEMENT_GETENV`. Requires `` + +If your microwave doesn't have these headers, you can override their default implementations: + +```c +void doom_set_print(doom_print_fn print_fn); +void doom_set_malloc(doom_malloc_fn malloc_fn, doom_free_fn free_fn); +void doom_set_file_io(doom_open_fn open_fn, + doom_close_fn close_fn, + doom_read_fn read_fn, + doom_write_fn write_fn, + doom_seek_fn seek_fn, + doom_tell_fn tell_fn, + doom_eof_fn eof_fn); +void doom_set_gettime(doom_gettime_fn gettime_fn); +void doom_set_exit(doom_exit_fn exit_fn); +void doom_set_getenv(doom_getenv_fn getenv_fn); +``` + +## Video +Every frame, after having called `doom_update()`, you can get the screen pixels with `doom_get_framebuffer` and display it as you please. + +```c +while (true) +{ + doom_update(); + uint8_t* framebuffer = doom_get_framebuffer(4 /* RGBA */); + // ... Display framebuffer +} +``` + +## Inputs +When you receive input events from your microwave touch pad, simply call one of the DOOM input events: + +```c +void doom_key_down(doom_key_t key); +void doom_key_up(doom_key_t key); +void doom_button_down(doom_button_t button); +void doom_button_up(doom_button_t button); +void doom_mouse_move(int delta_x, int delta_y); +``` + +## Sounds +Create a sound thread that outputs at 11025hz (`DOOM_SAMPLERATE`), 512 samples, 16 bits, stereo. Then in your sound callback, call `doom_get_sound_buffer` to update and get the current DOOM's sound output. Make sure to add synchronization primitives around this and `doom_update` if your sound loop is in a thread. + +Here is a quick example using SDL audio callback: +```c +void sdl_audio_callback(void* userdata, Uint8* stream, int len) +{ + SDL_LockAudio(); + int16_t* buffer = doom_get_sound_buffer(len); + SDL_UnlockAudio(); + + memcpy(stream, buffer, len); +} +``` + +You can use different bitrate, but make sure to resample because DOOM will always be 11025hz, 512 samples, 16 bits, 2 channels. For a total for 2048 bytes per buffer. + +## Music +Set a timer in your application that runs at 140hz. In the timer's callback, tick DOOM's music as long as there are MIDI messages to send. + +Here is an example using Window's MultiMedia to play MIDI events, using an SDL timer. +```c +Uint32 tick_music(Uint32 interval, void *param) +{ + uint32_t midi_msg; + + SDL_LockAudio(); + + while (midi_msg = doom_tick_midi()) + midiOutShortMsg(midi_out_handle, midi_msg); + + SDL_UnlockAudio(); + + return 1000 / DOOM_MIDI_RATE /* 140 */; +} +``` + +## Change default settings +Default input setup in DOOM's source is not modern. It uses Arrows to move and `','` / `'.'` to strafe. You can call `doom_set_default_int` and `doom_set_default_str` to change them: + +```c +// Change default bindings to modern mapping +doom_set_default_int("key_up", DOOM_KEY_W); +doom_set_default_int("key_down", DOOM_KEY_S); +doom_set_default_int("key_strafeleft", DOOM_KEY_A); +doom_set_default_int("key_straferight", DOOM_KEY_D); +doom_set_default_int("key_use", DOOM_KEY_E); +doom_set_default_int("mouse_move", 0); // Mouse will not move forward +``` + +Refer to the defaults in `m_misc.cpp` for the complete list. + +## Working SDL Example +See the file `src/sdl_example.c` for a complete SDL example. + +# DOOM LICENSE +``` + LIMITED USE SOFTWARE LICENSE AGREEMENT + + This Limited Use Software License Agreement (the "Agreement") +is a legal agreement between you, the end-user, and Id Software, Inc. +("ID"). By downloading or purchasing the software material, which +includes source code (the "Source Code"), artwork data, music and +software tools (collectively, the "Software"), you are agreeing to +be bound by the terms of this Agreement. If you do not agree to the +terms of this Agreement, promptly destroy the Software you may have +downloaded or copied. + +ID SOFTWARE LICENSE + +1. Grant of License. ID grants to you the right to use the +Software. You have no ownership or proprietary rights in or to the +Software, or the Trademark. For purposes of this section, "use" means +loading the Software into RAM, as well as installation on a hard disk +or other storage device. The Software, together with any archive copy +thereof, shall be destroyed when no longer used in accordance with +this Agreement, or when the right to use the Software is terminated. +You agree that the Software will not be shipped, transferred or +exported into any country in violation of the U.S. Export +Administration Act (or any other law governing such matters) and that +you will not utilize, in any other manner, the Software in violation +of any applicable law. + +2. Permitted Uses. For educational purposes only, you, the +end-user, may use portions of the Source Code, such as particular +routines, to develop your own software, but may not duplicate the +Source Code, except as noted in paragraph 4. The limited right +referenced in the preceding sentence is hereinafter referred to as +"Educational Use." By so exercising the Educational Use right you +shall not obtain any ownership, copyright, proprietary or other +interest in or to the Source Code, or any portion of the Source +Code. You may dispose of your own software in your sole discretion. +With the exception of the Educational Use right, you may not +otherwise use the Software, or an portion of the Software, which +includes the Source Code, for commercial gain. + +3. Prohibited Uses: Under no circumstances shall you, the +end-user, be permitted, allowed or authorized to commercially exploit +the Software. Neither you nor anyone at your direction shall do any +of the following acts with regard to the Software, or any portion +thereof: + + Rent; + + Sell; + + Lease; + + Offer on a pay-per-play basis; + + Distribute for money or any other consideration; or + + In any other manner and through any medium whatsoever +commercially exploit or use for any commercial purpose. + +Notwithstanding the foregoing prohibitions, you may commercially +exploit the software you develop by exercising the Educational Use +right, referenced in paragraph 2. hereinabove. + +4. Copyright. The Software and all copyrights related thereto +(including all characters and other images generated by the Software +or depicted in the Software) are owned by ID and is protected by +United States copyright laws and international treaty provisions. +Id shall retain exclusive ownership and copyright in and to the +Software and all portions of the Software and you shall have no +ownership or other proprietary interest in such materials. You must +treat the Software like any other copyrighted material. You may not +otherwise reproduce, copy or disclose to others, in whole or in any +part, the Software. You may not copy the written materials +accompanying the Software. You agree to use your best efforts to +see that any user of the Software licensed hereunder complies with +this Agreement. + +5. NO WARRANTIES. ID DISCLAIMS ALL WARRANTIES, BOTH EXPRESS +IMPLIED, INCLUDING BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE WITH RESPECT +TO THE SOFTWARE. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL +RIGHTS. YOU MAY HAVE OTHER RIGHTS WHICH VARY FROM JURISDICTION TO +JURISDICTION. ID DOES NOT WARRANT THAT THE OPERATION OF THE SOFTWARE +WILL BE UNINTERRUPTED, ERROR FREE OR MEET YOUR SPECIFIC REQUIREMENTS. +THE WARRANTY SET FORTH ABOVE IS IN LIEU OF ALL OTHER EXPRESS +WARRANTIES WHETHER ORAL OR WRITTEN. THE AGENTS, EMPLOYEES, +DISTRIBUTORS, AND DEALERS OF ID ARE NOT AUTHORIZED TO MAKE +MODIFICATIONS TO THIS WARRANTY, OR ADDITIONAL WARRANTIES ON BEHALF +OF ID. + + Exclusive Remedies. The Software is being offered to you +free of any charge. You agree that you have no remedy against ID, its +affiliates, contractors, suppliers, and agents for loss or damage +caused by any defect or failure in the Software regardless of the form +of action, whether in contract, tort, includinegligence, strict +liability or otherwise, with regard to the Software. This Agreement +shall be construed in accordance with and governed by the laws of the +State of Texas. Copyright and other proprietary matters will be +governed by United States laws and international treaties. IN ANY +CASE, ID SHALL NOT BE LIABLE FOR LOSS OF DATA, LOSS OF PROFITS, LOST +SAVINGS, SPECIAL, INCIDENTAL, CONSEQUENTIAL, INDIRECT OR OTHER +SIMILAR DAMAGES ARISING FROM BREACH OF WARRANTY, BREACH OF CONTRACT, +NEGLIGENCE, OR OTHER LEGAL THEORY EVEN IF ID OR ITS AGENT HAS BEEN +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY +OTHER PARTY. Some jurisdictions do not allow the exclusion or +limitation of incidental or consequential damages, so the above +limitation or exclusion may not apply to you. +``` diff --git a/examples/SDL/CMakeLists.txt b/examples/SDL/CMakeLists.txt new file mode 100644 index 0000000..32931f6 --- /dev/null +++ b/examples/SDL/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.0) + +# Project +project(pd_sdl_example) + +# add_definitions(-DDOOM_FAST_TICK) +add_definitions( + -DDOOM_IMPLEMENT_PRINT + -DDOOM_IMPLEMENT_MALLOC + -DDOOM_IMPLEMENT_FILE_IO + -DDOOM_IMPLEMENT_SOCKETS + -DDOOM_IMPLEMENT_GETTIME + -DDOOM_IMPLEMENT_EXIT + -DDOOM_IMPLEMENT_GETENV +) + +# Sources +file(GLOB dpsdl_src_files sdl_example.c) +file(GLOB dpsdl_doom_src_files ../../src/DOOM/*.*) +list(APPEND dpsdl_includes PUBLIC ../../src/) + +# SDL +add_definitions(-DSDL_MAIN_HANDLED) +set(HAVE_LIBC ON) +add_subdirectory(../../thirdparty/SDL/ ./thirdparty/SDL/) +list(APPEND dpsdl_libs SDL2-static) +list(APPEND dpsdl_includes PUBLIC ../../thirdparty/SDL/include/) + +# Source groups +source_group("Example" FILES ${dpsdl_src_files}) +source_group("DOOM" FILES ${dpsdl_doom_src_files}) + +# Executable +add_executable(${PROJECT_NAME} ${dpsdl_src_files}) + +# Work dir +set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../") + +# For midi output on mac +if (APPLE) + list(APPEND dpsdl_libs "-framework CoreFoundation") + list(APPEND dpsdl_libs "-framework AudioToolbox") + list(APPEND dpsdl_libs "-framework CoreMIDI") + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-Wl,-F/Library/Frameworks") +endif() + +# Lib/Headers +target_include_directories(${PROJECT_NAME} PUBLIC ${dpsdl_includes}) +target_link_libraries(${PROJECT_NAME} PUBLIC ${dpsdl_libs}) diff --git a/examples/SDL/sdl_example.c b/examples/SDL/sdl_example.c new file mode 100644 index 0000000..6c6424b --- /dev/null +++ b/examples/SDL/sdl_example.c @@ -0,0 +1,514 @@ +#if defined(WIN32) +#pragma comment(lib, "winmm.lib") +#include +#include +#elif defined(__APPLE__) +#include +#include +#endif +#include +#include +#include +#include + +#if 1 // Use the PureDOOM.h single header +#define DOOM_IMPLEMENTATION +#include "../../PureDOOM.h" +#else +#include "DOOM/DOOM.h" +#endif + + +// Palette experiments +//#define PICO8 1 +//#define CGA 1 +//#define GAME_BOY 1 + + +// Resolution DOOM renders at +#define WIDTH 320 +#define HEIGHT 200 +#define SCALE 4 + +// Resolution of the SDL window +#define FULL_WIDTH (WIDTH * SCALE) +#define FULL_HEIGHT (int)(HEIGHT * 1.2 * SCALE) // 1.2x higher than DOOM's height. Original game was designed stretched + + +doom_key_t sdl_scancode_to_doom_key(SDL_Scancode scancode) +{ + switch (scancode) + { + case SDL_SCANCODE_TAB: return DOOM_KEY_TAB; + case SDL_SCANCODE_RETURN: return DOOM_KEY_ENTER; + case SDL_SCANCODE_ESCAPE: return DOOM_KEY_ESCAPE; + case SDL_SCANCODE_SPACE: return DOOM_KEY_SPACE; + case SDL_SCANCODE_APOSTROPHE: return DOOM_KEY_APOSTROPHE; + case SDL_SCANCODE_KP_MULTIPLY: return DOOM_KEY_MULTIPLY; + case SDL_SCANCODE_COMMA: return DOOM_KEY_COMMA; + case SDL_SCANCODE_MINUS: return DOOM_KEY_MINUS; + case SDL_SCANCODE_PERIOD: return DOOM_KEY_PERIOD; + case SDL_SCANCODE_SLASH: return DOOM_KEY_SLASH; + case SDL_SCANCODE_0: return DOOM_KEY_0; + case SDL_SCANCODE_1: return DOOM_KEY_1; + case SDL_SCANCODE_2: return DOOM_KEY_2; + case SDL_SCANCODE_3: return DOOM_KEY_3; + case SDL_SCANCODE_4: return DOOM_KEY_4; + case SDL_SCANCODE_5: return DOOM_KEY_5; + case SDL_SCANCODE_6: return DOOM_KEY_6; + case SDL_SCANCODE_7: return DOOM_KEY_7; + case SDL_SCANCODE_8: return DOOM_KEY_8; + case SDL_SCANCODE_9: return DOOM_KEY_9; + case SDL_SCANCODE_SEMICOLON: return DOOM_KEY_SEMICOLON; + case SDL_SCANCODE_EQUALS: return DOOM_KEY_EQUALS; + case SDL_SCANCODE_LEFTBRACKET: return DOOM_KEY_LEFT_BRACKET; + case SDL_SCANCODE_RIGHTBRACKET: return DOOM_KEY_RIGHT_BRACKET; + case SDL_SCANCODE_A: return DOOM_KEY_A; + case SDL_SCANCODE_B: return DOOM_KEY_B; + case SDL_SCANCODE_C: return DOOM_KEY_C; + case SDL_SCANCODE_D: return DOOM_KEY_D; + case SDL_SCANCODE_E: return DOOM_KEY_E; + case SDL_SCANCODE_F: return DOOM_KEY_F; + case SDL_SCANCODE_G: return DOOM_KEY_G; + case SDL_SCANCODE_H: return DOOM_KEY_H; + case SDL_SCANCODE_I: return DOOM_KEY_I; + case SDL_SCANCODE_J: return DOOM_KEY_J; + case SDL_SCANCODE_K: return DOOM_KEY_K; + case SDL_SCANCODE_L: return DOOM_KEY_L; + case SDL_SCANCODE_M: return DOOM_KEY_M; + case SDL_SCANCODE_N: return DOOM_KEY_N; + case SDL_SCANCODE_O: return DOOM_KEY_O; + case SDL_SCANCODE_P: return DOOM_KEY_P; + case SDL_SCANCODE_Q: return DOOM_KEY_Q; + case SDL_SCANCODE_R: return DOOM_KEY_R; + case SDL_SCANCODE_S: return DOOM_KEY_S; + case SDL_SCANCODE_T: return DOOM_KEY_T; + case SDL_SCANCODE_U: return DOOM_KEY_U; + case SDL_SCANCODE_V: return DOOM_KEY_V; + case SDL_SCANCODE_W: return DOOM_KEY_W; + case SDL_SCANCODE_X: return DOOM_KEY_X; + case SDL_SCANCODE_Y: return DOOM_KEY_Y; + case SDL_SCANCODE_Z: return DOOM_KEY_Z; + case SDL_SCANCODE_BACKSPACE: return DOOM_KEY_BACKSPACE; + case SDL_SCANCODE_LCTRL: + case SDL_SCANCODE_RCTRL: return DOOM_KEY_CTRL; + case SDL_SCANCODE_LEFT: return DOOM_KEY_LEFT_ARROW; + case SDL_SCANCODE_UP: return DOOM_KEY_UP_ARROW; + case SDL_SCANCODE_RIGHT: return DOOM_KEY_RIGHT_ARROW; + case SDL_SCANCODE_DOWN: return DOOM_KEY_DOWN_ARROW; + case SDL_SCANCODE_LSHIFT: + case SDL_SCANCODE_RSHIFT: return DOOM_KEY_SHIFT; + case SDL_SCANCODE_LALT: + case SDL_SCANCODE_RALT: return DOOM_KEY_ALT; + case SDL_SCANCODE_F1: return DOOM_KEY_F1; + case SDL_SCANCODE_F2: return DOOM_KEY_F2; + case SDL_SCANCODE_F3: return DOOM_KEY_F3; + case SDL_SCANCODE_F4: return DOOM_KEY_F4; + case SDL_SCANCODE_F5: return DOOM_KEY_F5; + case SDL_SCANCODE_F6: return DOOM_KEY_F6; + case SDL_SCANCODE_F7: return DOOM_KEY_F7; + case SDL_SCANCODE_F8: return DOOM_KEY_F8; + case SDL_SCANCODE_F9: return DOOM_KEY_F9; + case SDL_SCANCODE_F10: return DOOM_KEY_F10; + case SDL_SCANCODE_F11: return DOOM_KEY_F11; + case SDL_SCANCODE_F12: return DOOM_KEY_F12; + case SDL_SCANCODE_PAUSE: return DOOM_KEY_PAUSE; + default: return DOOM_KEY_UNKNOWN; + } + return DOOM_KEY_UNKNOWN; +} + + +doom_button_t sdl_button_to_doom_button(Uint8 sdl_button) +{ + switch (sdl_button) + { + case SDL_BUTTON_LEFT: return DOOM_LEFT_BUTTON; + case SDL_BUTTON_RIGHT: return DOOM_RIGHT_BUTTON; + case SDL_BUTTON_MIDDLE: return DOOM_MIDDLE_BUTTON; + } + return (doom_button_t)0; +} + + +void audio_callback(void* userdata, Uint8* stream, int len) +{ + SDL_LockAudio(); + int16_t* buffer = doom_get_sound_buffer(); + SDL_UnlockAudio(); + + memcpy(stream, buffer, len); +} + + +#if defined(WIN32) +static HMIDIOUT midi_out_handle = 0; +void send_midi_msg(uint32_t midi_msg) +{ + if (midi_out_handle) + { + midiOutShortMsg(midi_out_handle, midi_msg); + } +} +#elif defined(__APPLE__) +AudioUnit audio_unit = 0; +void send_midi_msg(uint32_t midi_msg) +{ + if (audio_unit) + { + MusicDeviceMIDIEvent(audio_unit, + (midi_msg) & 0xFF, + (midi_msg >> 8) & 0xFF, + (midi_msg >> 16) & 0xFF, + 0); + } +} +#else +void send_midi_msg(uint32_t midi_msg) {} +#endif + + +SDL_TimerID midi_timer = 0; +Uint32 tick_midi(Uint32 interval, void *param) +{ + uint32_t midi_msg; + + SDL_LockAudio(); + while ((midi_msg = doom_tick_midi()) != 0) send_midi_msg(midi_msg); + SDL_UnlockAudio(); + +#if defined(__APPLE__) + return 1000 / DOOM_MIDI_RATE - 1; // Weirdly, on Apple music is too slow +#else + return 1000 / DOOM_MIDI_RATE; +#endif +} + + +int main(int argc, char** argv) +{ + // Init SDL + SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_TIMER); + SDL_Window* window = SDL_CreateWindow("Pure DOOM - SDL Example", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + FULL_WIDTH, FULL_HEIGHT, + SDL_WINDOW_RESIZABLE); + + // SDL Renderer + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE); + SDL_Texture* render_target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT); + + // SDL Audio thread + SDL_AudioSpec audio_spec; + memset(&audio_spec, 0, sizeof(audio_spec)); + audio_spec.freq = DOOM_SAMPLERATE; + audio_spec.format = AUDIO_S16; + audio_spec.channels = 2; + audio_spec.samples = 512; + audio_spec.callback = audio_callback; + + if (SDL_OpenAudio(&audio_spec, NULL) < 0) + { + printf("Failed to SDL_OpenAudio\n"); + return 1; + } + + // Capture mouse + SDL_SetRelativeMouseMode(SDL_TRUE); + + //----------------------------------------------------------------------- + // Setup DOOM + //----------------------------------------------------------------------- + + // Change default bindings to modern + doom_set_default_int("key_up", DOOM_KEY_W); + doom_set_default_int("key_down", DOOM_KEY_S); + doom_set_default_int("key_strafeleft", DOOM_KEY_A); + doom_set_default_int("key_straferight", DOOM_KEY_D); + doom_set_default_int("key_use", DOOM_KEY_E); + doom_set_default_int("mouse_move", 0); // Mouse will not move forward + + // Setup resolution + doom_set_resolution(WIDTH, HEIGHT); + + // Setup MIDI for songs +#if defined(WIN32) + if (midiOutGetNumDevs() != 0) + midiOutOpen(&midi_out_handle, 0, 0, 0, 0); +#elif defined(__APPLE__) + // I don't really understand this part, but it works. + AUGraph graph; + AUNode outputNode, mixerNode, dlsNode; + NewAUGraph(&graph); + AudioComponentDescription output = {'auou','ahal','appl',0,0}; + AUGraphAddNode(graph, &output, &outputNode); + AUGraphOpen(graph); + AUGraphInitialize(graph); + AUGraphStart(graph); + AudioComponentDescription dls = {'aumu','dls ','appl',0,0}; + AUGraphAddNode(graph, &dls, &dlsNode); + AUGraphNodeInfo(graph, dlsNode, NULL, &audio_unit); + AudioComponentDescription mixer = {'aumx','smxr','appl',0,0}; + AUGraphAddNode(graph, &mixer, &mixerNode); + AUGraphConnectNodeInput(graph,mixerNode,0,outputNode,0); + AUGraphConnectNodeInput(graph,dlsNode,0,mixerNode,0); + AUGraphUpdate(graph,NULL); +#endif + midi_timer = SDL_AddTimer(0, tick_midi, 0); + + // Initialize doom + doom_init(argc, argv, DOOM_FLAG_MENU_DARKEN_BG); + + //----------------------------------------------------------------------- + + // Main loop + SDL_PauseAudio(0); + int done = 0; + int active_mouse = 1; // Dev allow us to take mouse out of window + while (!done) + { + SDL_Event e; + Sint32 mouse_motion_x = 0; + Sint32 mouse_motion_y = 0; + while (SDL_PollEvent(&e)) + { + switch (e.type) + { + case SDL_QUIT: + done = 1; + break; + + case SDL_KEYDOWN: + if (e.key.keysym.scancode == SDL_SCANCODE_END) + { + active_mouse = !active_mouse; + SDL_SetRelativeMouseMode(active_mouse ? SDL_TRUE : SDL_FALSE); + } + + if (!e.key.repeat) + doom_key_down(sdl_scancode_to_doom_key(e.key.keysym.scancode)); + break; + + case SDL_KEYUP: + if (!e.key.repeat) + doom_key_up(sdl_scancode_to_doom_key(e.key.keysym.scancode)); + break; + + case SDL_MOUSEBUTTONDOWN: + if (active_mouse) doom_button_down(sdl_button_to_doom_button(e.button.button)); + break; + + case SDL_MOUSEBUTTONUP: + if (active_mouse) doom_button_up(sdl_button_to_doom_button(e.button.button)); + break; + + case SDL_MOUSEMOTION: + if (active_mouse) + { + mouse_motion_x += e.motion.xrel; + mouse_motion_y += e.motion.yrel; + } + break; + } + if (done) break; + } + if (done) break; + + if (mouse_motion_x || mouse_motion_y) + doom_mouse_move(mouse_motion_x * 4, mouse_motion_y * 4); + + SDL_LockAudio(); + doom_update(); + SDL_UnlockAudio(); + + // Blit DOOM's framebuffer onto our SDL texture +#if GAME_BOY + void* dst; + const unsigned char* src = doom_get_framebuffer(3); + int src_pitch = WIDTH * 3; + int dst_pitch; + const int palette_size = 4; + const int pico8_palette[4 * 3] = { + 0x0f, 0x3, 0x80f, + 0x30, 0x62, 0x30, + 0x8b, 0xac, 0x0f, + 0x9b, 0xbc, 0x0f + }; + if (!SDL_LockTexture(render_target, NULL, &dst, &dst_pitch)) + { + unsigned char* dst8 = (unsigned char*)dst; + for (int y = 0; y < HEIGHT; ++y) + { + for (int x = 0; x < WIDTH; ++x) + { + int dstk = y * dst_pitch + x * 4; + int srck = y * src_pitch + x * 3; + + int r = src[srck + 0]; + int g = src[srck + 1]; + int b = src[srck + 2]; + + int best = 0; + int best_score = (r * r + g * g + b * b) * 2; + for (int p = 0; p < palette_size * 3; p += 3) + { + const int* pico8_color = pico8_palette + p; + if (pico8_color[0] == r && + pico8_color[1] == g && + pico8_color[2] == b) + { + // Perfect match + best = p; + break; + } + int ri = pico8_color[0] - r; + int gi = pico8_color[1] - g; + int bi = pico8_color[2] - b; + int score = ri * ri + gi * gi + bi * bi; + if (score < best_score) + { + best_score = score; + best = p; + } + } + + dst8[dstk + 0] = pico8_palette[best + 0]; + dst8[dstk + 1] = pico8_palette[best + 1]; + dst8[dstk + 2] = pico8_palette[best + 2]; + dst8[dstk + 3] = 255; + } + } + SDL_UnlockTexture(render_target); + } +#elif PICO8 || CGA + void* dst; + const unsigned char* src = doom_get_framebuffer(3); + int src_pitch = WIDTH * 3; + int dst_pitch; +#if CGA + const int palette_size = 16; + const int pico8_palette[16 * 3] = { + 0, 0, 0, + 0, 0, 0xAA, + 0, 0xAA, 0, + 0, 0xAA, 0xAA, + 0xAA, 0, 0, + 0xAA, 0, 0xAA, + 0xAA, 0x55, 0, + 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, + 0x55, 0x55, 0xFF, + 0x55, 0xFF, 0x55, + 0x55, 0xFF, 0xFF, + 0xFF, 0x55, 0x55, + 0xFF, 0x55, 0xFF, + 0xFF, 0xFF, 0x55, + 0xFF, 0xFF, 0xFF + }; +#elif PICO8 + const int palette_size = 16; + const int pico8_palette[16 * 3] = { + 0, 0, 0, + 29, 43, 83, + 126, 37, 83, + 0, 135, 81, + 171, 82, 54, + 95, 87, 79, + 194, 195, 199, + 255, 241, 232, + 255, 0, 77, + 255, 163, 0, + 255, 236, 39, + 0, 228, 54, + 41, 173, 255, + 131, 118, 156, + 255, 119, 168, + 255, 204, 170 + }; +#endif + if (!SDL_LockTexture(render_target, NULL, &dst, &dst_pitch)) + { + unsigned char* dst8 = (unsigned char*)dst; + for (int y = 0; y < HEIGHT; ++y) + { + for (int x = 0; x < WIDTH; ++x) + { + int dstk = y * dst_pitch + x * 4; + int srck = y * src_pitch + x * 3; + + int r = src[srck + 0]; + int g = src[srck + 1]; + int b = src[srck + 2]; + + int best = 0; + int best_score = (r * r + g * g + b * b) * 2; + for (int p = 0; p < palette_size * 3; p += 3) + { + const int* pico8_color = pico8_palette + p; + if (pico8_color[0] == r && + pico8_color[1] == g && + pico8_color[2] == b) + { + // Perfect match + best = p; + break; + } + int ri = pico8_color[0] - r; + int gi = pico8_color[1] - g; + int bi = pico8_color[2] - b; + int score = ri * ri + gi * gi + bi * bi; + if (score < best_score) + { + best_score = score; + best = p; + } + } + + dst8[dstk + 0] = pico8_palette[best + 0]; + dst8[dstk + 1] = pico8_palette[best + 1]; + dst8[dstk + 2] = pico8_palette[best + 2]; + dst8[dstk + 3] = 255; + } + } + SDL_UnlockTexture(render_target); + } +#else + void* dst; + const unsigned char* src = doom_get_framebuffer(4); + int src_pitch = WIDTH * 4; + int dst_pitch; + if (!SDL_LockTexture(render_target, NULL, &dst, &dst_pitch)) + { + for (int y = 0; y < HEIGHT; ++y) + { + memcpy(dst, src, src_pitch); + dst = (unsigned char*)dst + dst_pitch; + src += src_pitch; + } + SDL_UnlockTexture(render_target); + } +#endif + + // Stretch our texture on the screen + SDL_Rect src_rect = {0, 0, WIDTH, HEIGHT }; + SDL_Rect dst_rect = {0, 0, FULL_WIDTH, FULL_HEIGHT}; + SDL_RenderCopy(renderer, render_target, &src_rect, &dst_rect); + + // Swap + SDL_RenderPresent(renderer); + } + + // Shutdown + if (midi_timer) SDL_RemoveTimer(midi_timer); +#if defined(WIN32) + if (midi_out_handle) midiOutClose(midi_out_handle); +#endif + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_CloseAudio(); + SDL_DestroyTexture(render_target); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} diff --git a/images/PureDOOM.png b/images/PureDOOM.png new file mode 100644 index 0000000..0287df5 Binary files /dev/null and b/images/PureDOOM.png differ diff --git a/src/DOOM/DOOM.c b/src/DOOM/DOOM.c new file mode 100644 index 0000000..ee68ef4 --- /dev/null +++ b/src/DOOM/DOOM.c @@ -0,0 +1,739 @@ +#if defined(WIN32) +#define _CRT_SECURE_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#include "DOOM.h" + +#include "d_main.h" +#include "doomdef.h" +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_misc.h" + + +extern byte* screens[5]; +extern unsigned char screen_palette[256 * 3]; +extern doom_boolean is_wiping_screen; +extern default_t defaults[]; +extern int numdefaults; +extern signed short mixbuffer[2048]; + + +static unsigned char* screen_buffer = 0; +static unsigned char* final_screen_buffer = 0; +static int last_update_time = 0; +static int button_states[3] = { 0 }; +static char itoa_buf[20]; + + +char error_buf[260]; +int doom_flags = 0; +doom_print_fn doom_print = 0; +doom_malloc_fn doom_malloc = 0; +doom_free_fn doom_free = 0; +doom_open_fn doom_open = 0; +doom_close_fn doom_close = 0; +doom_read_fn doom_read = 0; +doom_write_fn doom_write = 0; +doom_seek_fn doom_seek = 0; +doom_tell_fn doom_tell = 0; +doom_eof_fn doom_eof = 0; +doom_gettime_fn doom_gettime = 0; +doom_exit_fn doom_exit = 0; +doom_getenv_fn doom_getenv = 0; + + +void D_DoomLoop(void); +void D_UpdateWipe(void); +void I_UpdateSound(); +unsigned long I_TickSong(); + + +#if defined(DOOM_IMPLEMENT_PRINT) +#include +static void doom_print_impl(const char* str) +{ + printf("%s", str); +} +#else +static void doom_print_impl(const char* str) {} +#endif + +#if defined(DOOM_IMPLEMENT_MALLOC) +#include +static void* doom_malloc_impl(int size) +{ + return malloc((size_t)size); +} +static void doom_free_impl(void* ptr) +{ + free(ptr); +} +#else +static void* doom_malloc_impl(int size) { return 0; } +static void doom_free_impl(void* ptr) {} +#endif + + +#if defined(DOOM_IMPLEMENT_FILE_IO) +#include +void* doom_open_impl(const char* filename, const char* mode) +{ + return fopen(filename, mode); +} +void doom_close_impl(void* handle) +{ + fclose(handle); +} +int doom_read_impl(void* handle, void *buf, int count) +{ + return (int)fread(buf, 1, count, handle); +} +int doom_write_impl(void* handle, const void *buf, int count) +{ + return (int)fwrite(buf, 1, count, handle); +} +int doom_seek_impl(void* handle, int offset, doom_seek_t origin) +{ + return fseek(handle, offset, origin); +} +int doom_tell_impl(void* handle) +{ + return (int)ftell(handle); +} +int doom_eof_impl(void* handle) +{ + return feof(handle); +} +#else +void* doom_open_impl(const char* filename, const char* mode) +{ + return 0; +} +void doom_close_impl(void* handle) {} +int doom_read_impl(void* handle, void *buf, int count) +{ + return -1; +} +int doom_write_impl(void* handle, const void *buf, int count) +{ + return -1; +} +int doom_seek_impl(void* handle, int offset, doom_seek_t origin) +{ + return -1; +} +int doom_tell_impl(void* handle) +{ + return -1; +} +int doom_eof_impl(void* handle) +{ + return 1; +} +#endif + + +#if defined(DOOM_IMPLEMENT_GETTIME) +#if defined(WIN32) +#include +#else +#include +#endif +void doom_gettime_impl(int* sec, int* usec) +{ +#if defined(WIN32) + static const unsigned long long EPOCH = ((unsigned long long)116444736000000000ULL); + SYSTEMTIME system_time; + FILETIME file_time; + unsigned long long time; + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((unsigned long long)file_time.dwLowDateTime); + time += ((unsigned long long)file_time.dwHighDateTime) << 32; + *sec = (int)((time - EPOCH) / 10000000L); + *usec = (int)(system_time.wMilliseconds * 1000); +#else + struct timeval tp; + struct timezone tzp; + gettimeofday(&tp, &tzp); + *sec = tp.tv_sec; + *usec = tp.tv_usec; +#endif +} +#else +void doom_gettime_impl(int* sec, int* usec) +{ + *sec = 0; + *usec = 0; +} +#endif + + +#if defined(DOOM_IMPLEMENT_EXIT) +#include +void doom_exit_impl(int code) +{ + exit(code); +} +#else +void doom_exit_impl(int code) {} +#endif + + +#if defined(DOOM_IMPLEMENT_GETENV) +#include +char* doom_getenv_impl(const char* var) +{ + return getenv(var); +} +#else +char* doom_getenv_impl(const char* var) { return 0; } +#endif + + +void doom_memset(void* ptr, int value, int num) +{ + unsigned char* p = ptr; + for (int i = 0; i < num; ++i, ++p) + { + *p = (unsigned char)value; + } +} + + +void* doom_memcpy(void* destination, const void* source, int num) +{ + unsigned char* dst = destination; + const unsigned char* src = source; + + for (int i = 0; i < num; ++i, ++dst, ++src) + { + *dst = *src; + } + + return destination; +} + + +int doom_strlen(const char* str) +{ + int len = 0; + while (*str++) ++len; + return len; +} + + +char* doom_concat(char* dst, const char* src) +{ + char* ret = dst; + dst += doom_strlen(dst); + + while (*src) *dst++ = *src++; + *dst = *src; // \0 + + return ret; +} + + +char* doom_strcpy(char* dst, const char* src) +{ + char* ret = dst; + + while (*src) *dst++ = *src++; + *dst = *src; // \0 + + return ret; +} + + +char* doom_strncpy(char* dst, const char* src, int num) +{ + int i = 0; + + for (; i < num; ++i) + { + if (!src[i]) break; + dst[i] = src[i]; + } + + while (i < num) dst[i++] = '\0'; + + return dst; +} + + +int doom_strcmp(const char* str1, const char* str2) +{ + int ret = 0; + + while (!(ret = *(unsigned char*)str1 - *(unsigned char*) str2) && *str1) + ++str1, ++str2; + + if (ret < 0) + ret = -1; + else if (ret > 0) + ret = 1; + + return (ret); +} + + +int doom_strncmp(const char* str1, const char* str2, int n) +{ + int ret = 0; + int count = 1; + + while (!(ret = *(unsigned char*)str1 - *(unsigned char*) str2) && *str1 && count++ < n) + ++str1, ++str2; + + if (ret < 0) + ret = -1; + else if (ret > 0) + ret = 1; + + return (ret); +} + + +int doom_toupper(int c) +{ + if (c >= 'a' && c <= 'z') return c - 'a' + 'A'; + return c; +} + + +int doom_strcasecmp(const char* str1, const char* str2) +{ + int ret = 0; + + while (!(ret = doom_toupper(*(unsigned char*)str1) - doom_toupper(*(unsigned char*)str2)) && *str1) + ++str1, ++str2; + + if (ret < 0) + ret = -1; + else if (ret > 0) + ret = 1; + + return (ret); +} + + +int doom_strncasecmp(const char* str1, const char* str2, int n) +{ + int ret = 0; + int count = 1; + + while (!(ret = doom_toupper(*(unsigned char*)str1) - doom_toupper(*(unsigned char*)str2)) && *str1 && count++ < n) + ++str1, ++str2; + + if (ret < 0) + ret = -1; + else if (ret > 0) + ret = 1; + + return (ret); +} + + +int doom_atoi(const char* str) +{ + int i = 0; + int c; + + while ((c = *str++) != 0) + { + i *= 10; + i += c - '0'; + } + + return i; +} + + +int doom_atox(const char* str) +{ + int i = 0; + int c; + + while ((c = *str++) != 0) + { + i *= 16; + if (c >= '0' && c <= '9') + i += c - '0'; + else + i += c - 'A' + 10; + } + + return i; +} + + +const char* doom_itoa(int k, int radix) +{ + int i = k < 0 ? -k : k; + if (i == 0) + { + itoa_buf[0] = '0'; + itoa_buf[1] = '\0'; + return itoa_buf; + } + + int idx = k < 0 ? 1 : 0; + int j = i; + while (j) + { + j /= radix; + idx++; + } + itoa_buf[idx] = '\0'; + + if (radix == 10) + { + while (i) + { + itoa_buf[--idx] = '0' + (i % 10); + i /= 10; + } + } + else + { + while (i) + { + int k = (i & 0xF); + if (k >= 10) + itoa_buf[--idx] = 'A' + ((i & 0xF) - 10); + else + itoa_buf[--idx] = '0' + (i & 0xF); + i >>= 4; + } + } + + if (k < 0) itoa_buf[0] = '-'; + + return itoa_buf; +} + + +const char* doom_ctoa(char c) +{ + itoa_buf[0] = c; + itoa_buf[1] = '\0'; + return itoa_buf; +} + + +const char* doom_ptoa(void* p) +{ + int idx = 0; + unsigned long long i = (unsigned long long)p; + + itoa_buf[idx++] = '0'; + itoa_buf[idx++] = 'x'; + + while (i) + { + int k = (i & 0xF); + if (k >= 10) + itoa_buf[idx++] = 'A' + ((i & 0xF) - 10); + else + itoa_buf[idx++] = '0' + (i & 0xF); + i >>= 4; + } + + itoa_buf[idx] = '\0'; + return itoa_buf; +} + + +int doom_fprint(void* handle, const char* str) +{ + return doom_write(handle, str, doom_strlen(str)); +} + + +static default_t* get_default(const char* name) +{ + for (int i = 0; i < numdefaults; ++i) + { + if (doom_strcmp(defaults[i].name, name) == 0) return &defaults[i]; + } + return 0; +} + + +void doom_set_resolution(int width, int height) +{ + if (width <= 0 || height <= 0) return; + // SCREENWIDTH = width; + // SCREENHEIGHT = height; +} + + +void doom_set_default_int(const char* name, int value) +{ + default_t* def = get_default(name); + if (!def) return; + def->defaultvalue = value; +} + + +void doom_set_default_string(const char* name, const char* value) +{ + default_t* def = get_default(name); + if (!def) return; + def->default_text_value = (char*)value; +} + + +void doom_set_print(doom_print_fn print_fn) +{ + doom_print = print_fn; +} + + +void doom_set_malloc(doom_malloc_fn malloc_fn, doom_free_fn free_fn) +{ + doom_malloc = malloc_fn; + doom_free = free_fn; +} + + +void doom_set_file_io(doom_open_fn open_fn, + doom_close_fn close_fn, + doom_read_fn read_fn, + doom_write_fn write_fn, + doom_seek_fn seek_fn, + doom_tell_fn tell_fn, + doom_eof_fn eof_fn) +{ + doom_open = open_fn; + doom_close = close_fn; + doom_read = read_fn; + doom_write = write_fn; + doom_seek = seek_fn; + doom_tell = tell_fn; + doom_eof = eof_fn; +} + + +void doom_set_gettime(doom_gettime_fn gettime_fn) +{ + doom_gettime = gettime_fn; +} + + +void doom_set_exit(doom_exit_fn exit_fn) +{ + doom_exit = exit_fn; +} + + +void doom_set_getenv(doom_getenv_fn getenv_fn) +{ + doom_getenv = getenv_fn; +} + + +void doom_init(int argc, char** argv, int flags) +{ + if (!doom_print) doom_print = doom_print_impl; + if (!doom_malloc) doom_malloc = doom_malloc_impl; + if (!doom_free) doom_free = doom_free_impl; + if (!doom_open) doom_open = doom_open_impl; + if (!doom_close) doom_close = doom_close_impl; + if (!doom_read) doom_read = doom_read_impl; + if (!doom_write) doom_write = doom_write_impl; + if (!doom_seek) doom_seek = doom_seek_impl; + if (!doom_tell) doom_tell = doom_tell_impl; + if (!doom_eof) doom_eof = doom_eof_impl; + if (!doom_gettime) doom_gettime = doom_gettime_impl; + if (!doom_exit) doom_exit = doom_exit_impl; + if (!doom_getenv) doom_getenv = doom_getenv_impl; + + screen_buffer = doom_malloc(SCREENWIDTH * SCREENHEIGHT); + final_screen_buffer = doom_malloc(SCREENWIDTH * SCREENHEIGHT * 4); + last_update_time = I_GetTime(); + + myargc = argc; + myargv = argv; + doom_flags = flags; + + D_DoomMain(); +} + + +void doom_update() +{ + int now = I_GetTime(); + int delta_time = now - last_update_time; + + while (delta_time-- > 0) + { + if (is_wiping_screen) + D_UpdateWipe(); + else + D_DoomLoop(); + } + + last_update_time = now; +} + + +const unsigned char* doom_get_framebuffer(int channels) +{ + int i, len; + + doom_memcpy(screen_buffer, screens[0], SCREENWIDTH * SCREENHEIGHT); + + extern doom_boolean menuactive; + extern gamestate_t gamestate; + extern doom_boolean automapactive; + extern int crosshair; + + // Draw crosshair + if (crosshair && + !menuactive && + gamestate == GS_LEVEL && + !automapactive) + { + int y; + extern int setblocks; + if (setblocks == 11) y = SCREENHEIGHT / 2 + 8; + else y = SCREENHEIGHT / 2 - 8; + for (i = 0; i < 2; ++i) + { + screen_buffer[SCREENWIDTH / 2 - 2 - i + y * SCREENWIDTH] = 4; + screen_buffer[SCREENWIDTH / 2 + 2 + i + y * SCREENWIDTH] = 4; + } + for (i = 0; i < 2; ++i) + { + screen_buffer[SCREENWIDTH / 2 + (y - 2 - i) * SCREENWIDTH] = 4; + screen_buffer[SCREENWIDTH / 2 + (y + 2 + i) * SCREENWIDTH] = 4; + } + } + + if (channels == 1) + { + return screen_buffer; + } + else if (channels == 3) + { + for (i = 0, len = SCREENWIDTH * SCREENHEIGHT; i < len; ++i) + { + int k = i * 3; + int kpal = screen_buffer[i] * 3; + final_screen_buffer[k + 0] = screen_palette[kpal + 0]; + final_screen_buffer[k + 1] = screen_palette[kpal + 1]; + final_screen_buffer[k + 2] = screen_palette[kpal + 2]; + } + return final_screen_buffer; + } + else if (channels == 4) + { + for (i = 0, len = SCREENWIDTH * SCREENHEIGHT; i < len; ++i) + { + int k = i * 4; + int kpal = screen_buffer[i] * 3; + final_screen_buffer[k + 0] = screen_palette[kpal + 0]; + final_screen_buffer[k + 1] = screen_palette[kpal + 1]; + final_screen_buffer[k + 2] = screen_palette[kpal + 2]; + final_screen_buffer[k + 3] = 255; + } + return final_screen_buffer; + } + else + { + return 0; + } +} + + +unsigned long doom_tick_midi() +{ + return I_TickSong(); +} + + +short* doom_get_sound_buffer() +{ + I_UpdateSound(); + return mixbuffer; +} + + +void doom_key_down(doom_key_t key) +{ + event_t event; + event.type = ev_keydown; + event.data1 = (int)key; + D_PostEvent(&event); +} + + +void doom_key_up(doom_key_t key) +{ + event_t event; + event.type = ev_keyup; + event.data1 = (int)key; + D_PostEvent(&event); +} + + +void doom_button_down(doom_button_t button) +{ + button_states[button] = 1; + + event_t event; + event.type = ev_mouse; + event.data1 = + (button_states[0]) | + (button_states[1] ? 2 : 0) | + (button_states[2] ? 4 : 0); + event.data2 = event.data3 = 0; + D_PostEvent(&event); +} + + +void doom_button_up(doom_button_t button) +{ + button_states[button] = 0; + + event_t event; + event.type = ev_mouse; + event.data1 = + (button_states[0]) | + (button_states[1] ? 2 : 0) | + (button_states[2] ? 4 : 0); + + event.data1 = + event.data1 + ^ (button_states[0] ? 1 : 0) + ^ (button_states[1] ? 2 : 0) + ^ (button_states[2] ? 4 : 0); + + event.data2 = event.data3 = 0; + D_PostEvent(&event); +} + + +void doom_mouse_move(int delta_x, int delta_y) +{ + event_t event; + + event.type = ev_mouse; + event.data1 = + (button_states[0]) | + (button_states[1] ? 2 : 0) | + (button_states[2] ? 4 : 0); + event.data2 = delta_x; + event.data3 = -delta_y; + + if (event.data2 || event.data3) + { + D_PostEvent(&event); + } +} diff --git a/src/DOOM/DOOM.h b/src/DOOM/DOOM.h new file mode 100644 index 0000000..a44aa00 --- /dev/null +++ b/src/DOOM/DOOM.h @@ -0,0 +1,216 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +//----------------------------------------------------------------------------- +/* Pure DOOM usage + + Do this: + #define DOOM_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define DOOM_IMPLEMENTATION + #include "PureDOOM.h" + +*/ +//----------------------------------------------------------------------------- + + +#ifndef __DOOM_H__ +#define __DOOM_H__ + + +// Sample rate of sound samples from doom +#define DOOM_SAMPLERATE 11025 + +// MIDI tick needs to be called 140 times per seconds +#define DOOM_MIDI_RATE 140 + +// Hide menu options. If for say your platform doesn't support mouse or +// MIDI playback, you can hide these settings from the menu. +#define DOOM_FLAG_HIDE_MOUSE_OPTIONS 1 // Remove mouse options from menu +#define DOOM_FLAG_HIDE_SOUND_OPTIONS 2 // Remove sound options from menu +#define DOOM_FLAG_HIDE_MUSIC_OPTIONS 4 // Remove music options from menu + +// Darken background when menu is open, making it more readable. This +// uses a bit more CPU and redraws the HUD every frame +#define DOOM_FLAG_MENU_DARKEN_BG 8 + + +#if __cplusplus +extern "C" { +#endif + +typedef enum +{ + DOOM_SEEK_CUR = 1, + DOOM_SEEK_END = 2, + DOOM_SEEK_SET = 0 +} doom_seek_t; + + +typedef void(*doom_print_fn)(const char* str); +typedef void*(*doom_malloc_fn)(int size); +typedef void(*doom_free_fn)(void* ptr); +typedef void*(*doom_open_fn)(const char* filename, const char* mode); +typedef void(*doom_close_fn)(void* handle); +typedef int(*doom_read_fn)(void* handle, void *buf, int count); +typedef int(*doom_write_fn)(void* handle, const void *buf, int count); +typedef int(*doom_seek_fn)(void* handle, int offset, doom_seek_t origin); +typedef int(*doom_tell_fn)(void* handle); +typedef int(*doom_eof_fn)(void* handle); +typedef void(*doom_gettime_fn)(int* sec, int* usec); +typedef void(*doom_exit_fn)(int code); +typedef char*(*doom_getenv_fn)(const char* var); + + +// Doom key mapping +typedef enum +{ + DOOM_KEY_UNKNOWN = -1, + DOOM_KEY_TAB = 9, + DOOM_KEY_ENTER = 13, + DOOM_KEY_ESCAPE = 27, + DOOM_KEY_SPACE = 32, + DOOM_KEY_APOSTROPHE = '\'', + DOOM_KEY_MULTIPLY = '*', + DOOM_KEY_COMMA = ',', + DOOM_KEY_MINUS = 0x2d, + DOOM_KEY_PERIOD = '.', + DOOM_KEY_SLASH = '/', + DOOM_KEY_0 = '0', + DOOM_KEY_1 = '1', + DOOM_KEY_2 = '2', + DOOM_KEY_3 = '3', + DOOM_KEY_4 = '4', + DOOM_KEY_5 = '5', + DOOM_KEY_6 = '6', + DOOM_KEY_7 = '7', + DOOM_KEY_8 = '8', + DOOM_KEY_9 = '9', + DOOM_KEY_SEMICOLON = ';', + DOOM_KEY_EQUALS = 0x3d, + DOOM_KEY_LEFT_BRACKET = '[', + DOOM_KEY_RIGHT_BRACKET = ']', + DOOM_KEY_A = 'a', + DOOM_KEY_B = 'b', + DOOM_KEY_C = 'c', + DOOM_KEY_D = 'd', + DOOM_KEY_E = 'e', + DOOM_KEY_F = 'f', + DOOM_KEY_G = 'g', + DOOM_KEY_H = 'h', + DOOM_KEY_I = 'i', + DOOM_KEY_J = 'j', + DOOM_KEY_K = 'k', + DOOM_KEY_L = 'l', + DOOM_KEY_M = 'm', + DOOM_KEY_N = 'n', + DOOM_KEY_O = 'o', + DOOM_KEY_P = 'p', + DOOM_KEY_Q = 'q', + DOOM_KEY_R = 'r', + DOOM_KEY_S = 's', + DOOM_KEY_T = 't', + DOOM_KEY_U = 'u', + DOOM_KEY_V = 'v', + DOOM_KEY_W = 'w', + DOOM_KEY_X = 'x', + DOOM_KEY_Y = 'y', + DOOM_KEY_Z = 'z', + DOOM_KEY_BACKSPACE = 127, + DOOM_KEY_CTRL = (0x80 + 0x1d), // Both left and right + DOOM_KEY_LEFT_ARROW = 0xac, + DOOM_KEY_UP_ARROW = 0xad, + DOOM_KEY_RIGHT_ARROW = 0xae, + DOOM_KEY_DOWN_ARROW = 0xaf, + DOOM_KEY_SHIFT = (0x80 + 0x36), // Both left and right + DOOM_KEY_ALT = (0x80 + 0x38), // Both left and right + DOOM_KEY_F1 = (0x80 + 0x3b), + DOOM_KEY_F2 = (0x80 + 0x3c), + DOOM_KEY_F3 = (0x80 + 0x3d), + DOOM_KEY_F4 = (0x80 + 0x3e), + DOOM_KEY_F5 = (0x80 + 0x3f), + DOOM_KEY_F6 = (0x80 + 0x40), + DOOM_KEY_F7 = (0x80 + 0x41), + DOOM_KEY_F8 = (0x80 + 0x42), + DOOM_KEY_F9 = (0x80 + 0x43), + DOOM_KEY_F10 = (0x80 + 0x44), + DOOM_KEY_F11 = (0x80 + 0x57), + DOOM_KEY_F12 = (0x80 + 0x58), + DOOM_KEY_PAUSE = 0xff +} doom_key_t; + + +// Mouse button mapping +typedef enum +{ + DOOM_LEFT_BUTTON = 0, + DOOM_RIGHT_BUTTON = 1, + DOOM_MIDDLE_BUTTON = 2 +} doom_button_t; + + +// For the software renderer. Default is 320x200 +void doom_set_resolution(int width, int height); + +// Set default configurations. Lets say, changing arrows to WASD as default controls +void doom_set_default_int(const char* name, int value); +void doom_set_default_string(const char* name, const char* value); + +// set callbacks +void doom_set_print(doom_print_fn print_fn); +void doom_set_malloc(doom_malloc_fn malloc_fn, doom_free_fn free_fn); +void doom_set_file_io(doom_open_fn open_fn, + doom_close_fn close_fn, + doom_read_fn read_fn, + doom_write_fn write_fn, + doom_seek_fn seek_fn, + doom_tell_fn tell_fn, + doom_eof_fn eof_fn); +void doom_set_gettime(doom_gettime_fn gettime_fn); +void doom_set_exit(doom_exit_fn exit_fn); +void doom_set_getenv(doom_getenv_fn getenv_fn); + +// Initializes DOOM and start things up. Call only call one +void doom_init(int argc, char** argv, int flags); + +// Call this every frame +void doom_update(); + +// Channels: 1 = indexed, 3 = RGB, 4 = RGBA +const unsigned char* doom_get_framebuffer(int channels); + +// It is always 2048 bytes in size +short* doom_get_sound_buffer(); + +// Call this 140 times per second. Or about every 7ms. +// Returns midi message. Keep calling it until it returns 0. +unsigned long doom_tick_midi(); + +// Events +void doom_key_down(doom_key_t key); +void doom_key_up(doom_key_t key); +void doom_button_down(doom_button_t button); +void doom_button_up(doom_button_t button); +void doom_mouse_move(int delta_x, int delta_y); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/src/DOOM/DOOMLIC.TXT b/src/DOOM/DOOMLIC.TXT new file mode 100644 index 0000000..2b2252e --- /dev/null +++ b/src/DOOM/DOOMLIC.TXT @@ -0,0 +1,112 @@ + + + LIMITED USE SOFTWARE LICENSE AGREEMENT + + This Limited Use Software License Agreement (the "Agreement") +is a legal agreement between you, the end-user, and Id Software, Inc. +("ID"). By downloading or purchasing the software material, which +includes source code (the "Source Code"), artwork data, music and +software tools (collectively, the "Software"), you are agreeing to +be bound by the terms of this Agreement. If you do not agree to the +terms of this Agreement, promptly destroy the Software you may have +downloaded or copied. + +ID SOFTWARE LICENSE + +1. Grant of License. ID grants to you the right to use the +Software. You have no ownership or proprietary rights in or to the +Software, or the Trademark. For purposes of this section, "use" means +loading the Software into RAM, as well as installation on a hard disk +or other storage device. The Software, together with any archive copy +thereof, shall be destroyed when no longer used in accordance with +this Agreement, or when the right to use the Software is terminated. +You agree that the Software will not be shipped, transferred or +exported into any country in violation of the U.S. Export +Administration Act (or any other law governing such matters) and that +you will not utilize, in any other manner, the Software in violation +of any applicable law. + +2. Permitted Uses. For educational purposes only, you, the +end-user, may use portions of the Source Code, such as particular +routines, to develop your own software, but may not duplicate the +Source Code, except as noted in paragraph 4. The limited right +referenced in the preceding sentence is hereinafter referred to as +"Educational Use." By so exercising the Educational Use right you +shall not obtain any ownership, copyright, proprietary or other +interest in or to the Source Code, or any portion of the Source +Code. You may dispose of your own software in your sole discretion. +With the exception of the Educational Use right, you may not +otherwise use the Software, or an portion of the Software, which +includes the Source Code, for commercial gain. + +3. Prohibited Uses: Under no circumstances shall you, the +end-user, be permitted, allowed or authorized to commercially exploit +the Software. Neither you nor anyone at your direction shall do any +of the following acts with regard to the Software, or any portion +thereof: + + Rent; + + Sell; + + Lease; + + Offer on a pay-per-play basis; + + Distribute for money or any other consideration; or + + In any other manner and through any medium whatsoever +commercially exploit or use for any commercial purpose. + +Notwithstanding the foregoing prohibitions, you may commercially +exploit the software you develop by exercising the Educational Use +right, referenced in paragraph 2. hereinabove. + +4. Copyright. The Software and all copyrights related thereto +(including all characters and other images generated by the Software +or depicted in the Software) are owned by ID and is protected by +United States copyright laws and international treaty provisions. +Id shall retain exclusive ownership and copyright in and to the +Software and all portions of the Software and you shall have no +ownership or other proprietary interest in such materials. You must +treat the Software like any other copyrighted material. You may not +otherwise reproduce, copy or disclose to others, in whole or in any +part, the Software. You may not copy the written materials +accompanying the Software. You agree to use your best efforts to +see that any user of the Software licensed hereunder complies with +this Agreement. + +5. NO WARRANTIES. ID DISCLAIMS ALL WARRANTIES, BOTH EXPRESS +IMPLIED, INCLUDING BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE WITH RESPECT +TO THE SOFTWARE. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL +RIGHTS. YOU MAY HAVE OTHER RIGHTS WHICH VARY FROM JURISDICTION TO +JURISDICTION. ID DOES NOT WARRANT THAT THE OPERATION OF THE SOFTWARE +WILL BE UNINTERRUPTED, ERROR FREE OR MEET YOUR SPECIFIC REQUIREMENTS. +THE WARRANTY SET FORTH ABOVE IS IN LIEU OF ALL OTHER EXPRESS +WARRANTIES WHETHER ORAL OR WRITTEN. THE AGENTS, EMPLOYEES, +DISTRIBUTORS, AND DEALERS OF ID ARE NOT AUTHORIZED TO MAKE +MODIFICATIONS TO THIS WARRANTY, OR ADDITIONAL WARRANTIES ON BEHALF +OF ID. + + Exclusive Remedies. The Software is being offered to you +free of any charge. You agree that you have no remedy against ID, its +affiliates, contractors, suppliers, and agents for loss or damage +caused by any defect or failure in the Software regardless of the form +of action, whether in contract, tort, includinegligence, strict +liability or otherwise, with regard to the Software. This Agreement +shall be construed in accordance with and governed by the laws of the +State of Texas. Copyright and other proprietary matters will be +governed by United States laws and international treaties. IN ANY +CASE, ID SHALL NOT BE LIABLE FOR LOSS OF DATA, LOSS OF PROFITS, LOST +SAVINGS, SPECIAL, INCIDENTAL, CONSEQUENTIAL, INDIRECT OR OTHER +SIMILAR DAMAGES ARISING FROM BREACH OF WARRANTY, BREACH OF CONTRACT, +NEGLIGENCE, OR OTHER LEGAL THEORY EVEN IF ID OR ITS AGENT HAS BEEN +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY +OTHER PARTY. Some jurisdictions do not allow the exclusion or +limitation of incidental or consequential damages, so the above +limitation or exclusion may not apply to you. + + + + diff --git a/src/DOOM/am_map.c b/src/DOOM/am_map.c new file mode 100644 index 0000000..b92500f --- /dev/null +++ b/src/DOOM/am_map.c @@ -0,0 +1,1319 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// +// $Log:$ +// +// DESCRIPTION: the automap code +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "z_zone.h" +#include "doomdef.h" +#include "st_stuff.h" +#include "p_local.h" +#include "w_wad.h" +#include "m_cheat.h" +#include "i_system.h" +#include "v_video.h" // Needs access to LFB. +#include "doomstat.h" // State. +#include "r_state.h" // State. +#include "dstrings.h" // Data. +#include "am_map.h" + + +// For use if I do walls with outsides/insides +#define REDS (256-5*16) +#define REDRANGE 16 +#define BLUES (256-4*16+8) +#define BLUERANGE 8 +#define GREENS (7*16) +#define GREENRANGE 16 +#define GRAYS (6*16) +#define GRAYSRANGE 16 +#define BROWNS (4*16) +#define BROWNRANGE 16 +#define YELLOWS (256-32+7) +#define YELLOWRANGE 1 +#define BLACK 0 +#define WHITE (256-47) + +// Automap colors +#define BACKGROUND BLACK +#define YOURCOLORS WHITE +#define YOURRANGE 0 +#define WALLCOLORS REDS +#define WALLRANGE REDRANGE +#define TSWALLCOLORS GRAYS +#define TSWALLRANGE GRAYSRANGE +#define FDWALLCOLORS BROWNS +#define FDWALLRANGE BROWNRANGE +#define CDWALLCOLORS YELLOWS +#define CDWALLRANGE YELLOWRANGE +#define THINGCOLORS GREENS +#define THINGRANGE GREENRANGE +#define SECRETWALLCOLORS WALLCOLORS +#define SECRETWALLRANGE WALLRANGE +#define GRIDCOLORS (GRAYS + GRAYSRANGE/2) +#define GRIDRANGE 0 +#define XHAIRCOLORS GRAYS + +// drawing stuff +#define FB 0 + +#define AM_PANDOWNKEY KEY_DOWNARROW +#define AM_PANUPKEY KEY_UPARROW +#define AM_PANRIGHTKEY KEY_RIGHTARROW +#define AM_PANLEFTKEY KEY_LEFTARROW +#define AM_ZOOMINKEY '=' +#define AM_ZOOMOUTKEY '-' +#define AM_STARTKEY KEY_TAB +#define AM_ENDKEY KEY_TAB +#define AM_GOBIGKEY '0' +#define AM_FOLLOWKEY 'f' +#define AM_GRIDKEY 'g' +#define AM_MARKKEY 'm' +#define AM_CLEARMARKKEY 'c' + +#define AM_NUMMARKPOINTS 10 + +// scale on entry +#define INITSCALEMTOF (.2*FRACUNIT) +// how much the automap moves window per tic in frame-buffer coordinates +// moves 140 pixels in 1 second +#define F_PANINC 4 +// how much zoom-in per tic +// goes to 2x in 1 second +#define M_ZOOMIN ((int) (1.02*FRACUNIT)) +// how much zoom-out per tic +// pulls out to 0.5x in 1 second +#define M_ZOOMOUT ((int) (FRACUNIT/1.02)) + +// translates between frame-buffer and map distances +#define FTOM(x) FixedMul(((x)<<16),scale_ftom) +#define MTOF(x) (FixedMul((x),scale_mtof)>>16) +// translates between frame-buffer and map coordinates +#define CXMTOF(x) (f_x + MTOF((x)-m_x)) +#define CYMTOF(y) (f_y + (f_h - MTOF((y)-m_y))) + +// the following is crap +#define LINE_NEVERSEE ML_DONTDRAW + + +typedef struct +{ + int x, y; +} fpoint_t; + + +typedef struct +{ + fpoint_t a, b; +} fline_t; + + +typedef struct +{ + fixed_t x, y; +} mpoint_t; + + +typedef struct +{ + mpoint_t a, b; +} mline_t; + + +typedef struct +{ + fixed_t slp, islp; +} islope_t; + + +// +// The vector graphics for the automap. +// A line drawing of the player pointing right, +// starting from the middle. +// +#define R ((8*PLAYERRADIUS)/7) +mline_t player_arrow[] = { + { { -R + R / 8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R - R / 2, R / 4 } }, // -----> + { { R, 0 }, { R - R / 2, -R / 4 } }, + { { -R + R / 8, 0 }, { -R - R / 8, R / 4 } }, // >----> + { { -R + R / 8, 0 }, { -R - R / 8, -R / 4 } }, + { { -R + 3 * R / 8, 0 }, { -R + R / 8, R / 4 } }, // >>---> + { { -R + 3 * R / 8, 0 }, { -R + R / 8, -R / 4 } } +}; +#undef R +#define NUMPLYRLINES (sizeof(player_arrow)/sizeof(mline_t)) + +#define R ((8*PLAYERRADIUS)/7) +mline_t cheat_player_arrow[] = { + { { -R + R / 8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R - R / 2, R / 6 } }, // -----> + { { R, 0 }, { R - R / 2, -R / 6 } }, + { { -R + R / 8, 0 }, { -R - R / 8, R / 6 } }, // >-----> + { { -R + R / 8, 0 }, { -R - R / 8, -R / 6 } }, + { { -R + 3 * R / 8, 0 }, { -R + R / 8, R / 6 } }, // >>-----> + { { -R + 3 * R / 8, 0 }, { -R + R / 8, -R / 6 } }, + { { -R / 2, 0 }, { -R / 2, -R / 6 } }, // >>-d---> + { { -R / 2, -R / 6 }, { -R / 2 + R / 6, -R / 6 } }, + { { -R / 2 + R / 6, -R / 6 }, { -R / 2 + R / 6, R / 4 } }, + { { -R / 6, 0 }, { -R / 6, -R / 6 } }, // >>-dd--> + { { -R / 6, -R / 6 }, { 0, -R / 6 } }, + { { 0, -R / 6 }, { 0, R / 4 } }, + { { R / 6, R / 4 }, { R / 6, -R / 7 } }, // >>-ddt-> + { { R / 6, -R / 7 }, { R / 6 + R / 32, -R / 7 - R / 32 } }, + { { R / 6 + R / 32, -R / 7 - R / 32 }, { R / 6 + R / 10, -R / 7 } } +}; +#undef R +#define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t)) + +#define R (FRACUNIT) +mline_t triangle_guy[] = { + { { (fixed_t)(-.867 * R), (fixed_t)(-.5 * R) }, { (fixed_t)(.867 * R), (fixed_t)(-.5 * R) } }, + { { (fixed_t)(.867 * R), (fixed_t)(-.5 * R) } , { 0, R } }, + { { 0, R }, { (fixed_t)(-.867 * R), (fixed_t)(-.5 * R) } } +}; +#undef R +#define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t)) + +#define R (FRACUNIT) +mline_t thintriangle_guy[] = { + { { (fixed_t)(-.5 * R), (fixed_t)(-.7 * R) }, { R, 0 } }, + { { R, 0 }, { (fixed_t)(-.5 * R), (fixed_t)(.7 * R) } }, + { { (fixed_t)(-.5 * R), (fixed_t)(.7 * R) }, { (fixed_t)(-.5 * R), (fixed_t)(-.7 * R) } } +}; +#undef R +#define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t)) + + +static int cheating = 0; +static int grid = 0; + +static int leveljuststarted = 1; // kluge until AM_LevelInit() is called + +static int finit_width = SCREENWIDTH; +static int finit_height = SCREENHEIGHT - 32; + +// location of window on screen +static int f_x; +static int f_y; + +// size of window on screen +static int f_w; +static int f_h; + +static int lightlev; // used for funky strobing effect +static byte* fb; // pseudo-frame buffer +static int amclock; + +static mpoint_t m_paninc; // how far the window pans each tic (map coords) +static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords) +static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords) + +static fixed_t m_x, m_y; // LL x,y where the window is on the map (map coords) +static fixed_t m_x2, m_y2; // UR x,y where the window is on the map (map coords) + +// +// width/height of window on map (map coords) +// +static fixed_t m_w; +static fixed_t m_h; + +// based on level size +static fixed_t min_x; +static fixed_t min_y; +static fixed_t max_x; +static fixed_t max_y; + +static fixed_t max_w; // max_x-min_x, +static fixed_t max_h; // max_y-min_y + +// based on player size +static fixed_t min_w; +static fixed_t min_h; + +static fixed_t min_scale_mtof; // used to tell when to stop zooming out +static fixed_t max_scale_mtof; // used to tell when to stop zooming in + +// old stuff for recovery later +static fixed_t old_m_w, old_m_h; +static fixed_t old_m_x, old_m_y; + +// old location used by the Follower routine +static mpoint_t f_oldloc; + +// used by MTOF to scale from map-to-frame-buffer coords +static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF; +// used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) +static fixed_t scale_ftom; + +static player_t* plr; // the player represented by an arrow + +static patch_t* marknums[10]; // numbers used for marking by the automap +static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are +static int markpointnum = 0; // next point to be assigned + +static int followplayer = 1; // specifies whether to follow the player around + +static unsigned char cheat_amap_seq[] = { 0xb2, 0x26, 0x26, 0x2e, 0xff }; +static cheatseq_t cheat_amap = { cheat_amap_seq, 0 }; + +static doom_boolean stopped = true; + + +doom_boolean automapactive = false; + + +extern doom_boolean viewactive; + + +void V_MarkRect(int x, int y, int width, int height); + + +// Calculates the slope and slope according to the x-axis of a line +// segment in map coordinates (with the upright y-axis n' all) so +// that it can be used with the brain-dead drawing stuff. +void AM_getIslope(mline_t* ml, islope_t* is) +{ + int dx, dy; + + dy = ml->a.y - ml->b.y; + dx = ml->b.x - ml->a.x; + if (!dy) is->islp = (dx < 0 ? -DOOM_MAXINT : DOOM_MAXINT); + else is->islp = FixedDiv(dx, dy); + if (!dx) is->slp = (dy < 0 ? -DOOM_MAXINT : DOOM_MAXINT); + else is->slp = FixedDiv(dy, dx); +} + + +// +// +// +void AM_activateNewScale(void) +{ + m_x += m_w / 2; + m_y += m_h / 2; + m_w = FTOM(f_w); + m_h = FTOM(f_h); + m_x -= m_w / 2; + m_y -= m_h / 2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + + +// +// +// +void AM_saveScaleAndLoc(void) +{ + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; +} + + +// +// +// +void AM_restoreScaleAndLoc(void) +{ + m_w = old_m_w; + m_h = old_m_h; + if (!followplayer) + { + m_x = old_m_x; + m_y = old_m_y; + } + else + { + m_x = plr->mo->x - m_w / 2; + m_y = plr->mo->y - m_h / 2; + } + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + // Change the scaling multipliers + scale_mtof = FixedDiv(f_w << FRACBITS, m_w); + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); +} + + +// +// adds a marker at the current location +// +void AM_addMark(void) +{ + markpoints[markpointnum].x = m_x + m_w / 2; + markpoints[markpointnum].y = m_y + m_h / 2; + markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS; +} + + +// +// Determines bounding box of all vertices, +// sets global variables controlling zoom range. +// +void AM_findMinMaxBoundaries(void) +{ + int i; + fixed_t a; + fixed_t b; + + min_x = min_y = DOOM_MAXINT; + max_x = max_y = -DOOM_MAXINT; + + for (i = 0; i < numvertexes; i++) + { + if (vertexes[i].x < min_x) + min_x = vertexes[i].x; + else if (vertexes[i].x > max_x) + max_x = vertexes[i].x; + + if (vertexes[i].y < min_y) + min_y = vertexes[i].y; + else if (vertexes[i].y > max_y) + max_y = vertexes[i].y; + } + + max_w = max_x - min_x; + max_h = max_y - min_y; + + min_w = 2 * PLAYERRADIUS; // const? never changed? + min_h = 2 * PLAYERRADIUS; + + a = FixedDiv(f_w << FRACBITS, max_w); + b = FixedDiv(f_h << FRACBITS, max_h); + + min_scale_mtof = a < b ? a : b; + max_scale_mtof = FixedDiv(f_h << FRACBITS, 2 * PLAYERRADIUS); +} + + +// +// +// +void AM_changeWindowLoc(void) +{ + if (m_paninc.x || m_paninc.y) + { + followplayer = 0; + f_oldloc.x = DOOM_MAXINT; + } + + m_x += m_paninc.x; + m_y += m_paninc.y; + + if (m_x + m_w / 2 > max_x) + m_x = max_x - m_w / 2; + else if (m_x + m_w / 2 < min_x) + m_x = min_x - m_w / 2; + + if (m_y + m_h / 2 > max_y) + m_y = max_y - m_h / 2; + else if (m_y + m_h / 2 < min_y) + m_y = min_y - m_h / 2; + + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + + +// +// +// +void AM_initVariables(void) +{ + int pnum; + static event_t st_notify = { ev_keyup, AM_MSGENTERED }; + + automapactive = true; + fb = screens[0]; + + f_oldloc.x = DOOM_MAXINT; + amclock = 0; + lightlev = 0; + + m_paninc.x = m_paninc.y = 0; + ftom_zoommul = FRACUNIT; + mtof_zoommul = FRACUNIT; + + m_w = FTOM(f_w); + m_h = FTOM(f_h); + + // find player to center on initially + if (!playeringame[pnum = consoleplayer]) + for (pnum = 0; pnum < MAXPLAYERS; pnum++) + if (playeringame[pnum]) + break; + + plr = &players[pnum]; + m_x = plr->mo->x - m_w / 2; + m_y = plr->mo->y - m_h / 2; + AM_changeWindowLoc(); + + // for saving & restoring + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; + + // inform the status bar of the change + ST_Responder(&st_notify); +} + + +// +// +// +void AM_loadPics(void) +{ + int i; + char namebuf[9]; + + for (i = 0; i < 10; i++) + { + doom_concat(doom_strcpy(namebuf, "AMMNUM"), doom_itoa(i, 10)); + marknums[i] = W_CacheLumpName(namebuf, PU_STATIC); + } +} + + +void AM_unloadPics(void) +{ + int i; + + for (i = 0; i < 10; i++) + Z_ChangeTag(marknums[i], PU_CACHE); +} + + +void AM_clearMarks(void) +{ + int i; + + for (i = 0; i < AM_NUMMARKPOINTS; i++) + markpoints[i].x = -1; // means empty + markpointnum = 0; +} + + +// +// should be called at the start of every level +// right now, i figure it out myself +// +void AM_LevelInit(void) +{ + leveljuststarted = 0; + + f_x = f_y = 0; + f_w = finit_width; + f_h = finit_height; + + AM_clearMarks(); + + AM_findMinMaxBoundaries(); + scale_mtof = FixedDiv(min_scale_mtof, (int)(0.7 * FRACUNIT)); + if (scale_mtof > max_scale_mtof) + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); +} + + +// +// +// +void AM_Stop(void) +{ + static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED }; + + AM_unloadPics(); + automapactive = false; + ST_Responder(&st_notify); + stopped = true; +} + + +// +// +// +void AM_Start(void) +{ + static int lastlevel = -1, lastepisode = -1; + + if (!stopped) AM_Stop(); + stopped = false; + if (lastlevel != gamemap || lastepisode != gameepisode) + { + AM_LevelInit(); + lastlevel = gamemap; + lastepisode = gameepisode; + } + AM_initVariables(); + AM_loadPics(); +} + + +// +// set the window scale to the maximum size +// +void AM_minOutWindowScale(void) +{ + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + + +// +// set the window scale to the minimum size +// +void AM_maxOutWindowScale(void) +{ + scale_mtof = max_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + + +// +// Handle events (user inputs) in automap mode +// +doom_boolean AM_Responder(event_t* ev) +{ + int rc; + static int cheatstate = 0; + static int bigstate = 0; + static char buffer[20]; + + rc = false; + + if (!automapactive) + { + if (ev->type == ev_keydown && ev->data1 == AM_STARTKEY) + { + AM_Start(); + viewactive = false; + rc = true; + } + } + + else if (ev->type == ev_keydown) + { + + rc = true; + switch (ev->data1) + { + case AM_PANRIGHTKEY: // pan right + if (!followplayer) m_paninc.x = FTOM(F_PANINC); + else rc = false; + break; + case AM_PANLEFTKEY: // pan left + if (!followplayer) m_paninc.x = -FTOM(F_PANINC); + else rc = false; + break; + case AM_PANUPKEY: // pan up + if (!followplayer) m_paninc.y = FTOM(F_PANINC); + else rc = false; + break; + case AM_PANDOWNKEY: // pan down + if (!followplayer) m_paninc.y = -FTOM(F_PANINC); + else rc = false; + break; + case AM_ZOOMOUTKEY: // zoom out + mtof_zoommul = M_ZOOMOUT; + ftom_zoommul = M_ZOOMIN; + break; + case AM_ZOOMINKEY: // zoom in + mtof_zoommul = M_ZOOMIN; + ftom_zoommul = M_ZOOMOUT; + break; + case AM_ENDKEY: + bigstate = 0; + viewactive = true; + AM_Stop(); + break; + case AM_GOBIGKEY: + bigstate = !bigstate; + if (bigstate) + { + AM_saveScaleAndLoc(); + AM_minOutWindowScale(); + } + else AM_restoreScaleAndLoc(); + break; + case AM_FOLLOWKEY: + followplayer = !followplayer; + f_oldloc.x = DOOM_MAXINT; + plr->message = followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF; + break; + case AM_GRIDKEY: + grid = !grid; + plr->message = grid ? AMSTR_GRIDON : AMSTR_GRIDOFF; + break; + case AM_MARKKEY: + doom_strcpy(buffer, AMSTR_MARKEDSPOT); + doom_concat(buffer, " "); + doom_concat(buffer, doom_itoa(markpointnum, 10)); + //doom_sprintf(buffer, "%s %d", AMSTR_MARKEDSPOT, markpointnum); + plr->message = buffer; + AM_addMark(); + break; + case AM_CLEARMARKKEY: + AM_clearMarks(); + plr->message = AMSTR_MARKSCLEARED; + break; + default: + cheatstate = 0; + rc = false; + } + if (!deathmatch && cht_CheckCheat(&cheat_amap, ev->data1)) + { + rc = false; + cheating = (cheating + 1) % 3; + } + } + + else if (ev->type == ev_keyup) + { + rc = false; + switch (ev->data1) + { + case AM_PANRIGHTKEY: + if (!followplayer) m_paninc.x = 0; + break; + case AM_PANLEFTKEY: + if (!followplayer) m_paninc.x = 0; + break; + case AM_PANUPKEY: + if (!followplayer) m_paninc.y = 0; + break; + case AM_PANDOWNKEY: + if (!followplayer) m_paninc.y = 0; + break; + case AM_ZOOMOUTKEY: + case AM_ZOOMINKEY: + mtof_zoommul = FRACUNIT; + ftom_zoommul = FRACUNIT; + break; + } + } + + return rc; +} + + +// +// Zooming +// +void AM_changeWindowScale(void) +{ + + // Change the scaling multipliers + scale_mtof = FixedMul(scale_mtof, mtof_zoommul); + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + + if (scale_mtof < min_scale_mtof) + AM_minOutWindowScale(); + else if (scale_mtof > max_scale_mtof) + AM_maxOutWindowScale(); + else + AM_activateNewScale(); +} + + +// +// +// +void AM_doFollowPlayer(void) +{ + if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) + { + m_x = FTOM(MTOF(plr->mo->x)) - m_w / 2; + m_y = FTOM(MTOF(plr->mo->y)) - m_h / 2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + f_oldloc.x = plr->mo->x; + f_oldloc.y = plr->mo->y; + } +} + + +// +// +// +void AM_updateLightLev(void) +{ + static int nexttic = 0; + //static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 }; + static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 }; + static int litelevelscnt = 0; + + // Change light level + if (amclock > nexttic) + { + lightlev = litelevels[litelevelscnt++]; + if (litelevelscnt == sizeof(litelevels) / sizeof(int)) litelevelscnt = 0; + nexttic = amclock + 6 - (amclock % 6); + } +} + + +// +// Updates on Game Tick +// +void AM_Ticker(void) +{ + if (!automapactive) + return; + + amclock++; + + if (followplayer) + AM_doFollowPlayer(); + + // Change the zoom if necessary + if (ftom_zoommul != FRACUNIT) + AM_changeWindowScale(); + + // Change x,y location + if (m_paninc.x || m_paninc.y) + AM_changeWindowLoc(); + + // Update light level + // AM_updateLightLev(); +} + + +// +// Clear automap frame buffer. +// +void AM_clearFB(int color) +{ + doom_memset(fb, color, f_w * f_h); +} + + +// +// Automap clipping of lines. +// +// Based on Cohen-Sutherland clipping algorithm but with a slightly +// faster reject and precalculated slopes. If the speed is needed, +// use a hash algorithm to handle the common cases. +// +doom_boolean AM_clipMline(mline_t* ml, fline_t* fl) +{ + enum + { + LEFT = 1, + RIGHT = 2, + BOTTOM = 4, + TOP = 8 + }; + + register int outcode1 = 0; + register int outcode2 = 0; + register int outside; + + fpoint_t tmp; + int dx; + int dy; + + +#define DOOUTCODE(oc, mx, my) \ + (oc) = 0; \ + if ((my) < 0) (oc) |= TOP; \ + else if ((my) >= f_h) (oc) |= BOTTOM; \ + if ((mx) < 0) (oc) |= LEFT; \ + else if ((mx) >= f_w) (oc) |= RIGHT; + + + // do trivial rejects and outcodes + if (ml->a.y > m_y2) + outcode1 = TOP; + else if (ml->a.y < m_y) + outcode1 = BOTTOM; + + if (ml->b.y > m_y2) + outcode2 = TOP; + else if (ml->b.y < m_y) + outcode2 = BOTTOM; + + if (outcode1 & outcode2) + return false; // trivially outside + + if (ml->a.x < m_x) + outcode1 |= LEFT; + else if (ml->a.x > m_x2) + outcode1 |= RIGHT; + + if (ml->b.x < m_x) + outcode2 |= LEFT; + else if (ml->b.x > m_x2) + outcode2 |= RIGHT; + + if (outcode1 & outcode2) + return false; // trivially outside + + // transform to frame-buffer coordinates. + fl->a.x = CXMTOF(ml->a.x); + fl->a.y = CYMTOF(ml->a.y); + fl->b.x = CXMTOF(ml->b.x); + fl->b.y = CYMTOF(ml->b.y); + + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + + if (outcode1 & outcode2) + return false; + + while (outcode1 | outcode2) + { + // may be partially inside box + // find an outside point + if (outcode1) + outside = outcode1; + else + outside = outcode2; + + // clip to each side + if (outside & TOP) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx * (fl->a.y)) / dy; + tmp.y = 0; + } + else if (outside & BOTTOM) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx * (fl->a.y - f_h)) / dy; + tmp.y = f_h - 1; + } + else if (outside & RIGHT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy * (f_w - 1 - fl->a.x)) / dx; + tmp.x = f_w - 1; + } + else if (outside & LEFT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy * (-fl->a.x)) / dx; + tmp.x = 0; + } + + if (outside == outcode1) + { + fl->a = tmp; + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + } + else + { + fl->b = tmp; + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + } + + if (outcode1 & outcode2) + return false; // trivially outside + } + + return true; +} +#undef DOOUTCODE + + +// +// Classic Bresenham w/ whatever optimizations needed for speed +// +void AM_drawFline(fline_t* fl, int color) +{ + register int x; + register int y; + register int dx; + register int dy; + register int sx; + register int sy; + register int ax; + register int ay; + register int d; + + static int fuck = 0; + + // For debugging only +#if 0 // [pd] Don't waste CPU cycles testing this then + if (fl->a.x < 0 || fl->a.x >= f_w + || fl->a.y < 0 || fl->a.y >= f_h + || fl->b.x < 0 || fl->b.x >= f_w + || fl->b.y < 0 || fl->b.y >= f_h) + { + doom_print("fuck "); + doom_print(doom_itoa(fuck++, 10)); + doom_print("\r"); + return; + } +#endif + +#define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc) + + dx = fl->b.x - fl->a.x; + ax = 2 * (dx < 0 ? -dx : dx); + sx = dx < 0 ? -1 : 1; + + dy = fl->b.y - fl->a.y; + ay = 2 * (dy < 0 ? -dy : dy); + sy = dy < 0 ? -1 : 1; + + x = fl->a.x; + y = fl->a.y; + + if (ax > ay) + { + d = ay - ax / 2; + while (1) + { + PUTDOT(x, y, color); + if (x == fl->b.x) return; + if (d >= 0) + { + y += sy; + d -= ax; + } + x += sx; + d += ay; + } + } + else + { + d = ax - ay / 2; + while (1) + { + PUTDOT(x, y, color); + if (y == fl->b.y) return; + if (d >= 0) + { + x += sx; + d -= ay; + } + y += sy; + d += ax; + } + } +} + + +// +// Clip lines, draw visible part sof lines. +// +void AM_drawMline(mline_t* ml, int color) +{ + static fline_t fl; + + if (AM_clipMline(ml, &fl)) + AM_drawFline(&fl, color); // draws it on frame buffer using fb coords +} + + +// +// Draws flat (floor/ceiling tile) aligned grid lines. +// +void AM_drawGrid(int color) +{ + fixed_t x, y; + fixed_t start, end; + mline_t ml; + + // Figure out start of vertical gridlines + start = m_x; + if ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS)) + start += (MAPBLOCKUNITS << FRACBITS) + - ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS)); + end = m_x + m_w; + + // draw vertical gridlines + ml.a.y = m_y; + ml.b.y = m_y + m_h; + for (x = start; x < end; x += (MAPBLOCKUNITS << FRACBITS)) + { + ml.a.x = x; + ml.b.x = x; + AM_drawMline(&ml, color); + } + + // Figure out start of horizontal gridlines + start = m_y; + if ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS)) + start += (MAPBLOCKUNITS << FRACBITS) + - ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS)); + end = m_y + m_h; + + // draw horizontal gridlines + ml.a.x = m_x; + ml.b.x = m_x + m_w; + for (y = start; y < end; y += (MAPBLOCKUNITS << FRACBITS)) + { + ml.a.y = y; + ml.b.y = y; + AM_drawMline(&ml, color); + } +} + + +// +// Determines visible lines, draws them. +// This is LineDef based, not LineSeg based. +// +void AM_drawWalls(void) +{ + int i; + static mline_t l; + + for (i = 0; i < numlines; i++) + { + l.a.x = lines[i].v1->x; + l.a.y = lines[i].v1->y; + l.b.x = lines[i].v2->x; + l.b.y = lines[i].v2->y; + if (cheating || (lines[i].flags & ML_MAPPED)) + { + if ((lines[i].flags & LINE_NEVERSEE) && !cheating) + continue; + if (!lines[i].backsector) + { + AM_drawMline(&l, WALLCOLORS + lightlev); + } + else + { + if (lines[i].special == 39) + { // teleporters + AM_drawMline(&l, WALLCOLORS + WALLRANGE / 2); + } + else if (lines[i].flags & ML_SECRET) // secret door + { + if (cheating) AM_drawMline(&l, SECRETWALLCOLORS + lightlev); + else AM_drawMline(&l, WALLCOLORS + lightlev); + } + else if (lines[i].backsector->floorheight + != lines[i].frontsector->floorheight) + { + AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change + } + else if (lines[i].backsector->ceilingheight + != lines[i].frontsector->ceilingheight) + { + AM_drawMline(&l, CDWALLCOLORS + lightlev); // ceiling level change + } + else if (cheating) + { + AM_drawMline(&l, TSWALLCOLORS + lightlev); + } + } + } + else if (plr->powers[pw_allmap]) + { + if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS + 3); + } + } +} + + +// +// Rotation in 2D. +// Used to rotate player arrow line character. +// +void AM_rotate(fixed_t* x, fixed_t* y, angle_t a) +{ + fixed_t tmpx; + + tmpx = + FixedMul(*x, finecosine[a >> ANGLETOFINESHIFT]) + - FixedMul(*y, finesine[a >> ANGLETOFINESHIFT]); + + *y = + FixedMul(*x, finesine[a >> ANGLETOFINESHIFT]) + + FixedMul(*y, finecosine[a >> ANGLETOFINESHIFT]); + + *x = tmpx; +} + + +void AM_drawLineCharacter(mline_t* lineguy, + int lineguylines, + fixed_t scale, + angle_t angle, + int color, + fixed_t x, + fixed_t y) +{ + int i; + mline_t l; + + for (i = 0; i < lineguylines; i++) + { + l.a.x = lineguy[i].a.x; + l.a.y = lineguy[i].a.y; + + if (scale) + { + l.a.x = FixedMul(scale, l.a.x); + l.a.y = FixedMul(scale, l.a.y); + } + + if (angle) + AM_rotate(&l.a.x, &l.a.y, angle); + + l.a.x += x; + l.a.y += y; + + l.b.x = lineguy[i].b.x; + l.b.y = lineguy[i].b.y; + + if (scale) + { + l.b.x = FixedMul(scale, l.b.x); + l.b.y = FixedMul(scale, l.b.y); + } + + if (angle) + AM_rotate(&l.b.x, &l.b.y, angle); + + l.b.x += x; + l.b.y += y; + + AM_drawMline(&l, color); + } +} + + +void AM_drawPlayers(void) +{ + int i; + player_t* p; + static int their_colors[] = { GREENS, GRAYS, BROWNS, REDS }; + int their_color = -1; + int color; + + if (!netgame) + { + if (cheating) + AM_drawLineCharacter + (cheat_player_arrow, NUMCHEATPLYRLINES, 0, + plr->mo->angle, WHITE, plr->mo->x, plr->mo->y); + else + AM_drawLineCharacter + (player_arrow, NUMPLYRLINES, 0, plr->mo->angle, + WHITE, plr->mo->x, plr->mo->y); + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + their_color++; + p = &players[i]; + + if ((deathmatch && !singledemo) && p != plr) + continue; + + if (!playeringame[i]) + continue; + + if (p->powers[pw_invisibility]) + color = 246; // *close* to black + else + color = their_colors[their_color]; + + AM_drawLineCharacter + (player_arrow, NUMPLYRLINES, 0, p->mo->angle, + color, p->mo->x, p->mo->y); + } +} + + +void AM_drawThings(int colors, int colorrange) +{ + int i; + mobj_t* t; + + for (i = 0; i < numsectors; i++) + { + t = sectors[i].thinglist; + while (t) + { + AM_drawLineCharacter + (thintriangle_guy, NUMTHINTRIANGLEGUYLINES, + 16 << FRACBITS, t->angle, colors + lightlev, t->x, t->y); + t = t->snext; + } + } +} + + +void AM_drawMarks(void) +{ + int i, fx, fy, w, h; + + for (i = 0; i < AM_NUMMARKPOINTS; i++) + { + if (markpoints[i].x != -1) + { + // w = SHORT(marknums[i]->width); + // h = SHORT(marknums[i]->height); + w = 5; // because something's wrong with the wad, i guess + h = 6; // because something's wrong with the wad, i guess + fx = CXMTOF(markpoints[i].x); + fy = CYMTOF(markpoints[i].y); + if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h) + V_DrawPatch(fx, fy, FB, marknums[i]); + } + } +} + + +void AM_drawCrosshair(int color) +{ + fb[(f_w * (f_h + 1)) / 2] = color; // single point for now +} + + +void AM_Drawer(void) +{ + if (!automapactive) return; + + AM_clearFB(BACKGROUND); + if (grid) + AM_drawGrid(GRIDCOLORS); + AM_drawWalls(); + AM_drawPlayers(); + if (cheating == 2) + AM_drawThings(THINGCOLORS, THINGRANGE); + AM_drawCrosshair(XHAIRCOLORS); + + AM_drawMarks(); + + V_MarkRect(f_x, f_y, f_w, f_h); +} diff --git a/src/DOOM/am_map.h b/src/DOOM/am_map.h new file mode 100644 index 0000000..20a618f --- /dev/null +++ b/src/DOOM/am_map.h @@ -0,0 +1,56 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// AutoMap module. +// +//----------------------------------------------------------------------------- + +#ifndef __AMMAP_H__ +#define __AMMAP_H__ + + +#include "d_event.h" + + +// Used by ST StatusBar stuff. +#define AM_MSGHEADER (('a'<<24)+('m'<<16)) +#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) +#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) + + +// Called by main loop. +doom_boolean AM_Responder(event_t* ev); + +// Called by main loop. +void AM_Ticker(void); + +// Called by main loop, +// called instead of view drawer if automap active. +void AM_Drawer(void); + +// Called to force the automap to quit +// if the level is completed while it is up. +void AM_Stop(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/d_englsh.h b/src/DOOM/d_englsh.h new file mode 100644 index 0000000..0ec6f10 --- /dev/null +++ b/src/DOOM/d_englsh.h @@ -0,0 +1,699 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Printed strings for translation. +// English language support (default). +// +//----------------------------------------------------------------------------- + +#ifndef __D_ENGLSH__ +#define __D_ENGLSH__ + + +// +// Printed strings for translation +// + +// +// D_Main.C +// +#define D_DEVSTR "Development mode ON.\n" +#define D_CDROM "CD-ROM Version: default.cfg from c:\\doomdata\n" + +// +// M_Menu.C +// +#define PRESSKEY "press a key." +#define PRESSYN "press y or n." +#define QUITMSG "are you sure you want to\nquit this great game?" +#define LOADNET "you can't do load while in a net game!\n\n" PRESSKEY +#define QLOADNET "you can't quickload during a netgame!\n\n" PRESSKEY +#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n" PRESSKEY +#define SAVEDEAD "you can't save if you aren't playing!\n\n" PRESSKEY +#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n" PRESSYN +#define QLPROMPT "do you want to quickload the game named\n\n'%s'?\n\n" PRESSYN + +#define QSPROMPT_1 "quicksave over your game named\n\n'" +#define QSPROMPT_2 "'?\n\n" PRESSYN +#define QLPROMPT_1 "do you want to quickload the game named\n\n'%s" +#define QLPROMPT_2 "'?\n\n" PRESSYN + +#define NEWGAME \ +"you can't start a new game\n"\ +"while in a network game.\n\n" PRESSKEY + +#define NIGHTMARE \ +"are you sure? this skill level\n"\ +"isn't even remotely fair.\n\n" PRESSYN + +#define SWSTRING \ +"this is the shareware version of doom.\n\n"\ +"you need to order the entire trilogy.\n\n" PRESSKEY + +#define MSGOFF "Messages OFF" +#define MSGON "Messages ON" +#define CROSSOFF "Crosshair OFF" +#define CROSSON "Crosshair ON" +#define ALWAYSRUNOFF "Always run OFF" +#define ALWAYSRUNON "Always run ON" +#define NETEND "you can't end a netgame!\n\n" PRESSKEY +#define ENDGAME "are you sure you want to end the game?\n\n" PRESSYN + +#define DOSY "(press y to quit)" + +#define DETAILHI "High detail" +#define DETAILLO "Low detail" +#define GAMMALVL0 "Gamma correction OFF" +#define GAMMALVL1 "Gamma correction level 1" +#define GAMMALVL2 "Gamma correction level 2" +#define GAMMALVL3 "Gamma correction level 3" +#define GAMMALVL4 "Gamma correction level 4" +#define EMPTYSTRING "empty slot" + +// +// P_inter.C +// +#define GOTARMOR "Picked up the armor." +#define GOTMEGA "Picked up the MegaArmor!" +#define GOTHTHBONUS "Picked up a health bonus." +#define GOTARMBONUS "Picked up an armor bonus." +#define GOTSTIM "Picked up a stimpack." +#define GOTMEDINEED "Picked up a medikit that you REALLY need!" +#define GOTMEDIKIT "Picked up a medikit." +#define GOTSUPER "Supercharge!" + +#define GOTBLUECARD "Picked up a blue keycard." +#define GOTYELWCARD "Picked up a yellow keycard." +#define GOTREDCARD "Picked up a red keycard." +#define GOTBLUESKUL "Picked up a blue skull key." +#define GOTYELWSKUL "Picked up a yellow skull key." +#define GOTREDSKULL "Picked up a red skull key." + +#define GOTINVUL "Invulnerability!" +#define GOTBERSERK "Berserk!" +#define GOTINVIS "Partial Invisibility" +#define GOTSUIT "Radiation Shielding Suit" +#define GOTMAP "Computer Area Map" +#define GOTVISOR "Light Amplification Visor" +#define GOTMSPHERE "MegaSphere!" + +#define GOTCLIP "Picked up a clip." +#define GOTCLIPBOX "Picked up a box of bullets." +#define GOTROCKET "Picked up a rocket." +#define GOTROCKBOX "Picked up a box of rockets." +#define GOTCELL "Picked up an energy cell." +#define GOTCELLBOX "Picked up an energy cell pack." +#define GOTSHELLS "Picked up 4 shotgun shells." +#define GOTSHELLBOX "Picked up a box of shotgun shells." +#define GOTBACKPACK "Picked up a backpack full of ammo!" + +#define GOTBFG9000 "You got the BFG9000! Oh, yes." +#define GOTCHAINGUN "You got the chaingun!" +#define GOTCHAINSAW "A chainsaw! Find some meat!" +#define GOTLAUNCHER "You got the rocket launcher!" +#define GOTPLASMA "You got the plasma gun!" +#define GOTSHOTGUN "You got the shotgun!" +#define GOTSHOTGUN2 "You got the super shotgun!" + +// +// P_Doors.C +// +#define PD_BLUEO "You need a blue key to activate this object" +#define PD_REDO "You need a red key to activate this object" +#define PD_YELLOWO "You need a yellow key to activate this object" +#define PD_BLUEK "You need a blue key to open this door" +#define PD_REDK "You need a red key to open this door" +#define PD_YELLOWK "You need a yellow key to open this door" + +// +// G_game.C +// +#define GGSAVED "game saved." + +// +// HU_stuff.C +// +#define HUSTR_MSGU "[Message unsent]" + +#define HUSTR_E1M1 "E1M1: Hangar" +#define HUSTR_E1M2 "E1M2: Nuclear Plant" +#define HUSTR_E1M3 "E1M3: Toxin Refinery" +#define HUSTR_E1M4 "E1M4: Command Control" +#define HUSTR_E1M5 "E1M5: Phobos Lab" +#define HUSTR_E1M6 "E1M6: Central Processing" +#define HUSTR_E1M7 "E1M7: Computer Station" +#define HUSTR_E1M8 "E1M8: Phobos Anomaly" +#define HUSTR_E1M9 "E1M9: Military Base" + +#define HUSTR_E2M1 "E2M1: Deimos Anomaly" +#define HUSTR_E2M2 "E2M2: Containment Area" +#define HUSTR_E2M3 "E2M3: Refinery" +#define HUSTR_E2M4 "E2M4: Deimos Lab" +#define HUSTR_E2M5 "E2M5: Command Center" +#define HUSTR_E2M6 "E2M6: Halls of the Damned" +#define HUSTR_E2M7 "E2M7: Spawning Vats" +#define HUSTR_E2M8 "E2M8: Tower of Babel" +#define HUSTR_E2M9 "E2M9: Fortress of Mystery" + +#define HUSTR_E3M1 "E3M1: Hell Keep" +#define HUSTR_E3M2 "E3M2: Slough of Despair" +#define HUSTR_E3M3 "E3M3: Pandemonium" +#define HUSTR_E3M4 "E3M4: House of Pain" +#define HUSTR_E3M5 "E3M5: Unholy Cathedral" +#define HUSTR_E3M6 "E3M6: Mt. Erebus" +#define HUSTR_E3M7 "E3M7: Limbo" +#define HUSTR_E3M8 "E3M8: Dis" +#define HUSTR_E3M9 "E3M9: Warrens" + +#define HUSTR_E4M1 "E4M1: Hell Beneath" +#define HUSTR_E4M2 "E4M2: Perfect Hatred" +#define HUSTR_E4M3 "E4M3: Sever The Wicked" +#define HUSTR_E4M4 "E4M4: Unruly Evil" +#define HUSTR_E4M5 "E4M5: They Will Repent" +#define HUSTR_E4M6 "E4M6: Against Thee Wickedly" +#define HUSTR_E4M7 "E4M7: And Hell Followed" +#define HUSTR_E4M8 "E4M8: Unto The Cruel" +#define HUSTR_E4M9 "E4M9: Fear" + +#define HUSTR_1 "level 1: entryway" +#define HUSTR_2 "level 2: underhalls" +#define HUSTR_3 "level 3: the gantlet" +#define HUSTR_4 "level 4: the focus" +#define HUSTR_5 "level 5: the waste tunnels" +#define HUSTR_6 "level 6: the crusher" +#define HUSTR_7 "level 7: dead simple" +#define HUSTR_8 "level 8: tricks and traps" +#define HUSTR_9 "level 9: the pit" +#define HUSTR_10 "level 10: refueling base" +#define HUSTR_11 "level 11: 'o' of destruction!" + +#define HUSTR_12 "level 12: the factory" +#define HUSTR_13 "level 13: downtown" +#define HUSTR_14 "level 14: the inmost dens" +#define HUSTR_15 "level 15: industrial zone" +#define HUSTR_16 "level 16: suburbs" +#define HUSTR_17 "level 17: tenements" +#define HUSTR_18 "level 18: the courtyard" +#define HUSTR_19 "level 19: the citadel" +#define HUSTR_20 "level 20: gotcha!" + +#define HUSTR_21 "level 21: nirvana" +#define HUSTR_22 "level 22: the catacombs" +#define HUSTR_23 "level 23: barrels o' fun" +#define HUSTR_24 "level 24: the chasm" +#define HUSTR_25 "level 25: bloodfalls" +#define HUSTR_26 "level 26: the abandoned mines" +#define HUSTR_27 "level 27: monster condo" +#define HUSTR_28 "level 28: the spirit world" +#define HUSTR_29 "level 29: the living end" +#define HUSTR_30 "level 30: icon of sin" + +#define HUSTR_31 "level 31: wolfenstein" +#define HUSTR_32 "level 32: grosse" + +#define PHUSTR_1 "level 1: congo" +#define PHUSTR_2 "level 2: well of souls" +#define PHUSTR_3 "level 3: aztec" +#define PHUSTR_4 "level 4: caged" +#define PHUSTR_5 "level 5: ghost town" +#define PHUSTR_6 "level 6: baron's lair" +#define PHUSTR_7 "level 7: caughtyard" +#define PHUSTR_8 "level 8: realm" +#define PHUSTR_9 "level 9: abattoire" +#define PHUSTR_10 "level 10: onslaught" +#define PHUSTR_11 "level 11: hunted" + +#define PHUSTR_12 "level 12: speed" +#define PHUSTR_13 "level 13: the crypt" +#define PHUSTR_14 "level 14: genesis" +#define PHUSTR_15 "level 15: the twilight" +#define PHUSTR_16 "level 16: the omen" +#define PHUSTR_17 "level 17: compound" +#define PHUSTR_18 "level 18: neurosphere" +#define PHUSTR_19 "level 19: nme" +#define PHUSTR_20 "level 20: the death domain" + +#define PHUSTR_21 "level 21: slayer" +#define PHUSTR_22 "level 22: impossible mission" +#define PHUSTR_23 "level 23: tombstone" +#define PHUSTR_24 "level 24: the final frontier" +#define PHUSTR_25 "level 25: the temple of darkness" +#define PHUSTR_26 "level 26: bunker" +#define PHUSTR_27 "level 27: anti-christ" +#define PHUSTR_28 "level 28: the sewers" +#define PHUSTR_29 "level 29: odyssey of noises" +#define PHUSTR_30 "level 30: the gateway of hell" + +#define PHUSTR_31 "level 31: cyberden" +#define PHUSTR_32 "level 32: go 2 it" + +#define THUSTR_1 "level 1: system control" +#define THUSTR_2 "level 2: human bbq" +#define THUSTR_3 "level 3: power control" +#define THUSTR_4 "level 4: wormhole" +#define THUSTR_5 "level 5: hanger" +#define THUSTR_6 "level 6: open season" +#define THUSTR_7 "level 7: prison" +#define THUSTR_8 "level 8: metal" +#define THUSTR_9 "level 9: stronghold" +#define THUSTR_10 "level 10: redemption" +#define THUSTR_11 "level 11: storage facility" + +#define THUSTR_12 "level 12: crater" +#define THUSTR_13 "level 13: nukage processing" +#define THUSTR_14 "level 14: steel works" +#define THUSTR_15 "level 15: dead zone" +#define THUSTR_16 "level 16: deepest reaches" +#define THUSTR_17 "level 17: processing area" +#define THUSTR_18 "level 18: mill" +#define THUSTR_19 "level 19: shipping/respawning" +#define THUSTR_20 "level 20: central processing" + +#define THUSTR_21 "level 21: administration center" +#define THUSTR_22 "level 22: habitat" +#define THUSTR_23 "level 23: lunar mining project" +#define THUSTR_24 "level 24: quarry" +#define THUSTR_25 "level 25: baron's den" +#define THUSTR_26 "level 26: ballistyx" +#define THUSTR_27 "level 27: mount pain" +#define THUSTR_28 "level 28: heck" +#define THUSTR_29 "level 29: river styx" +#define THUSTR_30 "level 30: last call" + +#define THUSTR_31 "level 31: pharaoh" +#define THUSTR_32 "level 32: caribbean" + +#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +#define HUSTR_CHATMACRO2 "I'm OK." +#define HUSTR_CHATMACRO3 "I'm not looking too good!" +#define HUSTR_CHATMACRO4 "Help!" +#define HUSTR_CHATMACRO5 "You suck!" +#define HUSTR_CHATMACRO6 "Next time, scumbag..." +#define HUSTR_CHATMACRO7 "Come here!" +#define HUSTR_CHATMACRO8 "I'll take care of it." +#define HUSTR_CHATMACRO9 "Yes" +#define HUSTR_CHATMACRO0 "No" + +#define HUSTR_TALKTOSELF1 "You mumble to yourself" +#define HUSTR_TALKTOSELF2 "Who's there?" +#define HUSTR_TALKTOSELF3 "You scare yourself" +#define HUSTR_TALKTOSELF4 "You start to rave" +#define HUSTR_TALKTOSELF5 "You've lost it..." + +#define HUSTR_MESSAGESENT "[Message Sent]" + +// The following should NOT be changed unless it seems +// just AWFULLY necessary + +#define HUSTR_PLRGREEN "Green: " +#define HUSTR_PLRINDIGO "Indigo: " +#define HUSTR_PLRBROWN "Brown: " +#define HUSTR_PLRRED "Red: " + +#define HUSTR_KEYGREEN 'g' +#define HUSTR_KEYINDIGO 'i' +#define HUSTR_KEYBROWN 'b' +#define HUSTR_KEYRED 'r' + +// +// AM_map.C +// + +#define AMSTR_FOLLOWON "Follow Mode ON" +#define AMSTR_FOLLOWOFF "Follow Mode OFF" + +#define AMSTR_GRIDON "Grid ON" +#define AMSTR_GRIDOFF "Grid OFF" + +#define AMSTR_MARKEDSPOT "Marked Spot" +#define AMSTR_MARKSCLEARED "All Marks Cleared" + +// +// ST_stuff.C +// + +#define STSTR_MUS "Music Change" +#define STSTR_NOMUS "IMPOSSIBLE SELECTION" +#define STSTR_DQDON "Degreelessness Mode On" +#define STSTR_DQDOFF "Degreelessness Mode Off" + +#define STSTR_KFAADDED "Very Happy Ammo Added" +#define STSTR_FAADDED "Ammo (no keys) Added" + +#define STSTR_NCON "No Clipping Mode ON" +#define STSTR_NCOFF "No Clipping Mode OFF" + +#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp" +#define STSTR_BEHOLDX "Power-up Toggled" + +#define STSTR_CHOPPERS "... doesn't suck - GM" +#define STSTR_CLEV "Changing Level..." + +// +// F_Finale.C +// +#define E1TEXT \ +"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" + + +#define E2TEXT \ +"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." + + +#define E3TEXT \ +"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 ..." + + +#define E4TEXT \ +"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!" + + +// after level 6, put this: +#define C1TEXT \ +"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." + +// After level 11, put this: +#define C2TEXT \ +"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." + + +// After level 20, put this: +#define C3TEXT \ +"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?" + + +// After level 29, put this: +#define C4TEXT \ +"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" + + + +// Before level 31, put this: +#define C5TEXT \ +"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." + + +// Before level 32, put this: +#define C6TEXT \ +"CONGRATULATIONS, YOU'VE FOUND THE\n"\ +"SUPER SECRET LEVEL! YOU'D BETTER\n"\ +"BLAZE THROUGH THIS ONE!\n" + + +// after map 06 +#define P1TEXT \ +"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." + + +// after map 11 +#define P2TEXT \ +"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." + + +// after map 20 +#define P3TEXT \ +"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!" + +// after map 30 +#define P4TEXT \ +"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 ..." + +// before map 31 +#define P5TEXT \ +"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." + +// before map 32 +#define P6TEXT \ +"Betcha wondered just what WAS the hardest\n"\ +"level we had ready for ya? Now you know.\n"\ +"No one gets out alive." + + +#define T1TEXT \ +"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." + + +#define T2TEXT \ +"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." + + +#define T3TEXT \ +"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." + +#define T4TEXT \ +"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." + + +#define T5TEXT \ +"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.." + + +#define T6TEXT \ +"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." + + +// +// Character cast strings F_FINALE.C +// +#define CC_ZOMBIE "ZOMBIEMAN" +#define CC_SHOTGUN "SHOTGUN GUY" +#define CC_HEAVY "HEAVY WEAPON DUDE" +#define CC_IMP "IMP" +#define CC_DEMON "DEMON" +#define CC_LOST "LOST SOUL" +#define CC_CACO "CACODEMON" +#define CC_HELL "HELL KNIGHT" +#define CC_BARON "BARON OF HELL" +#define CC_ARACH "ARACHNOTRON" +#define CC_PAIN "PAIN ELEMENTAL" +#define CC_REVEN "REVENANT" +#define CC_MANCU "MANCUBUS" +#define CC_ARCH "ARCH-VILE" +#define CC_SPIDER "THE SPIDER MASTERMIND" +#define CC_CYBER "THE CYBERDEMON" +#define CC_HERO "OUR HERO" + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/d_event.h b/src/DOOM/d_event.h new file mode 100644 index 0000000..579ab6c --- /dev/null +++ b/src/DOOM/d_event.h @@ -0,0 +1,119 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// +// +//----------------------------------------------------------------------------- + +#ifndef __D_EVENT__ +#define __D_EVENT__ + + +#include "doomtype.h" + + +// +// Event handling. +// + +// Input event types. +typedef enum +{ + ev_keydown, + ev_keyup, + ev_mouse, + ev_joystick +} evtype_t; + + +// Event structure. +typedef struct +{ + evtype_t type; + int data1; // keys / mouse/joystick buttons + int data2; // mouse/joystick x move + int data3; // mouse/joystick y move +} event_t; + + +typedef enum +{ + ga_nothing, + ga_loadlevel, + ga_newgame, + ga_loadgame, + ga_savegame, + ga_playdemo, + ga_completed, + ga_victory, + ga_worlddone, + ga_screenshot +} gameaction_t; + + +// +// Button/action code definitions. +// +typedef enum +{ + // Press "Fire". + BT_ATTACK = 1, + // Use button, to open doors, activate switches. + BT_USE = 2, + + // Flag: game events, not really buttons. + BT_SPECIAL = 128, + BT_SPECIALMASK = 3, + + // Flag, weapon change pending. + // If true, the next 3 bits hold weapon num. + BT_CHANGE = 4, + // The 3bit weapon mask and shift, convenience. + BT_WEAPONMASK = (8 + 16 + 32), + BT_WEAPONSHIFT = 3, + + // Pause the game. + BTS_PAUSE = 1, + // Save the game at each console. + BTS_SAVEGAME = 2, + + // Savegame slot numbers + // occupy the second byte of buttons. + BTS_SAVEMASK = (4 + 8 + 16), + BTS_SAVESHIFT = 2, +} buttoncode_t; + + +// +// GLOBAL VARIABLES +// +#define MAXEVENTS (64 * 64) // [pd] Crank up the number because we pump them faster + +extern event_t events[MAXEVENTS]; +extern int eventhead; +extern int eventtail; + +extern gameaction_t gameaction; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/d_french.h b/src/DOOM/d_french.h new file mode 100644 index 0000000..0823a4b --- /dev/null +++ b/src/DOOM/d_french.h @@ -0,0 +1,433 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Printed strings, french translation. +// +//----------------------------------------------------------------------------- + + +#ifndef __D_FRENCH__ +#define __D_FRENCH__ + +// +// D_Main.C +// +#define D_DEVSTR "MODE DEVELOPPEMENT ON.\n" +#define D_CDROM "VERSION CD-ROM: DEFAULT.CFG DANS C:\\DOOMDATA\n" + +// +// M_Menu.C +// +#define PRESSKEY "APPUYEZ SUR UNE TOUCHE." +#define PRESSYN "APPUYEZ SUR Y OU N" +#define QUITMSG "VOUS VOULEZ VRAIMENT\nQUITTER CE SUPER JEU?" +#define LOADNET "VOUS NE POUVEZ PAS CHARGER\nUN JEU EN RESEAU!\n\n"PRESSKEY +#define QLOADNET "CHARGEMENT RAPIDE INTERDIT EN RESEAU!\n\n"PRESSKEY +#define QSAVESPOT "VOUS N'AVEZ PAS CHOISI UN EMPLACEMENT!\n\n"PRESSKEY +#define SAVEDEAD "VOUS NE POUVEZ PAS SAUVER SI VOUS NE JOUEZ "\ +"PAS!\n\n"PRESSKEY +#define QSPROMPT "SAUVEGARDE RAPIDE DANS LE FICHIER \n\n'%s'?\n\n"PRESSYN +#define QLPROMPT "VOULEZ-VOUS CHARGER LA SAUVEGARDE"\ +"\n\n'%s'?\n\n"PRESSYN +#define NEWGAME "VOUS NE POUVEZ PAS LANCER\n"\ +"UN NOUVEAU JEU SUR RESEAU.\n\n"PRESSKEY +#define NIGHTMARE "VOUS CONFIRMEZ? CE NIVEAU EST\n"\ +"VRAIMENT IMPITOYABLE!n"PRESSYN +#define SWSTRING "CECI EST UNE VERSION SHAREWARE DE DOOM.\n\n"\ +"VOUS DEVRIEZ COMMANDER LA TRILOGIE COMPLETE.\n\n"PRESSKEY +#define MSGOFF "MESSAGES OFF" +#define MSGON "MESSAGES ON" +#define NETEND "VOUS NE POUVEZ PAS METTRE FIN A UN JEU SUR "\ +"RESEAU!\n\n"PRESSKEY +#define ENDGAME "VOUS VOULEZ VRAIMENT METTRE FIN AU JEU?\n\n"PRESSYN + +#define DOSY "(APPUYEZ SUR Y POUR REVENIR AU OS.)" + +#define DETAILHI "GRAPHISMES MAXIMUM " +#define DETAILLO "GRAPHISMES MINIMUM " +#define GAMMALVL0 "CORRECTION GAMMA OFF" +#define GAMMALVL1 "CORRECTION GAMMA NIVEAU 1" +#define GAMMALVL2 "CORRECTION GAMMA NIVEAU 2" +#define GAMMALVL3 "CORRECTION GAMMA NIVEAU 3" +#define GAMMALVL4 "CORRECTION GAMMA NIVEAU 4" +#define EMPTYSTRING "EMPLACEMENT VIDE" + +// +// P_inter.C +// +#define GOTARMOR "ARMURE RECUPEREE." +#define GOTMEGA "MEGA-ARMURE RECUPEREE!" +#define GOTHTHBONUS "BONUS DE SANTE RECUPERE." +#define GOTARMBONUS "BONUS D'ARMURE RECUPERE." +#define GOTSTIM "STIMPACK RECUPERE." +#define GOTMEDINEED "MEDIKIT RECUPERE. VOUS EN AVEZ VRAIMENT BESOIN!" +#define GOTMEDIKIT "MEDIKIT RECUPERE." +#define GOTSUPER "SUPERCHARGE!" + +#define GOTBLUECARD "CARTE MAGNETIQUE BLEUE RECUPEREE." +#define GOTYELWCARD "CARTE MAGNETIQUE JAUNE RECUPEREE." +#define GOTREDCARD "CARTE MAGNETIQUE ROUGE RECUPEREE." +#define GOTBLUESKUL "CLEF CRANE BLEUE RECUPEREE." +#define GOTYELWSKUL "CLEF CRANE JAUNE RECUPEREE." +#define GOTREDSKULL "CLEF CRANE ROUGE RECUPEREE." + +#define GOTINVUL "INVULNERABILITE!" +#define GOTBERSERK "BERSERK!" +#define GOTINVIS "INVISIBILITE PARTIELLE " +#define GOTSUIT "COMBINAISON ANTI-RADIATIONS " +#define GOTMAP "CARTE INFORMATIQUE " +#define GOTVISOR "VISEUR A AMPLIFICATION DE LUMIERE " +#define GOTMSPHERE "MEGASPHERE!" + +#define GOTCLIP "CHARGEUR RECUPERE." +#define GOTCLIPBOX "BOITE DE BALLES RECUPEREE." +#define GOTROCKET "ROQUETTE RECUPEREE." +#define GOTROCKBOX "CAISSE DE ROQUETTES RECUPEREE." +#define GOTCELL "CELLULE D'ENERGIE RECUPEREE." +#define GOTCELLBOX "PACK DE CELLULES D'ENERGIE RECUPERE." +#define GOTSHELLS "4 CARTOUCHES RECUPEREES." +#define GOTSHELLBOX "BOITE DE CARTOUCHES RECUPEREE." +#define GOTBACKPACK "SAC PLEIN DE MUNITIONS RECUPERE!" + +#define GOTBFG9000 "VOUS AVEZ UN BFG9000! OH, OUI!" +#define GOTCHAINGUN "VOUS AVEZ LA MITRAILLEUSE!" +#define GOTCHAINSAW "UNE TRONCONNEUSE!" +#define GOTLAUNCHER "VOUS AVEZ UN LANCE-ROQUETTES!" +#define GOTPLASMA "VOUS AVEZ UN FUSIL A PLASMA!" +#define GOTSHOTGUN "VOUS AVEZ UN FUSIL!" +#define GOTSHOTGUN2 "VOUS AVEZ UN SUPER FUSIL!" + +// +// P_Doors.C +// +#define PD_BLUEO "IL VOUS FAUT UNE CLEF BLEUE" +#define PD_REDO "IL VOUS FAUT UNE CLEF ROUGE" +#define PD_YELLOWO "IL VOUS FAUT UNE CLEF JAUNE" +#define PD_BLUEK PD_BLUEO +#define PD_REDK PD_REDO +#define PD_YELLOWK PD_YELLOWO + +// +// G_game.C +// +#define GGSAVED "JEU SAUVEGARDE." + +// +// HU_stuff.C +// +#define HUSTR_MSGU "[MESSAGE NON ENVOYE]" + +#define HUSTR_E1M1 "E1M1: HANGAR" +#define HUSTR_E1M2 "E1M2: USINE NUCLEAIRE " +#define HUSTR_E1M3 "E1M3: RAFFINERIE DE TOXINES " +#define HUSTR_E1M4 "E1M4: CENTRE DE CONTROLE " +#define HUSTR_E1M5 "E1M5: LABORATOIRE PHOBOS " +#define HUSTR_E1M6 "E1M6: TRAITEMENT CENTRAL " +#define HUSTR_E1M7 "E1M7: CENTRE INFORMATIQUE " +#define HUSTR_E1M8 "E1M8: ANOMALIE PHOBOS " +#define HUSTR_E1M9 "E1M9: BASE MILITAIRE " + +#define HUSTR_E2M1 "E2M1: ANOMALIE DEIMOS " +#define HUSTR_E2M2 "E2M2: ZONE DE CONFINEMENT " +#define HUSTR_E2M3 "E2M3: RAFFINERIE" +#define HUSTR_E2M4 "E2M4: LABORATOIRE DEIMOS " +#define HUSTR_E2M5 "E2M5: CENTRE DE CONTROLE " +#define HUSTR_E2M6 "E2M6: HALLS DES DAMNES " +#define HUSTR_E2M7 "E2M7: CUVES DE REPRODUCTION " +#define HUSTR_E2M8 "E2M8: TOUR DE BABEL " +#define HUSTR_E2M9 "E2M9: FORTERESSE DU MYSTERE " + +#define HUSTR_E3M1 "E3M1: DONJON DE L'ENFER " +#define HUSTR_E3M2 "E3M2: BOURBIER DU DESESPOIR " +#define HUSTR_E3M3 "E3M3: PANDEMONIUM" +#define HUSTR_E3M4 "E3M4: MAISON DE LA DOULEUR " +#define HUSTR_E3M5 "E3M5: CATHEDRALE PROFANE " +#define HUSTR_E3M6 "E3M6: MONT EREBUS" +#define HUSTR_E3M7 "E3M7: LIMBES" +#define HUSTR_E3M8 "E3M8: DIS" +#define HUSTR_E3M9 "E3M9: CLAPIERS" + +#define HUSTR_1 "NIVEAU 1: ENTREE " +#define HUSTR_2 "NIVEAU 2: HALLS SOUTERRAINS " +#define HUSTR_3 "NIVEAU 3: LE FEU NOURRI " +#define HUSTR_4 "NIVEAU 4: LE FOYER " +#define HUSTR_5 "NIVEAU 5: LES EGOUTS " +#define HUSTR_6 "NIVEAU 6: LE BROYEUR " +#define HUSTR_7 "NIVEAU 7: L'HERBE DE LA MORT" +#define HUSTR_8 "NIVEAU 8: RUSES ET PIEGES " +#define HUSTR_9 "NIVEAU 9: LE PUITS " +#define HUSTR_10 "NIVEAU 10: BASE DE RAVITAILLEMENT " +#define HUSTR_11 "NIVEAU 11: LE CERCLE DE LA MORT!" + +#define HUSTR_12 "NIVEAU 12: L'USINE " +#define HUSTR_13 "NIVEAU 13: LE CENTRE VILLE" +#define HUSTR_14 "NIVEAU 14: LES ANTRES PROFONDES " +#define HUSTR_15 "NIVEAU 15: LA ZONE INDUSTRIELLE " +#define HUSTR_16 "NIVEAU 16: LA BANLIEUE" +#define HUSTR_17 "NIVEAU 17: LES IMMEUBLES" +#define HUSTR_18 "NIVEAU 18: LA COUR " +#define HUSTR_19 "NIVEAU 19: LA CITADELLE " +#define HUSTR_20 "NIVEAU 20: JE T'AI EU!" + +#define HUSTR_21 "NIVEAU 21: LE NIRVANA" +#define HUSTR_22 "NIVEAU 22: LES CATACOMBES " +#define HUSTR_23 "NIVEAU 23: LA GRANDE FETE " +#define HUSTR_24 "NIVEAU 24: LE GOUFFRE " +#define HUSTR_25 "NIVEAU 25: LES CHUTES DE SANG" +#define HUSTR_26 "NIVEAU 26: LES MINES ABANDONNEES " +#define HUSTR_27 "NIVEAU 27: CHEZ LES MONSTRES " +#define HUSTR_28 "NIVEAU 28: LE MONDE DE L'ESPRIT " +#define HUSTR_29 "NIVEAU 29: LA LIMITE " +#define HUSTR_30 "NIVEAU 30: L'ICONE DU PECHE " + +#define HUSTR_31 "NIVEAU 31: WOLFENSTEIN" +#define HUSTR_32 "NIVEAU 32: LE MASSACRE" + + +#define HUSTR_CHATMACRO1 "JE SUIS PRET A LEUR EN FAIRE BAVER!" +#define HUSTR_CHATMACRO2 "JE VAIS BIEN." +#define HUSTR_CHATMACRO3 "JE N'AI PAS L'AIR EN FORME!" +#define HUSTR_CHATMACRO4 "AU SECOURS!" +#define HUSTR_CHATMACRO5 "TU CRAINS!" +#define HUSTR_CHATMACRO6 "LA PROCHAINE FOIS, MINABLE..." +#define HUSTR_CHATMACRO7 "VIENS ICI!" +#define HUSTR_CHATMACRO8 "JE VAIS M'EN OCCUPER." +#define HUSTR_CHATMACRO9 "OUI" +#define HUSTR_CHATMACRO0 "NON" + +#define HUSTR_TALKTOSELF1 "VOUS PARLEZ TOUT SEUL " +#define HUSTR_TALKTOSELF2 "QUI EST LA?" +#define HUSTR_TALKTOSELF3 "VOUS VOUS FAITES PEUR " +#define HUSTR_TALKTOSELF4 "VOUS COMMENCEZ A DELIRER " +#define HUSTR_TALKTOSELF5 "VOUS ETES LARGUE..." + +#define HUSTR_MESSAGESENT "[MESSAGE ENVOYE]" + +// The following should NOT be changed unless it seems +// just AWFULLY necessary + +#define HUSTR_PLRGREEN "VERT: " +#define HUSTR_PLRINDIGO "INDIGO: " +#define HUSTR_PLRBROWN "BRUN: " +#define HUSTR_PLRRED "ROUGE: " + +#define HUSTR_KEYGREEN 'g' // french key should be "V" +#define HUSTR_KEYINDIGO 'i' +#define HUSTR_KEYBROWN 'b' +#define HUSTR_KEYRED 'r' + +// +// AM_map.C +// + +#define AMSTR_FOLLOWON "MODE POURSUITE ON" +#define AMSTR_FOLLOWOFF "MODE POURSUITE OFF" + +#define AMSTR_GRIDON "GRILLE ON" +#define AMSTR_GRIDOFF "GRILLE OFF" + +#define AMSTR_MARKEDSPOT "REPERE MARQUE " +#define AMSTR_MARKSCLEARED "REPERES EFFACES " + +// +// ST_stuff.C +// + +#define STSTR_MUS "CHANGEMENT DE MUSIQUE " +#define STSTR_NOMUS "IMPOSSIBLE SELECTION" +#define STSTR_DQDON "INVULNERABILITE ON " +#define STSTR_DQDOFF "INVULNERABILITE OFF" + +#define STSTR_KFAADDED "ARMEMENT MAXIMUM! " +#define STSTR_FAADDED "ARMES (SAUF CLEFS) AJOUTEES" + +#define STSTR_NCON "BARRIERES ON" +#define STSTR_NCOFF "BARRIERES OFF" + +#define STSTR_BEHOLD " inVuln, Str, Inviso, Rad, Allmap, or Lite-amp" +#define STSTR_BEHOLDX "AMELIORATION ACTIVEE" + +#define STSTR_CHOPPERS "... DOESN'T SUCK - GM" +#define STSTR_CLEV "CHANGEMENT DE NIVEAU..." + +// +// F_Finale.C +// +#define E1TEXT "APRES AVOIR VAINCU LES GROS MECHANTS\n"\ +"ET NETTOYE LA BASE LUNAIRE, VOUS AVEZ\n"\ +"GAGNE, NON? PAS VRAI? OU EST DONC VOTRE\n"\ +" RECOMPENSE ET VOTRE BILLET DE\n"\ +"RETOUR? QU'EST-QUE CA VEUT DIRE?CE"\ +"N'EST PAS LA FIN ESPEREE!\n"\ +"\n" \ +"CA SENT LA VIANDE PUTREFIEE, MAIS\n"\ +"ON DIRAIT LA BASE DEIMOS. VOUS ETES\n"\ +"APPAREMMENT BLOQUE AUX PORTES DE L'ENFER.\n"\ +"LA SEULE ISSUE EST DE L'AUTRE COTE.\n"\ +"\n"\ +"POUR VIVRE LA SUITE DE DOOM, JOUEZ\n"\ +"A 'AUX PORTES DE L'ENFER' ET A\n"\ +"L'EPISODE SUIVANT, 'L'ENFER'!\n" + +#define E2TEXT "VOUS AVEZ REUSSI. L'INFAME DEMON\n"\ +"QUI CONTROLAIT LA BASE LUNAIRE DE\n"\ +"DEIMOS EST MORT, ET VOUS AVEZ\n"\ +"TRIOMPHE! MAIS... OU ETES-VOUS?\n"\ +"VOUS GRIMPEZ JUSQU'AU BORD DE LA\n"\ +"LUNE ET VOUS DECOUVREZ L'ATROCE\n"\ +"VERITE.\n" \ +"\n"\ +"DEIMOS EST AU-DESSUS DE L'ENFER!\n"\ +"VOUS SAVEZ QUE PERSONNE NE S'EN\n"\ +"EST JAMAIS ECHAPPE, MAIS CES FUMIERS\n"\ +"VONT REGRETTER DE VOUS AVOIR CONNU!\n"\ +"VOUS REDESCENDEZ RAPIDEMENT VERS\n"\ +"LA SURFACE DE L'ENFER.\n"\ +"\n" \ +"VOICI MAINTENANT LE CHAPITRE FINAL DE\n"\ +"DOOM! -- L'ENFER." + +#define E3TEXT "LE DEMON ARACHNEEN ET REPUGNANT\n"\ +"QUI A DIRIGE L'INVASION DES BASES\n"\ +"LUNAIRES ET SEME LA MORT VIENT DE SE\n"\ +"FAIRE PULVERISER UNE FOIS POUR TOUTES.\n"\ +"\n"\ +"UNE PORTE SECRETE S'OUVRE. VOUS ENTREZ.\n"\ +"VOUS AVEZ PROUVE QUE VOUS POUVIEZ\n"\ +"RESISTER AUX HORREURS DE L'ENFER.\n"\ +"IL SAIT ETRE BEAU JOUEUR, ET LORSQUE\n"\ +"VOUS SORTEZ, VOUS REVOYEZ LES VERTES\n"\ +"PRAIRIES DE LA TERRE, VOTRE PLANETE.\n"\ +"\n"\ +"VOUS VOUS DEMANDEZ CE QUI S'EST PASSE\n"\ +"SUR TERRE PENDANT QUE VOUS AVEZ\n"\ +"COMBATTU LE DEMON. HEUREUSEMENT,\n"\ +"AUCUN GERME DU MAL N'A FRANCHI\n"\ +"CETTE PORTE AVEC VOUS..." + + + +// after level 6, put this: + +#define C1TEXT "VOUS ETES AU PLUS PROFOND DE L'ASTROPORT\n" \ +"INFESTE DE MONSTRES, MAIS QUELQUE CHOSE\n" \ +"NE VA PAS. ILS ONT APPORTE LEUR PROPRE\n" \ +"REALITE, ET LA TECHNOLOGIE DE L'ASTROPORT\n" \ +"EST AFFECTEE PAR LEUR PRESENCE.\n" \ +"\n"\ +"DEVANT VOUS, VOUS VOYEZ UN POSTE AVANCE\n" \ +"DE L'ENFER, UNE ZONE FORTIFIEE. SI VOUS\n" \ +"POUVEZ PASSER, VOUS POURREZ PENETRER AU\n" \ +"COEUR DE LA BASE HANTEE ET TROUVER \n" \ +"L'INTERRUPTEUR DE CONTROLE QUI GARDE LA \n" \ +"POPULATION DE LA TERRE EN OTAGE." + +// After level 11, put this: + +#define C2TEXT "VOUS AVEZ GAGNE! VOTRE VICTOIRE A PERMIS\n" \ +"A L'HUMANITE D'EVACUER LA TERRE ET \n"\ +"D'ECHAPPER AU CAUCHEMAR. VOUS ETES \n"\ +"MAINTENANT LE DERNIER HUMAIN A LA SURFACE \n"\ +"DE LA PLANETE. VOUS ETES ENTOURE DE \n"\ +"MUTANTS CANNIBALES, D'EXTRATERRESTRES \n"\ +"CARNIVORES ET D'ESPRITS DU MAL. VOUS \n"\ +"ATTENDEZ CALMEMENT LA MORT, HEUREUX \n"\ +"D'AVOIR PU SAUVER VOTRE RACE.\n"\ +"MAIS UN MESSAGE VOUS PARVIENT SOUDAIN\n"\ +"DE L'ESPACE: \"NOS CAPTEURS ONT LOCALISE\n"\ +"LA SOURCE DE L'INVASION EXTRATERRESTRE.\n"\ +"SI VOUS Y ALLEZ, VOUS POURREZ PEUT-ETRE\n"\ +"LES ARRETER. LEUR BASE EST SITUEE AU COEUR\n"\ +"DE VOTRE VILLE NATALE, PRES DE L'ASTROPORT.\n"\ +"VOUS VOUS RELEVEZ LENTEMENT ET PENIBLEMENT\n"\ +"ET VOUS REPARTEZ POUR LE FRONT." + +// After level 20, put this: + +#define C3TEXT "VOUS ETES AU COEUR DE LA CITE CORROMPUE,\n"\ +"ENTOURE PAR LES CADAVRES DE VOS ENNEMIS.\n"\ +"VOUS NE VOYEZ PAS COMMENT DETRUIRE LA PORTE\n"\ +"DES CREATURES DE CE COTE. VOUS SERREZ\n"\ +"LES DENTS ET PLONGEZ DANS L'OUVERTURE.\n"\ +"\n"\ +"IL DOIT Y AVOIR UN MOYEN DE LA FERMER\n"\ +"DE L'AUTRE COTE. VOUS ACCEPTEZ DE\n"\ +"TRAVERSER L'ENFER POUR LE FAIRE?" + +// After level 29, put this: + +#define C4TEXT "LE VISAGE HORRIBLE D'UN DEMON D'UNE\n"\ +"TAILLE INCROYABLE S'EFFONDRE DEVANT\n"\ +"VOUS LORSQUE VOUS TIREZ UNE SALVE DE\n"\ +"ROQUETTES DANS SON CERVEAU. LE MONSTRE\n"\ +"SE RATATINE, SES MEMBRES DECHIQUETES\n"\ +"SE REPANDANT SUR DES CENTAINES DE\n"\ +"KILOMETRES A LA SURFACE DE L'ENFER.\n"\ +"\n"\ +"VOUS AVEZ REUSSI. L'INVASION N'AURA.\n"\ +"PAS LIEU. LA TERRE EST SAUVEE. L'ENFER\n"\ +"EST ANEANTI. EN VOUS DEMANDANT OU IRONT\n"\ +"MAINTENANT LES DAMNES, VOUS ESSUYEZ\n"\ +"VOTRE FRONT COUVERT DE SUEUR ET REPARTEZ\n"\ +"VERS LA TERRE. SA RECONSTRUCTION SERA\n"\ +"BEAUCOUP PLUS DROLE QUE SA DESTRUCTION.\n" + +// Before level 31, put this: + +#define C5TEXT "FELICITATIONS! VOUS AVEZ TROUVE LE\n"\ +"NIVEAU SECRET! IL SEMBLE AVOIR ETE\n"\ +"CONSTRUIT PAR LES HUMAINS. VOUS VOUS\n"\ +"DEMANDEZ QUELS PEUVENT ETRE LES\n"\ +"HABITANTS DE CE COIN PERDU DE L'ENFER." + +// Before level 32, put this: + +#define C6TEXT "FELICITATIONS! VOUS AVEZ DECOUVERT\n"\ +"LE NIVEAU SUPER SECRET! VOUS FERIEZ\n"\ +"MIEUX DE FONCER DANS CELUI-LA!\n" + +// +// Character cast strings F_FINALE.C +// +#define CC_ZOMBIE "ZOMBIE" +#define CC_SHOTGUN "TYPE AU FUSIL" +#define CC_HEAVY "MEC SUPER-ARME" +#define CC_IMP "DIABLOTIN" +#define CC_DEMON "DEMON" +#define CC_LOST "AME PERDUE" +#define CC_CACO "CACODEMON" +#define CC_HELL "CHEVALIER DE L'ENFER" +#define CC_BARON "BARON DE L'ENFER" +#define CC_ARACH "ARACHNOTRON" +#define CC_PAIN "ELEMENTAIRE DE LA DOULEUR" +#define CC_REVEN "REVENANT" +#define CC_MANCU "MANCUBUS" +#define CC_ARCH "ARCHI-INFAME" +#define CC_SPIDER "L'ARAIGNEE CERVEAU" +#define CC_CYBER "LE CYBERDEMON" +#define CC_HERO "NOTRE HEROS" + + + +#endif +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- + + diff --git a/src/DOOM/d_items.c b/src/DOOM/d_items.c new file mode 100644 index 0000000..ca84070 --- /dev/null +++ b/src/DOOM/d_items.c @@ -0,0 +1,125 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "info.h" // We are referring to sprite numbers. +#include "d_items.h" + + +// +// PSPRITE ACTIONS for waepons. +// This struct controls the weapon animations. +// +// Each entry is: +// ammo/amunition type +// upstate +// downstate +// readystate +// atkstate, i.e. attack/fire/hit frame +// flashstate, muzzle flash +// +weaponinfo_t weaponinfo[NUMWEAPONS] = +{ + { + // fist + am_noammo, + S_PUNCHUP, + S_PUNCHDOWN, + S_PUNCH, + S_PUNCH1, + S_NULL + }, + { + // pistol + am_clip, + S_PISTOLUP, + S_PISTOLDOWN, + S_PISTOL, + S_PISTOL1, + S_PISTOLFLASH + }, + { + // shotgun + am_shell, + S_SGUNUP, + S_SGUNDOWN, + S_SGUN, + S_SGUN1, + S_SGUNFLASH1 + }, + { + // chaingun + am_clip, + S_CHAINUP, + S_CHAINDOWN, + S_CHAIN, + S_CHAIN1, + S_CHAINFLASH1 + }, + { + // missile launcher + am_misl, + S_MISSILEUP, + S_MISSILEDOWN, + S_MISSILE, + S_MISSILE1, + S_MISSILEFLASH1 + }, + { + // plasma rifle + am_cell, + S_PLASMAUP, + S_PLASMADOWN, + S_PLASMA, + S_PLASMA1, + S_PLASMAFLASH1 + }, + { + // bfg 9000 + am_cell, + S_BFGUP, + S_BFGDOWN, + S_BFG, + S_BFG1, + S_BFGFLASH1 + }, + { + // chainsaw + am_noammo, + S_SAWUP, + S_SAWDOWN, + S_SAW, + S_SAW1, + S_NULL + }, + { + // super shotgun + am_shell, + S_DSGUNUP, + S_DSGUNDOWN, + S_DSGUN, + S_DSGUN1, + S_DSGUNFLASH1 + }, +}; diff --git a/src/DOOM/d_items.h b/src/DOOM/d_items.h new file mode 100644 index 0000000..75d97c2 --- /dev/null +++ b/src/DOOM/d_items.h @@ -0,0 +1,50 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Items: key cards, artifacts, weapon, ammunition. +// +//----------------------------------------------------------------------------- + +#ifndef __D_ITEMS__ +#define __D_ITEMS__ + + +#include "doomdef.h" + + +// Weapon info: sprite frames, ammunition use. +typedef struct +{ + ammotype_t ammo; + int upstate; + int downstate; + int readystate; + int atkstate; + int flashstate; +} weaponinfo_t; + + +extern weaponinfo_t weaponinfo[NUMWEAPONS]; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/d_main.c b/src/DOOM/d_main.c new file mode 100644 index 0000000..6cf98c8 --- /dev/null +++ b/src/DOOM/d_main.c @@ -0,0 +1,1258 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// DOOM main program (D_DoomMain) and game loop (D_DoomLoop), +// plus functions to determine game mode (shareware, registered), +// parse command line parameters, configure game parameters (turbo), +// and call the startup functions. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#if defined(DOOM_WIN32) + + +#define X_OK 0 +#define W_OK 2 +#define R_OK 4 +#define RW_OK 6 +#elif defined(DOOM_APPLE) + + +#endif + +#include "doomdef.h" +#include "doomstat.h" +#include "dstrings.h" +#include "sounds.h" +#include "z_zone.h" +#include "w_wad.h" +#include "s_sound.h" +#include "v_video.h" +#include "f_finale.h" +#include "f_wipe.h" +#include "m_argv.h" +#include "m_misc.h" +#include "m_menu.h" +#include "i_system.h" +#include "i_sound.h" +#include "i_video.h" +#include "g_game.h" +#include "hu_stuff.h" +#include "wi_stuff.h" +#include "st_stuff.h" +#include "am_map.h" +#include "p_setup.h" +#include "r_local.h" +#include "d_main.h" + + +#define MAXARGVS 100 + + +char* wadfiles[MAXWADFILES]; + +doom_boolean devparm; // started game with -devparm +doom_boolean nomonsters; // checkparm of -nomonsters +doom_boolean respawnparm; // checkparm of -respawn +doom_boolean fastparm; // checkparm of -fast + +doom_boolean drone; + +doom_boolean singletics = true; // debug flag to cancel adaptiveness + +doom_boolean is_wiping_screen = false; + +skill_t startskill; +int startepisode; +int startmap; +doom_boolean autostart; + +void* debugfile = 0; + +doom_boolean advancedemo; + +char wadfile[1024]; // primary wad file +char mapdir[1024]; // directory of development maps +char basedefault[1024]; // default file + +// +// EVENT HANDLING +// +// Events are asynchronous inputs generally generated by the game user. +// Events can be discarded if no responder claims them +// +event_t events[MAXEVENTS]; +int eventhead; +int eventtail; + +// wipegamestate can be set to -1 to force a wipe on the next draw +gamestate_t wipegamestate = GS_DEMOSCREEN; +void R_ExecuteSetViewSize(void); + +// print title for every printed line +char title[128]; + + +extern doom_boolean inhelpscreens; +extern doom_boolean setsizeneeded; +extern int showMessages; +extern doom_boolean demorecording; + + +void D_DoomLoop(void); +void D_CheckNetGame(void); +void D_ProcessEvents(void); +void G_BuildTiccmd(ticcmd_t* cmd); +void D_DoAdvanceDemo(void); + + +// +// D_PostEvent +// Called by the I/O functions when input is detected +// +void D_PostEvent(event_t* ev) +{ + events[eventhead] = *ev; + eventhead++; + eventhead = (eventhead) & (MAXEVENTS - 1); +} + + +// +// D_ProcessEvents +// Send all the events of the given timestamp down the responder chain +// +void D_ProcessEvents(void) +{ + event_t* ev; + + // IF STORE DEMO, DO NOT ACCEPT INPUT + if ((gamemode == commercial) + && (W_CheckNumForName("map01") < 0)) + return; + + for (; eventtail != eventhead; ) + { + ev = &events[eventtail]; + if (!M_Responder(ev)) + G_Responder(ev); + // else menu ate the event + eventtail++; + eventtail = (eventtail) & (MAXEVENTS - 1); + } +} + + +// +// D_Display +// draw current display, possibly wiping it from the previous +// +void D_Display(void) +{ + static doom_boolean viewactivestate = false; + static doom_boolean menuactivestate = false; + static doom_boolean inhelpscreensstate = false; + static doom_boolean fullscreen = false; + static gamestate_t oldgamestate = -1; + static int borderdrawcount; + int wipestart; + int y; + doom_boolean wipe; + doom_boolean redrawsbar; + + if (nodrawers) + return; // for comparative timing / profiling + + redrawsbar = false; + + // change the view size if needed + if (setsizeneeded) + { + R_ExecuteSetViewSize(); + oldgamestate = -1; // force background redraw + borderdrawcount = 3; + } + + // save the current screen if about to wipe + if (gamestate != wipegamestate) + { + wipe = true; + wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); + } + else + wipe = false; + + if (gamestate == GS_LEVEL && gametic) + HU_Erase(); + + // do buffered drawing + switch (gamestate) + { + case GS_LEVEL: + if (!gametic) + break; + if (automapactive) + AM_Drawer(); + if (wipe || (viewheight != 200 && fullscreen)) + redrawsbar = true; + if (inhelpscreensstate && !inhelpscreens) + redrawsbar = true; // just put away the help screen + ST_Drawer(viewheight == 200, redrawsbar); + fullscreen = viewheight == 200; + break; + + case GS_INTERMISSION: + WI_Drawer(); + break; + + case GS_FINALE: + F_Drawer(); + break; + + case GS_DEMOSCREEN: + D_PageDrawer(); + break; + } + + // draw buffered stuff to screen + I_UpdateNoBlit(); + + // draw the view directly + if (gamestate == GS_LEVEL && !automapactive && gametic) + R_RenderPlayerView(&players[displayplayer]); + + if (gamestate == GS_LEVEL && gametic) + HU_Drawer(); + + // clean up border stuff + if (gamestate != oldgamestate && gamestate != GS_LEVEL) + I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); + + // see if the border needs to be initially drawn + if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL) + { + viewactivestate = false; // view was not active + R_FillBackScreen(); // draw the pattern into the back screen + } + + // see if the border needs to be updated to the screen + if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != 320) + { + if (menuactive || menuactivestate || !viewactivestate) + borderdrawcount = 3; + if (borderdrawcount) + { + R_DrawViewBorder(); // erase old menu stuff + borderdrawcount--; + } + + } + + menuactivestate = menuactive; + viewactivestate = viewactive; + inhelpscreensstate = inhelpscreens; + oldgamestate = wipegamestate = gamestate; + + // draw pause pic + if (paused) + { + if (automapactive) + y = 4; + else + y = viewwindowy + 4; + V_DrawPatchDirect(viewwindowx + (scaledviewwidth - 68) / 2, + y, 0, W_CacheLumpName("M_PAUSE", PU_CACHE)); + } + + + // menus go directly to the screen + M_Drawer(); // menu is drawn even on top of everything + NetUpdate(); // send out any new accumulation + + + // normal update + is_wiping_screen = wipe; + if (!wipe) + { + I_FinishUpdate(); // page flip or blit buffer + return; + } + + // wipe update + wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); + + wipestart = I_GetTime() - 1; + +#if 0 // [pd] Moved to D_UpdateWipe + do + { + do + { + nowtime = I_GetTime(); + tics = nowtime - wipestart; + } while (!tics); + wipestart = nowtime; + done = wipe_ScreenWipe(wipe_Melt + , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics); + I_UpdateNoBlit(); + M_Drawer(); // menu is drawn even on top of wipes + I_FinishUpdate(); // page flip or blit buffer + } while (!done); +#endif +} + + +// +// D_DoomLoop +// +void D_UpdateWipe(void) +{ + if (wipe_ScreenWipe(wipe_Melt, 0, 0, SCREENWIDTH, SCREENHEIGHT, 1)) + is_wiping_screen = false; +} + + +void D_DoomLoop(void) +{ +#if 0 // [pd] Moved to D_DoomMain() + if (demorecording) + G_BeginRecording(); + + if (M_CheckParm("-debugfile")) + { + char filename[20]; + //doom_sprintf(filename, "debug%i.txt", consoleplayer); + doom_print("debug output to: %s\n", filename); + debugfile = doom_open(filename, "w"); + } + + I_InitGraphics(); +#endif + + // while (1) + { + // frame syncronous IO operations + I_StartFrame(); + + // process one or more tics + if (singletics) + { + I_StartTic(); + D_ProcessEvents(); + G_BuildTiccmd(&netcmds[consoleplayer][maketic % BACKUPTICS]); + if (advancedemo) + D_DoAdvanceDemo(); + M_Ticker(); + G_Ticker(); + gametic++; + maketic++; + } + else + { + TryRunTics(); // will run at least one tic + } + + S_UpdateSounds(players[consoleplayer].mo);// move positional sounds + + // Update display, next frame, with current state. + D_Display(); + +#if 0 // [pd] Sound is queried by the application's audio thread. + // Sound mixing for the buffer is snychronous. + I_UpdateSound(); + + // Synchronous sound output is explicitly called. + // Update sound output. + I_SubmitSound(); +#endif + } +} + + +// +// DEMO LOOP +// +int demosequence; +int pagetic; +char* pagename; + + +// +// D_PageTicker +// Handles timing for warped projection +// +void D_PageTicker(void) +{ + if (--pagetic < 0) + D_AdvanceDemo(); +} + + +// +// D_PageDrawer +// +void D_PageDrawer(void) +{ + V_DrawPatch(0, 0, 0, W_CacheLumpName(pagename, PU_CACHE)); +} + + +// +// D_AdvanceDemo +// Called after each demo or intro demosequence finishes +// +void D_AdvanceDemo(void) +{ + advancedemo = true; +} + + +// +// This cycles through the demo sequences. +// FIXME - version dependend demo numbers? +// +void D_DoAdvanceDemo(void) +{ + players[consoleplayer].playerstate = PST_LIVE; // not reborn + advancedemo = false; + usergame = false; // no save / end game here + paused = false; + gameaction = ga_nothing; + + if (gamemode == retail) + demosequence = (demosequence + 1) % 7; + else + demosequence = (demosequence + 1) % 6; + + switch (demosequence) + { + case 0: + if (gamemode == commercial) + pagetic = 35 * 11; + else + pagetic = 170; + gamestate = GS_DEMOSCREEN; + pagename = "TITLEPIC"; + if (gamemode == commercial) + S_StartMusic(mus_dm2ttl); + else + S_StartMusic(mus_intro); + break; + case 1: + G_DeferedPlayDemo("demo1"); + break; + case 2: + pagetic = 200; + gamestate = GS_DEMOSCREEN; + pagename = "CREDIT"; + break; + case 3: + G_DeferedPlayDemo("demo2"); + break; + case 4: + gamestate = GS_DEMOSCREEN; + if (gamemode == commercial) + { + pagetic = 35 * 11; + pagename = "TITLEPIC"; + S_StartMusic(mus_dm2ttl); + } + else + { + pagetic = 200; + + if (gamemode == retail) + pagename = "CREDIT"; + else + pagename = "HELP2"; + } + break; + case 5: + G_DeferedPlayDemo("demo3"); + break; + // THE DEFINITIVE DOOM Special Edition demo + case 6: + G_DeferedPlayDemo("demo4"); + break; + } +} + + +// +// D_StartTitle +// +void D_StartTitle(void) +{ + gameaction = ga_nothing; + demosequence = -1; + D_AdvanceDemo(); +} + + +// +// D_AddFile +// +void D_AddFile(char* file) +{ + int numwadfiles; + char* newfile; + + for (numwadfiles = 0; wadfiles[numwadfiles]; numwadfiles++) + ; + + newfile = doom_malloc(doom_strlen(file) + 1); + doom_strcpy(newfile, file); + + wadfiles[numwadfiles] = newfile; +} + + +// +// IdentifyVersion +// Checks availability of IWAD files by name, +// to determine whether registered/commercial features +// should be executed (notably loading PWAD's). +// +void IdentifyVersion(void) +{ + char* doom1wad; + char* doomwad; + char* doomuwad; + char* doom2wad; + + char* doom2fwad; + char* plutoniawad; + char* tntwad; + + char* home; + char* doomwaddir; + doomwaddir = doom_getenv("DOOMWADDIR"); + if (!doomwaddir) + doomwaddir = "."; + + // Commercial. + doom2wad = doom_malloc(doom_strlen(doomwaddir) + 1 + 9 + 1); + //doom_sprintf(doom2wad, "%s/doom2.wad", doomwaddir); + doom_strcpy(doom2wad, doomwaddir); + doom_concat(doom2wad, "/doom2.wad"); + + // Retail. + doomuwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 8 + 1); + //doom_sprintf(doomuwad, "%s/doomu.wad", doomwaddir); + doom_strcpy(doomuwad, doomwaddir); + doom_concat(doomuwad, "/doomu.wad"); + + // Registered. + doomwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 8 + 1); + //doom_sprintf(doomwad, "%s/doom.wad", doomwaddir); + doom_strcpy(doomwad, doomwaddir); + doom_concat(doomwad, "/doom.wad"); + + // Shareware. + doom1wad = doom_malloc(doom_strlen(doomwaddir) + 1 + 9 + 1); + //doom_sprintf(doom1wad, "%s/doom1.wad", doomwaddir); + doom_strcpy(doom1wad, doomwaddir); + doom_concat(doom1wad, "/doom1.wad"); + + // Bug, dear Shawn. + // Insufficient malloc, caused spurious realloc errors. + plutoniawad = doom_malloc(doom_strlen(doomwaddir) + 1 +/*9*/12 + 1); + //doom_sprintf(plutoniawad, "%s/plutonia.wad", doomwaddir); + doom_strcpy(plutoniawad, doomwaddir); + doom_concat(plutoniawad, "/plutonia.wad"); + + tntwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 9 + 1); + //doom_sprintf(tntwad, "%s/tnt.wad", doomwaddir); + doom_strcpy(tntwad, doomwaddir); + doom_concat(tntwad, "/tnt.wad"); + + // French stuff. + doom2fwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 10 + 1); + //doom_sprintf(doom2fwad, "%s/doom2f.wad", doomwaddir); + doom_strcpy(doom2fwad, doomwaddir); + doom_concat(doom2fwad, "/doom2f.wad"); + +#if !defined(DOOM_WIN32) + home = doom_getenv("HOME"); + if (!home) + I_Error("Error: Please set $HOME to your home directory"); +#else + home = "."; +#endif + //doom_sprintf(basedefault, "%s/.doomrc", home); + doom_strcpy(basedefault, home); + doom_concat(basedefault, "/.doomrc"); + + if (M_CheckParm("-shdev")) + { + gamemode = shareware; + devparm = true; + D_AddFile(DEVDATA"doom1.wad"); + D_AddFile(DEVMAPS"data_se/texture1.lmp"); + D_AddFile(DEVMAPS"data_se/pnames.lmp"); + doom_strcpy(basedefault, DEVDATA"default.cfg"); + return; + } + + if (M_CheckParm("-regdev")) + { + gamemode = registered; + devparm = true; + D_AddFile(DEVDATA"doom.wad"); + D_AddFile(DEVMAPS"data_se/texture1.lmp"); + D_AddFile(DEVMAPS"data_se/texture2.lmp"); + D_AddFile(DEVMAPS"data_se/pnames.lmp"); + doom_strcpy(basedefault, DEVDATA"default.cfg"); + return; + } + + if (M_CheckParm("-comdev")) + { + gamemode = commercial; + devparm = true; + /* I don't bother + if(plutonia) + D_AddFile (DEVDATA"plutonia.wad"); + else if(tnt) + D_AddFile (DEVDATA"tnt.wad"); + else*/ + D_AddFile(DEVDATA"doom2.wad"); + + D_AddFile(DEVMAPS"cdata/texture1.lmp"); + D_AddFile(DEVMAPS"cdata/pnames.lmp"); + doom_strcpy(basedefault, DEVDATA"default.cfg"); + return; + } + + void* f; + if (f = doom_open(doom2fwad, "rb")) + { + doom_close(f); + gamemode = commercial; + // C'est ridicule! + // Let's handle languages in config files, okay? + language = french; + doom_print("French version\n"); + D_AddFile(doom2fwad); + return; + } + + if (f = doom_open(doom2wad, "rb")) + { + doom_close(f); + gamemode = commercial; + D_AddFile(doom2wad); + return; + } + + if (f = doom_open(plutoniawad, "rb")) + { + doom_close(f); + gamemode = commercial; + D_AddFile(plutoniawad); + return; + } + + if (f = doom_open(tntwad, "rb")) + { + doom_close(f); + gamemode = commercial; + D_AddFile(tntwad); + return; + } + + if (f = doom_open(doomuwad, "rb")) + { + doom_close(f); + gamemode = retail; + D_AddFile(doomuwad); + return; + } + + if (f = doom_open(doomwad, "rb")) + { + doom_close(f); + gamemode = registered; + D_AddFile(doomwad); + return; + } + + if (f = doom_open(doom1wad, "rb")) + { + doom_close(f); + gamemode = shareware; + D_AddFile(doom1wad); + return; + } + + doom_print("Game mode indeterminate.\n"); + gamemode = indetermined; +} + + +// +// Find a Response File +// +void FindResponseFile(void) +{ + int i; + + for (i = 1; i < myargc; i++) + if (myargv[i][0] == '@') + { + void* handle = 0; + int size; + int k; + int index; + int indexinfile; + char* infile; + char* file; + char* moreargs[20]; + char* firstargv; + + // READ THE RESPONSE FILE INTO MEMORY + handle = doom_open(&myargv[i][1], "rb"); + if (!handle) + { + doom_print("\nNo such response file!"); + doom_exit(1); + } + doom_print("Found response file %s!\n"); + doom_print(&myargv[i][1]); + doom_print("!\n"); + doom_seek(handle, 0, DOOM_SEEK_END); + size = doom_tell(handle); + doom_seek(handle, 0, DOOM_SEEK_SET); + file = doom_malloc(size); + doom_read(handle, file, size * 1); + doom_close(handle); + + // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG + for (index = 0, k = i + 1; k < myargc; k++) + moreargs[index++] = myargv[k]; + + firstargv = myargv[0]; + myargv = doom_malloc(sizeof(char*) * MAXARGVS); + doom_memset(myargv, 0, sizeof(char*) * MAXARGVS); + myargv[0] = firstargv; + + infile = file; + indexinfile = k = 0; + indexinfile++; // SKIP PAST ARGV[0] (KEEP IT) + do + { + myargv[indexinfile++] = infile + k; + while (k < size && + ((*(infile + k) >= ' ' + 1) && (*(infile + k) <= 'z'))) + k++; + *(infile + k) = 0; + while (k < size && + ((*(infile + k) <= ' ') || (*(infile + k) > 'z'))) + k++; + } while (k < size); + + for (k = 0; k < index; k++) + myargv[indexinfile++] = moreargs[k]; + myargc = indexinfile; + + // DISPLAY ARGS + doom_print(doom_itoa(myargc, 10)); + doom_print(" command-line args:\n"); + for (k = 1; k < myargc; k++) + { + //doom_print("%s\n", myargv[k]); + doom_print(myargv[k]); + doom_print("\n"); + } + + break; + } +} + + +// +// D_DoomMain +// +void D_DoomMain(void) +{ + int p; + char file[256]; + + FindResponseFile(); + + IdentifyVersion(); + + modifiedgame = false; + + nomonsters = M_CheckParm("-nomonsters"); + respawnparm = M_CheckParm("-respawn"); + fastparm = M_CheckParm("-fast"); + devparm = M_CheckParm("-devparm"); + if (M_CheckParm("-altdeath")) + deathmatch = 2; + else if (M_CheckParm("-deathmatch")) + deathmatch = 1; + + switch (gamemode) + { + case retail: + //doom_sprintf(title, + // " " + // "The Ultimate DOOM Startup v%i.%i" + // " ", + // VERSION / 100, VERSION % 100); + doom_strcpy(title, " " "The Ultimate DOOM Startup v"); + doom_concat(title, doom_itoa(VERSION / 100, 10)); + doom_concat(title, "."); + doom_concat(title, doom_itoa(VERSION % 100, 10)); + doom_concat(title, " "); + break; + case shareware: + //doom_sprintf(title, + // " " + // "DOOM Shareware Startup v%i.%i" + // " ", + // VERSION / 100, VERSION % 100); + doom_strcpy(title, " " "DOOM Shareware Startup v"); + doom_concat(title, doom_itoa(VERSION / 100, 10)); + doom_concat(title, "."); + doom_concat(title, doom_itoa(VERSION % 100, 10)); + doom_concat(title, " "); + break; + case registered: + //doom_sprintf(title, + // " " + // "DOOM Registered Startup v%i.%i" + // " ", + // VERSION / 100, VERSION % 100); + doom_strcpy(title, " " "DOOM Registered Startup v"); + doom_concat(title, doom_itoa(VERSION / 100, 10)); + doom_concat(title, "."); + doom_concat(title, doom_itoa(VERSION % 100, 10)); + doom_concat(title, " "); + break; + case commercial: + //doom_sprintf(title, + // " " + // "DOOM 2: Hell on Earth v%i.%i" + // " ", + // VERSION / 100, VERSION % 100); + doom_strcpy(title, " " "DOOM 2: Hell on Earth v"); + doom_concat(title, doom_itoa(VERSION / 100, 10)); + doom_concat(title, "."); + doom_concat(title, doom_itoa(VERSION % 100, 10)); + doom_concat(title, " "); + break; + /*FIXME + case pack_plut: + sprintf (title, + " " + "DOOM 2: Plutonia Experiment v%i.%i" + " ", + VERSION/100,VERSION%100); + break; + case pack_tnt: + sprintf (title, + " " + "DOOM 2: TNT - Evilution v%i.%i" + " ", + VERSION/100,VERSION%100); + break; + */ + default: + //doom_sprintf(title, + // " " + // "Public DOOM - v%i.%i" + // " ", + // VERSION / 100, VERSION % 100); + doom_strcpy(title, " " "Public DOOM - v"); + doom_concat(title, doom_itoa(VERSION / 100, 10)); + doom_concat(title, "."); + doom_concat(title, doom_itoa(VERSION % 100, 10)); + doom_concat(title, " "); + break; + } + + //doom_print("%s\n", title); + doom_print(title); + doom_print("\n"); + + if (devparm) + doom_print(D_DEVSTR); + +#if 0 // [pd] Ignore cdrom + if (M_CheckParm("-cdrom")) + { + doom_print(D_CDROM); + mkdir("c:\\doomdata", 0); + doom_strcpy(basedefault, "c:/doomdata/default.cfg"); + } +#endif + + // turbo option + if ((p = M_CheckParm("-turbo"))) + { + int scale = 200; + extern int forwardmove[2]; + extern int sidemove[2]; + + if (p < myargc - 1) + scale = doom_atoi(myargv[p + 1]); + if (scale < 10) + scale = 10; + if (scale > 400) + scale = 400; + //doom_print("turbo scale: %i%%\n", scale); + doom_print("turbo scale: "); + doom_print(doom_itoa(scale, 10)); + doom_print("%%\n"); + forwardmove[0] = forwardmove[0] * scale / 100; + forwardmove[1] = forwardmove[1] * scale / 100; + sidemove[0] = sidemove[0] * scale / 100; + sidemove[1] = sidemove[1] * scale / 100; + } + + // add any files specified on the command line with -file wadfile + // to the wad list + // + // convenience hack to allow -wart e m to add a wad file + // prepend a tilde to the filename so wadfile will be reloadable + p = M_CheckParm("-wart"); + if (p) + { + myargv[p][4] = 'p'; // big hack, change to -warp + + // Map name handling. + switch (gamemode) + { + case shareware: + case retail: + case registered: + //doom_sprintf(file, "~"DEVMAPS"E%cM%c.wad", myargv[p + 1][0], myargv[p + 2][0]); + doom_strcpy(file, "~"DEVMAPS"E"); + doom_concat(file, doom_ctoa(myargv[p + 1][0])); + doom_concat(file, "M"); + doom_concat(file, doom_ctoa(myargv[p + 2][0])); + doom_concat(file, ".wad"); + + //doom_print("Warping to Episode %s, Map %s.\n", myargv[p + 1], myargv[p + 2]); + doom_print("Warping to Episode "); + doom_print(myargv[p + 1]); + doom_print(", Map "); + doom_print(myargv[p + 2]); + doom_print(".\n"); + break; + + case commercial: + default: + p = doom_atoi(myargv[p + 1]); + if (p < 10) + { + //doom_sprintf(file, "~"DEVMAPS"cdata/map0%i.wad", p); + doom_strcpy(file, "~"DEVMAPS"cdata/map0"); + doom_concat(file, doom_itoa(p, 10)); + doom_concat(file, ".wad"); + } + else + { + //doom_sprintf(file, "~"DEVMAPS"cdata/map%i.wad", p); + doom_strcpy(file, "~"DEVMAPS"cdata/map"); + doom_concat(file, doom_itoa(p, 10)); + doom_concat(file, ".wad"); + } + break; + } + D_AddFile(file); + } + + p = M_CheckParm("-file"); + if (p) + { + // the parms after p are wadfile/lump names, + // until end of parms or another - preceded parm + modifiedgame = true; // homebrew levels + while (++p != myargc && myargv[p][0] != '-') + D_AddFile(myargv[p]); + } + + p = M_CheckParm("-playdemo"); + + if (!p) + p = M_CheckParm("-timedemo"); + + if (p && p < myargc - 1) + { + //doom_sprintf(file, "%s.lmp", myargv[p + 1]); + doom_strcpy(file, myargv[p + 1]); + doom_concat(file, ".lmp"); + D_AddFile(file); + //doom_print("Playing demo %s.lmp.\n", myargv[p + 1]); + doom_print("Playing demo "); + doom_print(myargv[p + 1]); + doom_print(".lmp.\n"); + } + + // get skill / episode / map from parms + startskill = sk_medium; + startepisode = 1; + startmap = 1; + autostart = false; + + + p = M_CheckParm("-skill"); + if (p && p < myargc - 1) + { + startskill = myargv[p + 1][0] - '1'; + autostart = true; + } + + p = M_CheckParm("-episode"); + if (p && p < myargc - 1) + { + startepisode = myargv[p + 1][0] - '0'; + startmap = 1; + autostart = true; + } + + p = M_CheckParm("-timer"); + if (p && p < myargc - 1 && deathmatch) + { + int time; + time = doom_atoi(myargv[p + 1]); + //doom_print("Levels will end after %d minute", time); + doom_print("Levels will end after "); + doom_print(doom_itoa(time, 10)); + doom_print(" minute"); + if (time > 1) + doom_print("s"); + doom_print(".\n"); + } + + p = M_CheckParm("-avg"); + if (p && p < myargc - 1 && deathmatch) + doom_print("Austin Virtual Gaming: Levels will end after 20 minutes\n"); + + p = M_CheckParm("-warp"); + if (p && p < myargc - 1) + { + if (gamemode == commercial) + startmap = doom_atoi(myargv[p + 1]); + else + { + startepisode = myargv[p + 1][0] - '0'; + startmap = myargv[p + 2][0] - '0'; + } + autostart = true; + } + + // init subsystems + doom_print("V_Init: allocate screens.\n"); + V_Init(); + + doom_print("M_LoadDefaults: Load system defaults.\n"); + M_LoadDefaults(); // load before initing other systems + + doom_print("Z_Init: Init zone memory allocation daemon. \n"); + Z_Init(); + + doom_print("W_Init: Init WADfiles.\n"); + W_InitMultipleFiles(wadfiles); + + + // Check for -file in shareware + if (modifiedgame) + { + // These are the lumps that will be checked in IWAD, + // if any one is not present, execution will be aborted. + char name[23][8] = + { + "e2m1","e2m2","e2m3","e2m4","e2m5","e2m6","e2m7","e2m8","e2m9", + "e3m1","e3m3","e3m3","e3m4","e3m5","e3m6","e3m7","e3m8","e3m9", + "dphoof","bfgga0","heada1","cybra1","spida1d1" + }; + int i; + + if (gamemode == shareware) + I_Error("Error: \nYou cannot -file with the shareware " + "version. Register!"); + + // Check for fake IWAD with right name, + // but w/o all the lumps of the registered version. + if (gamemode == registered) + for (i = 0; i < 23; i++) + if (W_CheckNumForName(name[i]) < 0) + I_Error("Error: \nThis is not the registered version."); + } + + // Iff additonal PWAD files are used, print modified banner + if (modifiedgame) + { + /*m*/doom_print( + "===========================================================================\n" + "ATTENTION: This version of DOOM has been modified. If you would like to\n" + "get a copy of the original game, call 1-800-IDGAMES or see the readme file.\n" + " You will not receive technical support for modified games.\n" + //" press enter to continue\n" + "===========================================================================\n" + ); + //getchar(); + } + + + // Check and print which version is executed. + switch (gamemode) + { + case shareware: + case indetermined: + doom_print( + "===========================================================================\n" + " Shareware!\n" + "===========================================================================\n" + ); + break; + case registered: + case retail: + case commercial: + doom_print( + "===========================================================================\n" + " Commercial product - do not distribute!\n" + " Please report software piracy to the SPA: 1-800-388-PIR8\n" + "===========================================================================\n" + ); + break; + + default: + // Ouch. + break; + } + + doom_print("M_Init: Init miscellaneous info.\n"); + M_Init(); + + doom_print("R_Init: Init DOOM refresh daemon - "); + R_Init(); + + doom_print("\nP_Init: Init Playloop state.\n"); + P_Init(); + + doom_print("I_Init: Setting up machine state.\n"); + I_Init(); + + doom_print("D_CheckNetGame: Checking network game status.\n"); + D_CheckNetGame(); + + doom_print("S_Init: Setting up sound.\n"); + S_Init(snd_SfxVolume /* *8 */, snd_MusicVolume /* *8*/); + + doom_print("HU_Init: Setting up heads up display.\n"); + HU_Init(); + + doom_print("ST_Init: Init status bar.\n"); + ST_Init(); + + // check for a driver that wants intermission stats +#if 0 // [pd] Unsure how to test this + p = M_CheckParm("-statcopy"); + if (p && p < myargc - 1) + { + // for statistics driver + extern void* statcopy; + + statcopy = (void*)atoll(myargv[p + 1]); + doom_print("External statistics registered.\n"); + } +#endif + + // start the apropriate game based on parms + p = M_CheckParm("-record"); + + if (p && p < myargc - 1) + { + G_RecordDemo(myargv[p + 1]); + autostart = true; + } + + p = M_CheckParm("-playdemo"); + if (p && p < myargc - 1) + { + singledemo = true; // quit after one demo + G_DeferedPlayDemo(myargv[p + 1]); + D_DoomLoop(); // never returns + } + + p = M_CheckParm("-timedemo"); + if (p && p < myargc - 1) + { + G_TimeDemo(myargv[p + 1]); + D_DoomLoop(); // never returns + } + + p = M_CheckParm("-loadgame"); + if (p && p < myargc - 1) + { +#if 0 // [pd] We don't support the cdrom flag + if (M_CheckParm("-cdrom")) + { + //doom_sprintf(file, "c:\\doomdata\\"SAVEGAMENAME"%c.dsg", myargv[p + 1][0]); + } + else +#endif + { + //doom_sprintf(file, SAVEGAMENAME"%c.dsg", myargv[p + 1][0]); + doom_strcpy(file, SAVEGAMENAME); + doom_concat(file, doom_ctoa(myargv[p + 1][0])); + doom_concat(file, ".dsg"); + } + G_LoadGame(file); + } + + + if (gameaction != ga_loadgame) + { + if (autostart || netgame) + G_InitNew(startskill, startepisode, startmap); + else + D_StartTitle(); // start up intro loop + + } + + // D_DoomLoop (); // never returns [ddps] Called by app + + if (demorecording) + G_BeginRecording(); + + if (M_CheckParm("-debugfile")) + { + char filename[20]; + //doom_sprintf(filename, "debug%i.txt", consoleplayer); + doom_strcpy(filename, "debug"); + doom_concat(filename, doom_itoa(consoleplayer, 10)); + doom_concat(filename, ".txt"); + //doom_print("debug output to: %s\n", filename); + doom_print("debug output to: "); + doom_print(filename); + doom_print("\n"); + debugfile = doom_open(filename, "w"); + } + + I_InitGraphics(); +} diff --git a/src/DOOM/d_main.h b/src/DOOM/d_main.h new file mode 100644 index 0000000..9bac67d --- /dev/null +++ b/src/DOOM/d_main.h @@ -0,0 +1,59 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// System specific interface stuff. +// +//----------------------------------------------------------------------------- + +#ifndef __D_MAIN__ +#define __D_MAIN__ + + +#include "d_event.h" + + +#define MAXWADFILES 20 + + +extern char* wadfiles[MAXWADFILES]; + + +void D_AddFile(char* file); + +// +// D_DoomMain() +// Not a globally visible function, just included for source reference, +// calls all startup code, parses command line options. +// If not overrided by user input, calls N_AdvanceDemo. +// +void D_DoomMain(void); + +// Called by IO functions when input is detected. +void D_PostEvent(event_t* ev); + +// +// BASE LEVEL +// +void D_PageTicker(void); +void D_PageDrawer(void); +void D_AdvanceDemo(void); +void D_StartTitle(void); + + +#endif diff --git a/src/DOOM/d_net.c b/src/DOOM/d_net.c new file mode 100644 index 0000000..a6c2482 --- /dev/null +++ b/src/DOOM/d_net.c @@ -0,0 +1,856 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// DOOM Network game communication and protocol, +// all OS independend parts. +// +//----------------------------------------------------------------------------- + +#include "doom_config.h" + + + +#include "m_menu.h" +#include "i_system.h" +#include "i_video.h" +#include "i_net.h" +#include "g_game.h" +#include "doomdef.h" +#include "doomstat.h" + + +// +// NETWORKING +// +// gametic is the tic about to (or currently being) run +// maketic is the tick that hasn't had control made for it yet +// nettics[] has the maketics for all players +// +// a gametic cannot be run until nettics[] > gametic for all players +// + +#define NCMD_EXIT 0x80000000 +#define NCMD_RETRANSMIT 0x40000000 +#define NCMD_SETUP 0x20000000 +#define NCMD_KILL 0x10000000 // kill game +#define NCMD_CHECKSUM 0x0fffffff + +#define RESENDCOUNT 10 +#define PL_DRONE 0x80 // bit flag in doomdata->player + + +doomcom_t* doomcom; +doomdata_t* netbuffer; // points inside doomcom + +ticcmd_t localcmds[BACKUPTICS]; + +ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; +int nettics[MAXNETNODES]; +doom_boolean nodeingame[MAXNETNODES]; // set false as nodes leave game +doom_boolean remoteresend[MAXNETNODES]; // set when local needs tics +int resendto[MAXNETNODES]; // set when remote needs tics +int resendcount[MAXNETNODES]; + +int nodeforplayer[MAXPLAYERS]; + +int maketic; +int lastnettic; +int skiptics; +int ticdup; +int maxsend; // BACKUPTICS/(2*ticdup)-1 + +doom_boolean reboundpacket; +doomdata_t reboundstore; + +char exitmsg[80]; +int gametime; +int frametics[4]; +int frameon; +int frameskip[4]; +int oldnettics; + + +extern int viewangleoffset; +extern doom_boolean advancedemo; + + +void D_ProcessEvents(void); +void G_BuildTiccmd(ticcmd_t* cmd); +void D_DoAdvanceDemo(void); + + +// +// +// +int NetbufferSize(void) +{ + return (int)(long long)&(((doomdata_t*)0)->cmds[netbuffer->numtics]); +} + + +// +// Checksum +// +unsigned NetbufferChecksum(void) +{ + unsigned c; + int i, l; + + c = 0x1234567; + + // FIXME -endianess? +// #ifdef NORMALUNIX + return 0; // byte order problems +// #endif + + l = (NetbufferSize() - (int)(long long)&(((doomdata_t*)0)->retransmitfrom)) / 4; + for (i = 0; i < l; i++) + c += ((unsigned*)&netbuffer->retransmitfrom)[i] * (i + 1); + + return c & NCMD_CHECKSUM; +} + + +// +// +// +int ExpandTics(int low) +{ + int delta; + + delta = low - (maketic & 0xff); + + if (delta >= -64 && delta <= 64) + return (maketic & ~0xff) + low; + if (delta > 64) + return (maketic & ~0xff) - 256 + low; + if (delta < -64) + return (maketic & ~0xff) + 256 + low; + + //I_Error("Error: ExpandTics: strange value %i at maketic %i", low, maketic); + doom_strcpy(error_buf, "Error: ExpandTics: strange value "); + doom_concat(error_buf, doom_itoa(low, 10)); + doom_concat(error_buf, " at maketic "); + doom_concat(error_buf, doom_itoa(maketic, 10)); + I_Error(error_buf); + return 0; +} + + +// +// HSendPacket +// +void HSendPacket(int node, int flags) +{ + netbuffer->checksum = NetbufferChecksum() | flags; + + if (!node) + { + reboundstore = *netbuffer; + reboundpacket = true; + return; + } + + if (demoplayback) + return; + + if (!netgame) + I_Error("Error: Tried to transmit to another node"); + + doomcom->command = CMD_SEND; + doomcom->remotenode = node; + doomcom->datalength = NetbufferSize(); + + if (debugfile) + { + int i; + int realretrans; + if (netbuffer->checksum & NCMD_RETRANSMIT) + realretrans = ExpandTics(netbuffer->retransmitfrom); + else + realretrans = -1; + + { + //fprintf(debugfile, "send (%i + %i, R %i) [%i] ", + // ExpandTics(netbuffer->starttic), + // netbuffer->numtics, realretrans, doomcom->datalength); + doom_fprint(debugfile, "send ("); + doom_fprint(debugfile, doom_itoa(ExpandTics(netbuffer->starttic), 10)); + doom_fprint(debugfile, " + "); + doom_fprint(debugfile, doom_itoa(netbuffer->numtics, 10)); + doom_fprint(debugfile, ", R "); + doom_fprint(debugfile, doom_itoa(realretrans, 10)); + doom_fprint(debugfile, ") ["); + doom_fprint(debugfile, doom_itoa(doomcom->datalength, 10)); + doom_fprint(debugfile, "] "); + } + + for (i = 0; i < doomcom->datalength; i++) + { + //fprintf(debugfile, "%i ", ((byte*)netbuffer)[i]); + doom_fprint(debugfile, doom_itoa(((byte*)netbuffer)[i], 10)); + doom_fprint(debugfile, " "); + } + + doom_fprint(debugfile, "\n"); + } + + I_NetCmd(); +} + + +// +// HGetPacket +// Returns false if no packet is waiting +// +doom_boolean HGetPacket(void) +{ + if (reboundpacket) + { + *netbuffer = reboundstore; + doomcom->remotenode = 0; + reboundpacket = false; + return true; + } + + if (!netgame) + return false; + + if (demoplayback) + return false; + + doomcom->command = CMD_GET; + I_NetCmd(); + + if (doomcom->remotenode == -1) + return false; + + if (doomcom->datalength != NetbufferSize()) + { + if (debugfile) + { + //fprintf(debugfile, "bad packet length %i\n", doomcom->datalength); + doom_fprint(debugfile, "bad packet length "); + doom_fprint(debugfile, doom_itoa(doomcom->datalength, 10)); + doom_fprint(debugfile, "\n"); + } + return false; + } + + if (NetbufferChecksum() != (netbuffer->checksum & NCMD_CHECKSUM)) + { + if (debugfile) + { + doom_fprint(debugfile, "bad packet checksum\n"); + } + return false; + } + + if (debugfile) + { + int realretrans; + int i; + + if (netbuffer->checksum & NCMD_SETUP) + { + doom_fprint(debugfile, "setup packet\n"); + } + else + { + if (netbuffer->checksum & NCMD_RETRANSMIT) + realretrans = ExpandTics(netbuffer->retransmitfrom); + else + realretrans = -1; + + { + //fprintf(debugfile, "get %i = (%i + %i, R %i)[%i] ", + // doomcom->remotenode, + // ExpandTics(netbuffer->starttic), + // netbuffer->numtics, realretrans, doomcom->datalength); + doom_fprint(debugfile, "get "); + doom_fprint(debugfile, doom_itoa(doomcom->remotenode, 10)); + doom_fprint(debugfile, " = ("); + doom_fprint(debugfile, doom_itoa(ExpandTics(netbuffer->starttic), 10)); + doom_fprint(debugfile, " + "); + doom_fprint(debugfile, doom_itoa(netbuffer->numtics, 10)); + doom_fprint(debugfile, ", R "); + doom_fprint(debugfile, doom_itoa(realretrans, 10)); + doom_fprint(debugfile, ")["); + doom_fprint(debugfile, doom_itoa(doomcom->datalength, 10)); + doom_fprint(debugfile, "] "); + } + + for (i = 0; i < doomcom->datalength; i++) + { + //fprintf(debugfile, "%i ", ((byte*)netbuffer)[i]); + doom_fprint(debugfile, doom_itoa(((byte*)netbuffer)[i], 10)); + doom_fprint(debugfile, " "); + } + doom_fprint(debugfile, "\n"); + } + } + return true; +} + + +// +// GetPackets +// +void GetPackets(void) +{ + int netconsole; + int netnode; + ticcmd_t* src, *dest; + int realend; + int realstart; + + while (HGetPacket()) + { + if (netbuffer->checksum & NCMD_SETUP) + continue; // extra setup packet + + netconsole = netbuffer->player & ~PL_DRONE; + netnode = doomcom->remotenode; + + // to save bytes, only the low byte of tic numbers are sent + // Figure out what the rest of the bytes are + realstart = ExpandTics(netbuffer->starttic); + realend = (realstart + netbuffer->numtics); + + // check for exiting the game + if (netbuffer->checksum & NCMD_EXIT) + { + if (!nodeingame[netnode]) + continue; + nodeingame[netnode] = false; + playeringame[netconsole] = false; + doom_strcpy(exitmsg, "Player 1 left the game"); + exitmsg[7] += netconsole; + players[consoleplayer].message = exitmsg; + if (demorecording) + G_CheckDemoStatus(); + continue; + } + + // check for a remote game kill + if (netbuffer->checksum & NCMD_KILL) + I_Error("Error: Killed by network driver"); + + nodeforplayer[netconsole] = netnode; + + // check for retransmit request + if (resendcount[netnode] <= 0 + && (netbuffer->checksum & NCMD_RETRANSMIT)) + { + resendto[netnode] = ExpandTics(netbuffer->retransmitfrom); + if (debugfile) + { + //fprintf(debugfile, "retransmit from %i\n", resendto[netnode]); + doom_fprint(debugfile, "retransmit from "); + doom_fprint(debugfile, doom_itoa(resendto[netnode], 10)); + doom_fprint(debugfile, "\n"); + } + resendcount[netnode] = RESENDCOUNT; + } + else + resendcount[netnode]--; + + // check for out of order / duplicated packet + if (realend == nettics[netnode]) + continue; + + if (realend < nettics[netnode]) + { + if (debugfile) + { + //fprintf(debugfile, + // "out of order packet (%i + %i)\n", + // realstart, netbuffer->numtics); + doom_fprint(debugfile, "out of order packet ("); + doom_fprint(debugfile, doom_itoa(realstart, 10)); + doom_fprint(debugfile, " + "); + doom_fprint(debugfile, doom_itoa(netbuffer->numtics, 10)); + doom_fprint(debugfile, ")\n"); + } + continue; + } + + // check for a missed packet + if (realstart > nettics[netnode]) + { + // stop processing until the other system resends the missed tics + if (debugfile) + { + //fprintf(debugfile, + // "missed tics from %i (%i - %i)\n", + // netnode, realstart, nettics[netnode]); + doom_fprint(debugfile, "missed tics from "); + doom_fprint(debugfile, doom_itoa(netnode, 10)); + doom_fprint(debugfile, " ("); + doom_fprint(debugfile, doom_itoa(realstart, 10)); + doom_fprint(debugfile, " - "); + doom_fprint(debugfile, doom_itoa(nettics[netnode], 10)); + doom_fprint(debugfile, ")\n"); + } + remoteresend[netnode] = true; + continue; + } + + // update command store from the packet + { + int start; + + remoteresend[netnode] = false; + + start = nettics[netnode] - realstart; + src = &netbuffer->cmds[start]; + + while (nettics[netnode] < realend) + { + dest = &netcmds[netconsole][nettics[netnode] % BACKUPTICS]; + nettics[netnode]++; + *dest = *src; + src++; + } + } + } +} + + +// +// NetUpdate +// Builds ticcmds for console player, +// sends out a packet +// +void NetUpdate(void) +{ + int nowtime; + int newtics; + int i, j; + int realstart; + int gameticdiv; + + // check time + nowtime = I_GetTime() / ticdup; + newtics = nowtime - gametime; + gametime = nowtime; + + if (newtics <= 0) // nothing new to update + goto listen; + + if (skiptics <= newtics) + { + newtics -= skiptics; + skiptics = 0; + } + else + { + skiptics -= newtics; + newtics = 0; + } + + netbuffer->player = consoleplayer; + + // build new ticcmds for console player + gameticdiv = gametic / ticdup; + for (i = 0; i < newtics; i++) + { + I_StartTic(); + D_ProcessEvents(); + if (maketic - gameticdiv >= BACKUPTICS / 2 - 1) + break; // can't hold any more + + //doom_print ("mk:%i ",maketic); + G_BuildTiccmd(&localcmds[maketic % BACKUPTICS]); + maketic++; + } + + + if (singletics) + return; // singletic update is syncronous + + // send the packet to the other nodes + for (i = 0; i < doomcom->numnodes; i++) + if (nodeingame[i]) + { + netbuffer->starttic = realstart = resendto[i]; + netbuffer->numtics = maketic - realstart; + if (netbuffer->numtics > BACKUPTICS) + I_Error("Error: NetUpdate: netbuffer->numtics > BACKUPTICS"); + + resendto[i] = maketic - doomcom->extratics; + + for (j = 0; j < netbuffer->numtics; j++) + netbuffer->cmds[j] = + localcmds[(realstart + j) % BACKUPTICS]; + + if (remoteresend[i]) + { + netbuffer->retransmitfrom = nettics[i]; + HSendPacket(i, NCMD_RETRANSMIT); + } + else + { + netbuffer->retransmitfrom = 0; + HSendPacket(i, 0); + } + } + + // listen for other packets +listen: + GetPackets(); +} + + +// +// CheckAbort +// +void CheckAbort(void) +{ + event_t* ev; + int stoptic; + + stoptic = I_GetTime() + 2; + while (I_GetTime() < stoptic) + I_StartTic(); + + I_StartTic(); + for (; eventtail != eventhead + ; ) + { + ev = &events[eventtail]; + if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE) + I_Error("Error: Network game synchronization aborted."); + } + + eventtail++; + eventtail = (eventtail) & (MAXEVENTS - 1); +} + + +// +// D_ArbitrateNetStart +// +void D_ArbitrateNetStart(void) +{ + int i; + doom_boolean gotinfo[MAXNETNODES]; + + autostart = true; + doom_memset(gotinfo, 0, sizeof(gotinfo)); + + if (doomcom->consoleplayer) + { + // listen for setup info from key player + doom_print("listening for network start info...\n"); + while (1) + { + CheckAbort(); + if (!HGetPacket()) + continue; + if (netbuffer->checksum & NCMD_SETUP) + { + if (netbuffer->player != VERSION) + I_Error("Error: Different DOOM versions cannot play a net game!"); + startskill = netbuffer->retransmitfrom & 15; + deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6; + nomonsters = (netbuffer->retransmitfrom & 0x20) > 0; + respawnparm = (netbuffer->retransmitfrom & 0x10) > 0; + startmap = netbuffer->starttic & 0x3f; + startepisode = netbuffer->starttic >> 6; + return; + } + } + } + else + { + // key player, send the setup info + doom_print("sending network start info...\n"); + do + { + CheckAbort(); + for (i = 0; i < doomcom->numnodes; i++) + { + netbuffer->retransmitfrom = startskill; + if (deathmatch) + netbuffer->retransmitfrom |= (deathmatch << 6); + if (nomonsters) + netbuffer->retransmitfrom |= 0x20; + if (respawnparm) + netbuffer->retransmitfrom |= 0x10; + netbuffer->starttic = startepisode * 64 + startmap; + netbuffer->player = VERSION; + netbuffer->numtics = 0; + HSendPacket(i, NCMD_SETUP); + } + +#if 1 + for (i = 10; i && HGetPacket(); --i) + { + if ((netbuffer->player & 0x7f) < MAXNETNODES) + gotinfo[netbuffer->player & 0x7f] = true; + } +#else + while (HGetPacket()) + { + gotinfo[netbuffer->player & 0x7f] = true; + } +#endif + + for (i = 1; i < doomcom->numnodes; i++) + if (!gotinfo[i]) + break; + } while (i < doomcom->numnodes); + } +} + + +// +// D_CheckNetGame +// Works out player numbers among the net participants +// +void D_CheckNetGame(void) +{ + int i; + + for (i = 0; i < MAXNETNODES; i++) + { + nodeingame[i] = false; + nettics[i] = 0; + remoteresend[i] = false; // set when local needs tics + resendto[i] = 0; // which tic to start sending + } + + // I_InitNetwork sets doomcom and netgame + I_InitNetwork(); + if (doomcom->id != DOOMCOM_ID) + I_Error("Error: Doomcom buffer invalid!"); + + netbuffer = &doomcom->data; + consoleplayer = displayplayer = doomcom->consoleplayer; + if (netgame) + D_ArbitrateNetStart(); + + //doom_print("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", + // startskill, deathmatch, startmap, startepisode); + doom_print("startskill "); + doom_print(doom_itoa(startskill, 10)); + doom_print(" deathmatch: "); + doom_print(doom_itoa(deathmatch, 10)); + doom_print(" startmap: "); + doom_print(doom_itoa(startmap, 10)); + doom_print(" startepisode: "); + doom_print(doom_itoa(startepisode, 10)); + doom_print("\n"); + + // read values out of doomcom + ticdup = doomcom->ticdup; + maxsend = BACKUPTICS / (2 * ticdup) - 1; + if (maxsend < 1) + maxsend = 1; + + for (i = 0; i < doomcom->numplayers; i++) + playeringame[i] = true; + for (i = 0; i < doomcom->numnodes; i++) + nodeingame[i] = true; + + //doom_print("player %i of %i (%i nodes)\n", + // consoleplayer + 1, doomcom->numplayers, doomcom->numnodes); + doom_print("player "); + doom_print(doom_itoa(consoleplayer + 1, 10)); + doom_print(" of "); + doom_print(doom_itoa(doomcom->numplayers, 10)); + doom_print(" ("); + doom_print(doom_itoa(doomcom->numnodes, 10)); + doom_print(" nodes)\n"); +} + + +// +// D_QuitNetGame +// Called before quitting to leave a net game +// without hanging the other players +// +void D_QuitNetGame(void) +{ + int i, j; + + if (debugfile) + doom_close(debugfile); + + if (!netgame || !usergame || consoleplayer == -1 || demoplayback) + return; + + // send a bunch of packets for security + netbuffer->player = consoleplayer; + netbuffer->numtics = 0; + for (i = 0; i < 4; i++) + { + for (j = 1; j < doomcom->numnodes; j++) + if (nodeingame[j]) + HSendPacket(j, NCMD_EXIT); + I_WaitVBL(1); + } +} + + +// +// TryRunTics +// +void TryRunTics(void) +{ + int i; + int lowtic; + int entertic; + static int oldentertics; + int realtics; + int availabletics; + int counts; + int numplaying; + + // get real tics + entertic = I_GetTime() / ticdup; + realtics = entertic - oldentertics; + oldentertics = entertic; + + // get available tics + NetUpdate(); + + lowtic = DOOM_MAXINT; + numplaying = 0; + for (i = 0; i < doomcom->numnodes; i++) + { + if (nodeingame[i]) + { + numplaying++; + if (nettics[i] < lowtic) + lowtic = nettics[i]; + } + } + availabletics = lowtic - gametic / ticdup; + + // decide how many tics to run + if (realtics < availabletics - 1) + counts = realtics + 1; + else if (realtics < availabletics) + counts = realtics; + else + counts = availabletics; + + if (counts < 1) + counts = 1; + + frameon++; + + if (debugfile) + { + //fprintf(debugfile, + // "=======real: %i avail: %i game: %i\n", + // realtics, availabletics, counts); + doom_fprint(debugfile, "=======real: "); + doom_fprint(debugfile, doom_itoa(realtics, 10)); + doom_fprint(debugfile, " avail: "); + doom_fprint(debugfile, doom_itoa(availabletics, 10)); + doom_fprint(debugfile, " game: "); + doom_fprint(debugfile, doom_itoa(counts, 10)); + doom_fprint(debugfile, "\n"); + } + + if (!demoplayback) + { + // ideally nettics[0] should be 1 - 3 tics above lowtic + // if we are consistantly slower, speed up time + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + break; + if (consoleplayer == i) + { + // the key player does not adapt + } + else + { + if (nettics[0] <= nettics[nodeforplayer[i]]) + { + gametime--; + // doom_print ("-"); + } + frameskip[frameon & 3] = (oldnettics > nettics[nodeforplayer[i]]); + oldnettics = nettics[0]; + if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) + { + skiptics = 1; + // doom_print ("+"); + } + } + }// demoplayback + + // wait for new tics if needed + while (lowtic < gametic / ticdup + counts) + { + NetUpdate(); + lowtic = DOOM_MAXINT; + + for (i = 0; i < doomcom->numnodes; i++) + if (nodeingame[i] && nettics[i] < lowtic) + lowtic = nettics[i]; + + if (lowtic < gametic / ticdup) + I_Error("Error: TryRunTics: lowtic < gametic"); + + // don't stay in here forever -- give the menu a chance to work + if (I_GetTime() / ticdup - entertic >= 20) + { + M_Ticker(); + return; + } + } + + // run the count * ticdup dics + while (counts--) + { + for (i = 0; i < ticdup; i++) + { + if (gametic / ticdup > lowtic) + I_Error("Error: gametic>lowtic"); + if (advancedemo) + D_DoAdvanceDemo(); + M_Ticker(); + G_Ticker(); + gametic++; + + // modify command for duplicated tics + if (i != ticdup - 1) + { + ticcmd_t* cmd; + int buf; + int j; + + buf = (gametic / ticdup) % BACKUPTICS; + for (j = 0; j < MAXPLAYERS; j++) + { + cmd = &netcmds[j][buf]; + cmd->chatchar = 0; + if (cmd->buttons & BT_SPECIAL) + cmd->buttons = 0; + } + } + } + NetUpdate(); // check for new console commands + } +} diff --git a/src/DOOM/d_net.h b/src/DOOM/d_net.h new file mode 100644 index 0000000..23b72da --- /dev/null +++ b/src/DOOM/d_net.h @@ -0,0 +1,137 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Networking stuff. +// +//----------------------------------------------------------------------------- + +#ifndef __D_NET__ +#define __D_NET__ + + +#include "d_player.h" + + +// +// Network play related stuff. +// There is a data struct that stores network +// communication related stuff, and another +// one that defines the actual packets to +// be transmitted. +// + +#define DOOMCOM_ID 0x12345678l + +// Max computers/players in a game. +#define MAXNETNODES 8 + +// Networking and tick handling related. +#define BACKUPTICS 12 + + +typedef enum +{ + CMD_SEND = 1, + CMD_GET = 2 +} command_t; + + +// +// Network packet data. +// +typedef struct +{ + // High bit is retransmit request. + unsigned checksum; + // Only valid if NCMD_RETRANSMIT. + byte retransmitfrom; + + byte starttic; + byte player; + byte numtics; + ticcmd_t cmds[BACKUPTICS]; +} doomdata_t; + + +typedef struct +{ + // Supposed to be DOOMCOM_ID? + long id; + + // DOOM executes an int to execute commands. + short intnum; + // Communication between DOOM and the driver. + // Is CMD_SEND or CMD_GET. + short command; + // Is dest for send, set by get (-1 = no packet). + short remotenode; + + // Number of bytes in doomdata to be sent + short datalength; + + // Info common to all nodes. + // Console is allways node 0. + short numnodes; + // Flag: 1 = no duplication, 2-5 = dup for slow nets. + short ticdup; + // Flag: 1 = send a backup tic in every packet. + short extratics; + // Flag: 1 = deathmatch. + short deathmatch; + // Flag: -1 = new game, 0-5 = load savegame + short savegame; + short episode; // 1-3 + short map; // 1-9 + short skill; // 1-5 + + // Info specific to this node. + short consoleplayer; + short numplayers; + + // These are related to the 3-display mode, + // in which two drones looking left and right + // were used to render two additional views + // on two additional computers. + // Probably not operational anymore. + // 1 = left, 0 = center, -1 = right + short angleoffset; + // 1 = drone + short drone; + + // The packet data to be sent. + doomdata_t data; +} doomcom_t; + + +// Create any new ticcmds and broadcast to other players. +void NetUpdate(void); + +// Broadcasts special packets to other players +// to notify of game exit +void D_QuitNetGame(void); + +//? how many ticks to run? +void TryRunTics(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/d_player.h b/src/DOOM/d_player.h new file mode 100644 index 0000000..fbdd777 --- /dev/null +++ b/src/DOOM/d_player.h @@ -0,0 +1,209 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// +// +//----------------------------------------------------------------------------- + +#ifndef __D_PLAYER__ +#define __D_PLAYER__ + + +// The player data structure depends on a number +// of other structs: items (internal inventory), +// animation states (closely tied to the sprites +// used to represent them, unfortunately). +#include "d_items.h" +#include "p_pspr.h" + +// In addition, the player is just a special +// case of the generic moving object/actor. +#include "p_mobj.h" + +// Finally, for odd reasons, the player input +// is buffered within the player data struct, +// as commands per game tick. +#include "d_ticcmd.h" + + +// +// Player states. +// +typedef enum +{ + // Playing or camping. + PST_LIVE, + // Dead on the ground, view follows killer. + PST_DEAD, + // Ready to restart/respawn??? + PST_REBORN +} playerstate_t; + + +// +// Player internal flags, for cheats and debug. +// +typedef enum +{ + // No clipping, walk through barriers. + CF_NOCLIP = 1, + // No damage, no health loss. + CF_GODMODE = 2, + // Not really a cheat, just a debug aid. + CF_NOMOMENTUM = 4 +} cheat_t; + + +// +// Extended player object info: player_t +// +typedef struct player_s +{ + mobj_t* mo; + playerstate_t playerstate; + ticcmd_t cmd; + + // Determine POV, + // including viewpoint bobbing during movement. + // Focal origin above r.z + fixed_t viewz; + // Base height above floor for viewz. + fixed_t viewheight; + // Bob/squat speed. + fixed_t deltaviewheight; + // bounded/scaled total momentum. + fixed_t bob; + + // This is only used between levels, + // mo->health is used during levels. + int health; + int armorpoints; + // Armor type is 0-2. + int armortype; + + // Power ups. invinc and invis are tic counters. + int powers[NUMPOWERS]; + doom_boolean cards[NUMCARDS]; + doom_boolean backpack; + + // Frags, kills of other players. + int frags[MAXPLAYERS]; + weapontype_t readyweapon; + + // Is wp_nochange if not changing. + weapontype_t pendingweapon; + + doom_boolean weaponowned[NUMWEAPONS]; + int ammo[NUMAMMO]; + int maxammo[NUMAMMO]; + + // True if button down last tic. + int attackdown; + int usedown; + + // Bit flags, for cheats and debug. + // See cheat_t, above. + int cheats; + + // Refired shots are less accurate. + int refire; + + // For intermission stats. + int killcount; + int itemcount; + int secretcount; + + // Hint messages. + char* message; + + // For screen flashing (red or bright). + int damagecount; + int bonuscount; + + // Who did damage (0 for floors/ceilings). + mobj_t* attacker; + + // So gun flashes light up areas. + int extralight; + + // Current PLAYPAL, ??? + // can be set to REDCOLORMAP for pain, etc. + int fixedcolormap; + + // Player skin colorshift, + // 0-3 for which color to draw player. + int colormap; + + // Overlay view sprites (gun, etc). + pspdef_t psprites[NUMPSPRITES]; + + // True if secret level has been done. + doom_boolean didsecret; +} player_t; + + +// +// INTERMISSION +// Structure passed e.g. to WI_Start(wb) +// +typedef struct +{ + doom_boolean in; // whether the player is in game + + // Player stats, kills, collected items etc. + int skills; + int sitems; + int ssecret; + int stime; + int frags[4]; + int score; // current score on entry, modified on return +} wbplayerstruct_t; + + +typedef struct +{ + int epsd; // episode # (0-2) + + // if true, splash the secret level + doom_boolean didsecret; + + // previous and next levels, origin 0 + int last; + int next; + + int maxkills; + int maxitems; + int maxsecret; + int maxfrags; + + // the par time + int partime; + + // index of this player in game + int pnum; + + wbplayerstruct_t plyr[MAXPLAYERS]; +} wbstartstruct_t; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/d_textur.h b/src/DOOM/d_textur.h new file mode 100644 index 0000000..8728aa9 --- /dev/null +++ b/src/DOOM/d_textur.h @@ -0,0 +1,48 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Typedefs related to to textures etc., +// isolated here to make it easier separating modules. +// +//----------------------------------------------------------------------------- + +#ifndef __D_TEXTUR__ +#define __D_TEXTUR__ + + +#include "doomtype.h" + + +// +// Flats? +// +// a pic is an unmasked block of pixels +typedef struct +{ + byte width; + byte height; + byte data; +} pic_t; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/d_think.h b/src/DOOM/d_think.h new file mode 100644 index 0000000..bfdf39f --- /dev/null +++ b/src/DOOM/d_think.h @@ -0,0 +1,67 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// MapObj data. Map Objects or mobjs are actors, entities, +// thinker, take-your-pick... anything that moves, acts, or +// suffers state changes of more or less violent nature. +// +//----------------------------------------------------------------------------- + +#ifndef __D_THINK__ +#define __D_THINK__ + + +// +// Experimental stuff. +// To compile this as "ANSI C with classes" +// we will need to handle the various +// action functions cleanly. +// +typedef void (*actionf_v)(); +typedef void (*actionf_p1)(void*); +typedef void (*actionf_p2)(void*, void*); + +typedef union +{ + actionf_p1 acp1; + actionf_v acv; + actionf_p2 acp2; +} actionf_t; + + +// Historically, "think_t" is yet another +// function pointer to a routine to handle +// an actor. +typedef actionf_t think_t; + + +// Doubly linked list of actors. +typedef struct thinker_s +{ + struct thinker_s* prev; + struct thinker_s* next; + think_t function; +} thinker_t; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/d_ticcmd.h b/src/DOOM/d_ticcmd.h new file mode 100644 index 0000000..bc1623d --- /dev/null +++ b/src/DOOM/d_ticcmd.h @@ -0,0 +1,50 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// System specific interface stuff. +// +//----------------------------------------------------------------------------- + +#ifndef __D_TICCMD__ +#define __D_TICCMD__ + + +#include "doomtype.h" + + +// The data sampled per tick (single player) +// and transmitted to other peers (multiplayer). +// Mainly movements/button commands per game tick, +// plus a checksum for internal state consistency. +typedef struct +{ + char forwardmove; // *2048 for move + char sidemove; // *2048 for move + short angleturn; // <<16 for angle delta + short consistancy; // checks for net game + byte chatchar; + byte buttons; +} ticcmd_t; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/doom_config.h b/src/DOOM/doom_config.h new file mode 100644 index 0000000..93d71ee --- /dev/null +++ b/src/DOOM/doom_config.h @@ -0,0 +1,56 @@ +#ifndef __DOOM_CONFIG_H__ +#define __DOOM_CONFIG_H__ + + +#if defined(WIN32) +#define DOOM_WIN32 +#elif defined(__APPLE__) +#define DOOM_APPLE +#else +#define DOOM_LINUX +#endif + + +#include "DOOM.h" + + +#define doom_abs(x) ((x) < 0 ? -(x) : (x)) + + +extern char error_buf[260]; +extern int doom_flags; +extern doom_print_fn doom_print; +extern doom_malloc_fn doom_malloc; +extern doom_free_fn doom_free; +extern doom_open_fn doom_open; +extern doom_close_fn doom_close; +extern doom_read_fn doom_read; +extern doom_write_fn doom_write; +extern doom_seek_fn doom_seek; +extern doom_tell_fn doom_tell; +extern doom_eof_fn doom_eof; +extern doom_gettime_fn doom_gettime; +extern doom_exit_fn doom_exit; +extern doom_getenv_fn doom_getenv; + + +const char* doom_itoa(int i, int radix); +const char* doom_ctoa(char c); +const char* doom_ptoa(void* p); +void doom_memset(void* ptr, int value, int num); +void* doom_memcpy(void* destination, const void* source, int num); +int doom_fprint(void* handle, const char* str); +int doom_strlen(const char* str); +char* doom_concat(char* dst, const char* src); +char* doom_strcpy(char* destination, const char* source); +char* doom_strncpy(char* destination, const char* source, int num); +int doom_strcmp(const char* str1, const char* str2); +int doom_strncmp(const char* str1, const char* str2, int n); +int doom_strcasecmp(const char* str1, const char* str2); +int doom_strncasecmp(const char* str1, const char* str2, int n); +int doom_atoi(const char* str); +int doom_atox(const char* str); +int doom_toupper(int c); + + +#endif diff --git a/src/DOOM/doomdata.h b/src/DOOM/doomdata.h new file mode 100644 index 0000000..e414eca --- /dev/null +++ b/src/DOOM/doomdata.h @@ -0,0 +1,215 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// all external data is defined here +// most of the data is loaded into different structures at run time +// some internal structures shared by many modules are here +// +//----------------------------------------------------------------------------- + +#ifndef __DOOMDATA__ +#define __DOOMDATA__ + + +// The most basic types we use, portability. +#include "doomtype.h" + +// Some global defines, that configure the game. +#include "doomdef.h" + + +// +// Map level types. +// The following data structures define the persistent format +// used in the lumps of the WAD files. +// + +// Lump order in a map WAD: each map needs a couple of lumps +// to provide a complete scene geometry description. +enum +{ + ML_LABEL, // A separator, name, ExMx or MAPxx + ML_THINGS, // Monsters, items.. + ML_LINEDEFS, // LineDefs, from editing + ML_SIDEDEFS, // SideDefs, from editing + ML_VERTEXES, // Vertices, edited and BSP splits generated + ML_SEGS, // LineSegs, from LineDefs split by BSP + ML_SSECTORS, // SubSectors, list of LineSegs + ML_NODES, // BSP nodes + ML_SECTORS, // Sectors, from editing + ML_REJECT, // LUT, sector-sector visibility + ML_BLOCKMAP // LUT, motion clipping, walls/grid element +}; + + +// A single Vertex. +typedef struct +{ + short x; + short y; +} mapvertex_t; + + +// A SideDef, defining the visual appearance of a wall, +// by setting textures and offsets. +typedef struct +{ + short textureoffset; + short rowoffset; + char toptexture[8]; + char bottomtexture[8]; + char midtexture[8]; + // Front sector, towards viewer. + short sector; +} mapsidedef_t; + + +// A LineDef, as used for editing, and as input +// to the BSP builder. +typedef struct +{ + short v1; + short v2; + short flags; + short special; + short tag; + // sidenum[1] will be -1 if one sided + short sidenum[2]; +} maplinedef_t; + + +// +// LineDef attributes. +// + +// Solid, is an obstacle. +#define ML_BLOCKING 1 + +// Blocks monsters only. +#define ML_BLOCKMONSTERS 2 + +// Backside will not be present at all +// if not two sided. +#define ML_TWOSIDED 4 + +// If a texture is pegged, the texture will have +// the end exposed to air held constant at the +// top or bottom of the texture (stairs or pulled +// down things) and will move with a height change +// of one of the neighbor sectors. +// Unpegged textures allways have the first row of +// the texture at the top pixel of the line for both +// top and bottom textures (use next to windows). + +// upper texture unpegged +#define ML_DONTPEGTOP 8 + +// lower texture unpegged +#define ML_DONTPEGBOTTOM 16 + +// In AutoMap: don't map as two sided: IT'S A SECRET! +#define ML_SECRET 32 + +// Sound rendering: don't let sound cross two of these. +#define ML_SOUNDBLOCK 64 + +// Don't draw on the automap at all. +#define ML_DONTDRAW 128 + +// Set if already seen, thus drawn in automap. +#define ML_MAPPED 256 + + +// Sector definition, from editing. +typedef struct +{ + short floorheight; + short ceilingheight; + char floorpic[8]; + char ceilingpic[8]; + short lightlevel; + short special; + short tag; +} mapsector_t; + + +// SubSector, as generated by BSP. +typedef struct +{ + short numsegs; + // Index of first one, segs are stored sequentially. + short firstseg; +} mapsubsector_t; + + +// LineSeg, generated by splitting LineDefs +// using partition lines selected by BSP builder. +typedef struct +{ + short v1; + short v2; + short angle; + short linedef; + short side; + short offset; +} mapseg_t; + + +// +// BSP node structure. +// + +// Indicate a leaf. +#define NF_SUBSECTOR 0x8000 + +typedef struct +{ + // Partition line from (x,y) to x+dx,y+dy) + short x; + short y; + short dx; + short dy; + + // Bounding box for each child, + // clip against view frustum. + short bbox[2][4]; + + // If NF_SUBSECTOR its a subsector, + // else it's a node of another subtree. + unsigned short children[2]; +} mapnode_t; + + +// Thing definition, position, orientation and type, +// plus skill/visibility flags and attributes. +typedef struct +{ + short x; + short y; + short angle; + short type; + short options; +} mapthing_t; + + +#endif // __DOOMDATA__ + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/doomdef.c b/src/DOOM/doomdef.c new file mode 100644 index 0000000..8c9899c --- /dev/null +++ b/src/DOOM/doomdef.c @@ -0,0 +1,27 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// DoomDef - basic defines for DOOM, e.g. Version, game mode +// and skill level, and display parameters. +// +//----------------------------------------------------------------------------- + +#include "doom_config.h" + +#include "doomdef.h" diff --git a/src/DOOM/doomdef.h b/src/DOOM/doomdef.h new file mode 100644 index 0000000..43b7fc2 --- /dev/null +++ b/src/DOOM/doomdef.h @@ -0,0 +1,264 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Internally used data structures for virtually everything, +// key definitions, lots of other stuff. +// +//----------------------------------------------------------------------------- + +#ifndef __DOOMDEF__ +#define __DOOMDEF__ + + + + + + +// +// Global parameters/defines. +// +// DOOM version +enum +{ + VERSION = 110 +}; + + +// Game mode handling - identify IWAD version +// to handle IWAD dependend animations etc. +typedef enum +{ + 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. +} GameMode_t; + + +// Mission packs - might be useful for TC stuff? +typedef enum +{ + doom, // DOOM 1 + doom2, // DOOM 2 + pack_tnt, // TNT mission pack + pack_plut, // Plutonia pack + none +} GameMission_t; + + +// Identify language to use, software localization. +typedef enum +{ + english, + french, + german, + unknown +} Language_t; + + +// If rangecheck is undefined, +// most parameter validation debugging code will not be compiled +#define RANGECHECK + +// +// For resize of screen, at start of game. +// It will not work dynamically, see visplanes. +// +#define BASE_WIDTH 320 + +// It is educational but futile to change this +// scaling e.g. to 2. Drawing of status bar, +// menues etc. is tied to the scale implied +// by the graphics. +#define SCREEN_MUL 1 +#define INV_ASPECT_RATIO 0.625 // 0.75, ideally + +// Defines suck. C sucks. +// C++ might sucks for OOP, but it sure is a better C. +// So there. +// extern int SCREENWIDTH; +#define SCREENWIDTH 320 +#define SCREENHEIGHT 200 + +// The maximum number of players, multiplayer/networking. +#define MAXPLAYERS 4 + +// State updates, number of tics / second. +#if defined(DOOM_FAST_TICK) +#define TICKMUL 2 +#else +#define TICKMUL 1 +#endif +#define TICRATE (35 * TICKMUL) + + +// The current state of the game: whether we are +// playing, gazing at the intermission screen, +// the game final animation, or a demo. +typedef enum +{ + GS_LEVEL, + GS_INTERMISSION, + GS_FINALE, + GS_DEMOSCREEN +} gamestate_t; + + +// +// Difficulty/skill settings/filters. +// + +// Skill flags. +#define MTF_EASY 1 +#define MTF_NORMAL 2 +#define MTF_HARD 4 + +// Deaf monsters/do not react to sound. +#define MTF_AMBUSH 8 + + +typedef enum +{ + sk_baby, + sk_easy, + sk_medium, + sk_hard, + sk_nightmare +} skill_t; + + +// +// Key cards. +// +typedef enum +{ + it_bluecard, + it_yellowcard, + it_redcard, + it_blueskull, + it_yellowskull, + it_redskull, + NUMCARDS +} card_t; + + +// The defined weapons, +// including a marker indicating +// user has not changed weapon. +typedef enum +{ + wp_fist, + wp_pistol, + wp_shotgun, + wp_chaingun, + wp_missile, + wp_plasma, + wp_bfg, + wp_chainsaw, + wp_supershotgun, + NUMWEAPONS, + // No pending weapon change. + wp_nochange +} weapontype_t; + + +// Ammunition types defined. +typedef enum +{ + am_clip, // Pistol / chaingun ammo. + am_shell, // Shotgun / double barreled shotgun. + am_cell, // Plasma rifle, BFG. + am_misl, // Missile launcher. + NUMAMMO, + am_noammo // Unlimited for chainsaw / fist. +} ammotype_t; + + +// Power up artifacts. +typedef enum +{ + pw_invulnerability, + pw_strength, + pw_invisibility, + pw_ironfeet, + pw_allmap, + pw_infrared, + NUMPOWERS +} powertype_t; + + +// +// Power up durations, +// how many seconds till expiration, +// assuming TICRATE is 35 ticks/second. +// +typedef enum +{ + INVULNTICS = (30 * TICRATE), + INVISTICS = (60 * TICRATE), + INFRATICS = (120 * TICRATE), + IRONTICS = (60 * TICRATE) +} powerduration_t; + + +// +// DOOM keyboard definition. +// This is the stuff configured by Setup.Exe. +// Most key data are simple ascii (uppercased). +// +#define KEY_RIGHTARROW 0xae +#define KEY_LEFTARROW 0xac +#define KEY_UPARROW 0xad +#define KEY_DOWNARROW 0xaf +#define KEY_ESCAPE 27 +#define KEY_ENTER 13 +#define KEY_TAB 9 +#define KEY_F1 (0x80+0x3b) +#define KEY_F2 (0x80+0x3c) +#define KEY_F3 (0x80+0x3d) +#define KEY_F4 (0x80+0x3e) +#define KEY_F5 (0x80+0x3f) +#define KEY_F6 (0x80+0x40) +#define KEY_F7 (0x80+0x41) +#define KEY_F8 (0x80+0x42) +#define KEY_F9 (0x80+0x43) +#define KEY_F10 (0x80+0x44) +#define KEY_F11 (0x80+0x57) +#define KEY_F12 (0x80+0x58) + +#define KEY_BACKSPACE 127 +#define KEY_PAUSE 0xff + +#define KEY_EQUALS 0x3d +#define KEY_MINUS 0x2d + +#define KEY_RSHIFT (0x80+0x36) +#define KEY_RCTRL (0x80+0x1d) +#define KEY_RALT (0x80+0x38) + +#define KEY_LALT KEY_RALT + + +#endif // __DOOMDEF__ + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/doomstat.c b/src/DOOM/doomstat.c new file mode 100644 index 0000000..c03932c --- /dev/null +++ b/src/DOOM/doomstat.c @@ -0,0 +1,37 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Put all global tate variables here. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" +#include "doomstat.h" + + +// Game Mode - identify IWAD as shareware, retail etc. +GameMode_t gamemode = indetermined; +GameMission_t gamemission = doom; + +// Language. +Language_t language = english; + +// Set if homebrew PWAD stuff has been added. +doom_boolean modifiedgame; diff --git a/src/DOOM/doomstat.h b/src/DOOM/doomstat.h new file mode 100644 index 0000000..288bfa4 --- /dev/null +++ b/src/DOOM/doomstat.h @@ -0,0 +1,268 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// All the global variables that store the internal state. +// Theoretically speaking, the internal state of the engine +// should be found by looking at the variables collected +// here, and every relevant module will have to include +// this header file. +// In practice, things are a bit messy. +// +//----------------------------------------------------------------------------- + +#ifndef __D_STATE__ +#define __D_STATE__ + + +// We need globally shared data structures, +// for defining the global state variables. +#include "doomdata.h" +#include "d_net.h" + +// We need the playr data structure as well. +#include "d_player.h" + + +// ------------------------ +// Command line parameters. +// +extern doom_boolean nomonsters; // checkparm of -nomonsters +extern doom_boolean respawnparm; // checkparm of -respawn +extern doom_boolean fastparm; // checkparm of -fast +extern doom_boolean devparm; // DEBUG: launched with -devparm + + +// ----------------------------------------------------- +// Game Mode - identify IWAD as shareware, retail etc. +// +extern GameMode_t gamemode; +extern GameMission_t gamemission; + +// Set if homebrew PWAD stuff has been added. +extern doom_boolean modifiedgame; + + +// ------------------------------------------- +// Language. +extern Language_t language; + + +// ------------------------------------------- +// Selected skill type, map etc. +// + +// Defaults for menu, methinks. +extern skill_t startskill; +extern int startepisode; +extern int startmap; + +extern doom_boolean autostart; + +// Selected by user. +extern skill_t gameskill; +extern int gameepisode; +extern int gamemap; + +// Nightmare mode flag, single player. +extern doom_boolean respawnmonsters; + +// Netgame? Only true if >1 player. +extern doom_boolean netgame; + +// Flag: true only if started as net deathmatch. +// An enum might handle altdeath/cooperative better. +extern doom_boolean deathmatch; + +// ------------------------- +// Internal parameters for sound rendering. +// These have been taken from the DOS version, +// but are not (yet) supported with Linux +// (e.g. no sound volume adjustment with menu. + +// These are not used, but should be (menu). +// From m_menu.c: +// Sound FX volume has default, 0 - 15 +// Music volume has default, 0 - 15 +// These are multiplied by 8. +extern int snd_SfxVolume; // maximum volume for sound +extern int snd_MusicVolume; // maximum volume for music + +// Current music/sfx card - index useless +// w/o a reference LUT in a sound module. +// Ideally, this would use indices found +// in: /usr/include/linux/soundcard.h +extern int snd_MusicDevice; +extern int snd_SfxDevice; + +// Config file? Same disclaimer as above. +extern int snd_DesiredMusicDevice; +extern int snd_DesiredSfxDevice; + + +// ------------------------- +// Status flags for refresh. +// + +// Depending on view size - no status bar? +// Note that there is no way to disable the +// status bar explicitely. +extern doom_boolean statusbaractive; + +extern doom_boolean automapactive; // In AutoMap mode? +extern doom_boolean menuactive; // Menu overlayed? +extern doom_boolean paused; // Game Pause? + +extern doom_boolean viewactive; + +extern doom_boolean nodrawers; +extern doom_boolean noblit; + +extern int viewwindowx; +extern int viewwindowy; +extern int viewheight; +extern int viewwidth; +extern int scaledviewwidth; + + +// This one is related to the 3-screen display mode. +// ANG90 = left side, ANG270 = right +extern int viewangleoffset; + +// Player taking events, and displaying. +extern int consoleplayer; +extern int displayplayer; + + +// ------------------------------------- +// Scores, rating. +// Statistics on a given map, for intermission. +// +extern int totalkills; +extern int totalitems; +extern int totalsecret; + +// Timer, for scores. +extern int levelstarttic; // gametic at level start +extern int leveltime; // tics in game play for par + + +// -------------------------------------- +// DEMO playback/recording related stuff. +// No demo, there is a human player in charge? +// Disable save/end game? +extern doom_boolean usergame; + +//? +extern doom_boolean demoplayback; +extern doom_boolean demorecording; + +// Quit after playing a demo from cmdline. +extern doom_boolean singledemo; + +//? +extern gamestate_t gamestate; + + +//----------------------------- +// Internal parameters, fixed. +// These are set by the engine, and not changed +// according to user inputs. Partly load from +// WAD, partly set at startup time. + +extern int gametic; + + +// Bookkeeping on players - state. +extern player_t players[MAXPLAYERS]; + +// Alive? Disconnected? +extern doom_boolean playeringame[MAXPLAYERS]; + + +// Player spawn spots for deathmatch. +#define MAX_DM_STARTS 10 +extern mapthing_t deathmatchstarts[MAX_DM_STARTS]; +extern mapthing_t* deathmatch_p; + +// Player spawn spots. +extern mapthing_t playerstarts[MAXPLAYERS]; + +// Intermission stats. +// Parameters for world map / intermission. +extern wbstartstruct_t wminfo; + + +// LUT of ammunition limits for each kind. +// This doubles with BackPack powerup item. +extern int maxammo[NUMAMMO]; + + +//----------------------------------------- +// Internal parameters, used for engine. +// + +// File handling stuff. +extern char basedefault[1024]; +extern void* debugfile; + +// if true, load all graphics at level load +extern doom_boolean precache; + +// wipegamestate can be set to -1 +// to force a wipe on the next draw +extern gamestate_t wipegamestate; + +extern int mouseSensitivity; +//? +// debug flag to cancel adaptiveness +extern doom_boolean singletics; + +extern int bodyqueslot; + + +// Needed to store the number of the dummy sky flat. +// Used for rendering, +// as well as tracking projectiles etc. +extern int skyflatnum; + + +// Netgame stuff (buffers and pointers, i.e. indices). + +// This is ??? +extern doomcom_t* doomcom; + +// This points inside doomcom. +extern doomdata_t* netbuffer; + + +extern ticcmd_t localcmds[BACKUPTICS]; +extern int rndindex; + +extern int maketic; +extern int nettics[MAXNETNODES]; + +extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; +extern int ticdup; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/doomtype.h b/src/DOOM/doomtype.h new file mode 100644 index 0000000..f398dc3 --- /dev/null +++ b/src/DOOM/doomtype.h @@ -0,0 +1,65 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Simple basic typedefs, isolated here to make it easier +// separating modules. +// +//----------------------------------------------------------------------------- + +#ifndef __DOOMTYPE__ +#define __DOOMTYPE__ + + +// Fixed to use builtin bool type with C++. +#ifdef __cplusplus +typedef bool doom_boolean; +#else +#if !defined(false) && !defined(true) +typedef enum +{ + false, true +} doom_boolean; +#else +typedef int doom_boolean; +#endif +#endif + + +typedef unsigned char byte; + + +#define DOOM_MAXCHAR ((char)0x7f) +#define DOOM_MAXSHORT ((short)0x7fff) + +// Max pos 32-bit int. +#define DOOM_MAXINT ((int)0x7fffffff) +#define DOOM_MAXLONG ((long)0x7fffffff) +#define DOOM_MINCHAR ((char)0x80) +#define DOOM_MINSHORT ((short)0x8000) + +// Max negative 32-bit integer. +#define DOOM_MININT ((int)0x80000000) +#define DOOM_MINLONG ((long)0x80000000) + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/dstrings.c b/src/DOOM/dstrings.c new file mode 100644 index 0000000..86ec099 --- /dev/null +++ b/src/DOOM/dstrings.c @@ -0,0 +1,61 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Globally defined strings. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" +#include "dstrings.h" + + +char* endmsg[NUM_QUITMESSAGES + 1] = +{ + // DOOM1 + QUITMSG, + "please don't leave, there's more\ndemons to toast!", + "let's beat it -- this is turning\ninto a bloodbath!", + "i wouldn't leave if i were you.\ndos is much worse.", + "you're trying to say you like dos\nbetter than me, right?", + "don't leave yet -- there's a\ndemon around that corner!", + "ya know, next time you come in here\ni'm gonna toast ya.", + "go ahead and leave. see if i care.", + + // QuitDOOM II messages + "you want to quit?\nthen, thou hast lost an eighth!", + "don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!", + "get outta here and go back\nto your boring programs.", + "if i were your boss, i'd \n deathmatch ya in a minute!", + "look, bud. you leave now\nand you forfeit your body count!", + "just leave. when you come\nback, i'll be waiting with a bat.", + "you're lucky i don't smack\nyou for thinking about leaving.", + + // FinalDOOM? + "fuck you, pussy!\nget the fuck out!", + "you quit and i'll jizz\nin your cystholes!", + "if you leave, i'll make\nthe lord drink my jizz.", + "hey, ron! can we say\n'fuck' in the game?", + "i'd leave: this is just\nmore monsters and levels.\nwhat a load.", + "suck it down, asshole!\nyou're a fucking wimp!", + "don't quit now! we're \nstill spending your money!", + + // Internal debug. Different style, too. + "THIS IS NO MESSAGE!\nPage intentionally left blank." +}; diff --git a/src/DOOM/dstrings.h b/src/DOOM/dstrings.h new file mode 100644 index 0000000..7b2db42 --- /dev/null +++ b/src/DOOM/dstrings.h @@ -0,0 +1,66 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// +// $Log:$ +// +// DESCRIPTION: +// DOOM strings, by language. +// +//----------------------------------------------------------------------------- + +#ifndef __DSTRINGS__ +#define __DSTRINGS__ + + +// All important printed strings. +// Language selection (message strings). +// Use -DFRENCH etc. + +#ifdef FRENCH +#include "d_french.h" // Leave the extra space there, to throw off regex in PureDOOM.h creation +#else +#include "d_englsh.h" +#endif + + +// Misc. other strings. +#define SAVEGAMENAME "doomsav" + +// +// File locations, +// relative to current position. +// Path names are OS-sensitive. +// +#define DEVMAPS "devmaps" +#define DEVDATA "devdata" + +// Not done in french? + +// QuitDOOM messages +#define NUM_QUITMESSAGES 22 + + +extern char* endmsg[]; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/f_finale.c b/src/DOOM/f_finale.c new file mode 100644 index 0000000..87cc70a --- /dev/null +++ b/src/DOOM/f_finale.c @@ -0,0 +1,718 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Game completion, final screen animation. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "i_system.h" // Functions. +#include "m_swap.h" // Functions. +#include "z_zone.h" // Functions. +#include "v_video.h" // Functions. +#include "w_wad.h" // Functions. +#include "s_sound.h" // Functions. +#include "dstrings.h" // Data. +#include "sounds.h" // Data. +#include "doomstat.h" +#include "r_state.h" +#include "hu_stuff.h" + + +#define TEXTSPEED 3 +#define TEXTWAIT 250 + + +typedef struct +{ + char* name; + mobjtype_t type; +} castinfo_t; + + +// Stage of animation: +// 0 = text, 1 = art screen, 2 = character cast +int finalestage; + +int finalecount; + +char* e1text = E1TEXT; +char* e2text = E2TEXT; +char* e3text = E3TEXT; +char* e4text = E4TEXT; + +char* c1text = C1TEXT; +char* c2text = C2TEXT; +char* c3text = C3TEXT; +char* c4text = C4TEXT; +char* c5text = C5TEXT; +char* c6text = C6TEXT; + +char* p1text = P1TEXT; +char* p2text = P2TEXT; +char* p3text = P3TEXT; +char* p4text = P4TEXT; +char* p5text = P5TEXT; +char* p6text = P6TEXT; + +char* t1text = T1TEXT; +char* t2text = T2TEXT; +char* t3text = T3TEXT; +char* t4text = T4TEXT; +char* t5text = T5TEXT; +char* t6text = T6TEXT; + +char* finaletext; +char* finaleflat; + +castinfo_t castorder[] = { + {CC_ZOMBIE, MT_POSSESSED}, + {CC_SHOTGUN, MT_SHOTGUY}, + {CC_HEAVY, MT_CHAINGUY}, + {CC_IMP, MT_TROOP}, + {CC_DEMON, MT_SERGEANT}, + {CC_LOST, MT_SKULL}, + {CC_CACO, MT_HEAD}, + {CC_HELL, MT_KNIGHT}, + {CC_BARON, MT_BRUISER}, + {CC_ARACH, MT_BABY}, + {CC_PAIN, MT_PAIN}, + {CC_REVEN, MT_UNDEAD}, + {CC_MANCU, MT_FATSO}, + {CC_ARCH, MT_VILE}, + {CC_SPIDER, MT_SPIDER}, + {CC_CYBER, MT_CYBORG}, + {CC_HERO, MT_PLAYER}, + + {0,0} +}; + +int castnum; +int casttics; +state_t* caststate; +doom_boolean castdeath; +int castframes; +int castonmelee; +doom_boolean castattacking; + + +// +// F_StartCast +// +extern gamestate_t wipegamestate; +extern patch_t* hu_font[HU_FONTSIZE]; + + +void F_StartCast(void); +void F_CastTicker(void); +doom_boolean F_CastResponder(event_t* ev); +void F_CastDrawer(void); +void V_DrawPatchFlipped(int x, int y, int scrn, patch_t* patch); + + +// +// F_StartFinale +// +void F_StartFinale(void) +{ + gameaction = ga_nothing; + gamestate = GS_FINALE; + viewactive = false; + automapactive = false; + + // Okay - IWAD dependend stuff. + // This has been changed severly, and + // some stuff might have changed in the process. + switch (gamemode) + { + + // DOOM 1 - E1, E3 or E4, but each nine missions + case shareware: + case registered: + case retail: + { + S_ChangeMusic(mus_victor, true); + + switch (gameepisode) + { + case 1: + finaleflat = "FLOOR4_8"; + finaletext = e1text; + break; + case 2: + finaleflat = "SFLR6_1"; + finaletext = e2text; + break; + case 3: + finaleflat = "MFLR8_4"; + finaletext = e3text; + break; + case 4: + finaleflat = "MFLR8_3"; + finaletext = e4text; + break; + default: + // Ouch. + break; + } + break; + } + + // DOOM II and missions packs with E1, M34 + case commercial: + { + S_ChangeMusic(mus_read_m, true); + + switch (gamemap) + { + case 6: + finaleflat = "SLIME16"; + finaletext = c1text; + break; + case 11: + finaleflat = "RROCK14"; + finaletext = c2text; + break; + case 20: + finaleflat = "RROCK07"; + finaletext = c3text; + break; + case 30: + finaleflat = "RROCK17"; + finaletext = c4text; + break; + case 15: + finaleflat = "RROCK13"; + finaletext = c5text; + break; + case 31: + finaleflat = "RROCK19"; + finaletext = c6text; + break; + default: + // Ouch. + break; + } + break; + } + + + // Indeterminate. + default: + S_ChangeMusic(mus_read_m, true); + finaleflat = "F_SKY1"; // Not used anywhere else. + finaletext = c1text; // FIXME - other text, music? + break; + } + + finalestage = 0; + finalecount = 0; +} + + +doom_boolean F_Responder(event_t* event) +{ + if (finalestage == 2) + return F_CastResponder(event); + + return false; +} + + +// +// F_Ticker +// +void F_Ticker(void) +{ + int i; + + // check for skipping + if ((gamemode == commercial) + && (finalecount > 50)) + { + // go on to the next level + for (i = 0; i < MAXPLAYERS; i++) + if (players[i].cmd.buttons) + break; + + if (i < MAXPLAYERS) + { + if (gamemap == 30) + F_StartCast(); + else + gameaction = ga_worlddone; + } + } + + // advance animation + finalecount++; + + if (finalestage == 2) + { + F_CastTicker(); + return; + } + + if (gamemode == commercial) + return; + + if (!finalestage && finalecount > doom_strlen(finaletext) * TEXTSPEED + TEXTWAIT) + { + finalecount = 0; + finalestage = 1; + wipegamestate = -1; // force a wipe + if (gameepisode == 3) + S_StartMusic(mus_bunny); + } +} + + +// +// F_TextWrite +// +void F_TextWrite(void) +{ + byte* src; + byte* dest; + + int x, y, w; + int count; + char* ch; + int c; + int cx; + int cy; + + // erase the entire screen to a tiled background + src = W_CacheLumpName(finaleflat, PU_CACHE); + dest = screens[0]; + + for (y = 0; y < SCREENHEIGHT; y++) + { + for (x = 0; x < SCREENWIDTH / 64; x++) + { + doom_memcpy(dest, src + ((y & 63) << 6), 64); + dest += 64; + } + if (SCREENWIDTH & 63) + { + doom_memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); + dest += (SCREENWIDTH & 63); + } + } + + V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT); + + // draw some of the text onto the screen + cx = 10; + cy = 10; + ch = finaletext; + + count = (finalecount - 10) / TEXTSPEED; + if (count < 0) + count = 0; + for (; count; count--) + { + c = *ch++; + if (!c) + break; + if (c == '\n') + { + cx = 10; + cy += 11; + continue; + } + + c = doom_toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + if (cx + w > SCREENWIDTH) + break; + V_DrawPatch(cx, cy, 0, hu_font[c]); + cx += w; + } +} + + +// +// Final DOOM 2 animation +// Casting by id Software. +// in order of appearance +// +void F_StartCast(void) +{ + wipegamestate = -1; // force a screen wipe + castnum = 0; + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + casttics = caststate->tics; + castdeath = false; + finalestage = 2; + castframes = 0; + castonmelee = 0; + castattacking = false; + S_ChangeMusic(mus_evil, true); +} + + +// +// F_CastTicker +// +void F_CastTicker(void) +{ + int st; + int sfx; + + if (--casttics > 0) + return; // not time to change state yet + + if (caststate->tics == -1 || caststate->nextstate == S_NULL) + { + // switch from deathstate to next monster + castnum++; + castdeath = false; + if (castorder[castnum].name == 0) + castnum = 0; + if (mobjinfo[castorder[castnum].type].seesound) + S_StartSound(0, mobjinfo[castorder[castnum].type].seesound); + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + castframes = 0; + } + else + { + // just advance to next state in animation + if (caststate == &states[S_PLAY_ATK1]) + goto stopattack; // Oh, gross hack! + st = caststate->nextstate; + caststate = &states[st]; + castframes++; + + // sound hacks.... + switch (st) + { + case S_PLAY_ATK1: sfx = sfx_dshtgn; break; + case S_POSS_ATK2: sfx = sfx_pistol; break; + case S_SPOS_ATK2: sfx = sfx_shotgn; break; + case S_VILE_ATK2: sfx = sfx_vilatk; break; + case S_SKEL_FIST2: sfx = sfx_skeswg; break; + case S_SKEL_FIST4: sfx = sfx_skepch; break; + case S_SKEL_MISS2: sfx = sfx_skeatk; break; + case S_FATT_ATK8: + case S_FATT_ATK5: + case S_FATT_ATK2: sfx = sfx_firsht; break; + case S_CPOS_ATK2: + case S_CPOS_ATK3: + case S_CPOS_ATK4: sfx = sfx_shotgn; break; + case S_TROO_ATK3: sfx = sfx_claw; break; + case S_SARG_ATK2: sfx = sfx_sgtatk; break; + case S_BOSS_ATK2: + case S_BOS2_ATK2: + case S_HEAD_ATK2: sfx = sfx_firsht; break; + case S_SKULL_ATK2: sfx = sfx_sklatk; break; + case S_SPID_ATK2: + case S_SPID_ATK3: sfx = sfx_shotgn; break; + case S_BSPI_ATK2: sfx = sfx_plasma; break; + case S_CYBER_ATK2: + case S_CYBER_ATK4: + case S_CYBER_ATK6: sfx = sfx_rlaunc; break; + case S_PAIN_ATK3: sfx = sfx_sklatk; break; + default: sfx = 0; break; + } + + if (sfx) + S_StartSound(0, sfx); + } + + if (castframes == 12) + { + // go into attack frame + castattacking = true; + if (castonmelee) + caststate = &states[mobjinfo[castorder[castnum].type].meleestate]; + else + caststate = &states[mobjinfo[castorder[castnum].type].missilestate]; + castonmelee ^= 1; + if (caststate == &states[S_NULL]) + { + if (castonmelee) + caststate = + &states[mobjinfo[castorder[castnum].type].meleestate]; + else + caststate = + &states[mobjinfo[castorder[castnum].type].missilestate]; + } + } + + if (castattacking) + { + if (castframes == 24 + || caststate == &states[mobjinfo[castorder[castnum].type].seestate]) + { +stopattack: + castattacking = false; + castframes = 0; + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + } + } + + casttics = caststate->tics; + if (casttics == -1) + casttics = 15; +} + + +// +// F_CastResponder +// +doom_boolean F_CastResponder(event_t* ev) +{ + if (ev->type != ev_keydown) + return false; + + if (castdeath) + return true; // already in dying frames + + // go into death frame + castdeath = true; + caststate = &states[mobjinfo[castorder[castnum].type].deathstate]; + casttics = caststate->tics; + castframes = 0; + castattacking = false; + if (mobjinfo[castorder[castnum].type].deathsound) + S_StartSound(0, mobjinfo[castorder[castnum].type].deathsound); + + return true; +} + + +void F_CastPrint(char* text) +{ + char* ch; + int c; + int cx; + int w; + int width; + + // find width + ch = text; + width = 0; + + while (ch) + { + c = *ch++; + if (!c) + break; + c = doom_toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + width += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + width += w; + } + + // draw it + cx = 160 - width / 2; + ch = text; + while (ch) + { + c = *ch++; + if (!c) + break; + c = doom_toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + V_DrawPatch(cx, 180, 0, hu_font[c]); + cx += w; + } +} + + +// +// F_CastDrawer +// +void F_CastDrawer(void) +{ + spritedef_t* sprdef; + spriteframe_t* sprframe; + int lump; + doom_boolean flip; + patch_t* patch; + + // erase the entire screen to a background + V_DrawPatch(0, 0, 0, W_CacheLumpName("BOSSBACK", PU_CACHE)); + + F_CastPrint(castorder[castnum].name); + + // draw the current frame in the middle of the screen + sprdef = &sprites[caststate->sprite]; + sprframe = &sprdef->spriteframes[caststate->frame & FF_FRAMEMASK]; + lump = sprframe->lump[0]; + flip = (doom_boolean)sprframe->flip[0]; + + patch = W_CacheLumpNum(lump + firstspritelump, PU_CACHE); + if (flip) + V_DrawPatchFlipped(160, 170, 0, patch); + else + V_DrawPatch(160, 170, 0, patch); +} + + +// +// F_DrawPatchCol +// +void F_DrawPatchCol(int x, patch_t* patch, int col) +{ + column_t* column; + byte* source; + byte* dest; + byte* desttop; + int count; + + column = (column_t*)((byte*)patch + LONG(patch->columnofs[col])); + desttop = screens[0] + x; + + // step through the posts in a column + while (column->topdelta != 0xff) + { + source = (byte*)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + column = (column_t*)((byte*)column + column->length + 4); + } +} + + +// +// F_BunnyScroll +// +void F_BunnyScroll(void) +{ + int scrolled; + int x; + patch_t* p1; + patch_t* p2; + char name[10]; + int stage; + static int laststage; + + p1 = W_CacheLumpName("PFUB2", PU_LEVEL); + p2 = W_CacheLumpName("PFUB1", PU_LEVEL); + + V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT); + + scrolled = 320 - (finalecount - 230) / 2; + if (scrolled > 320) + scrolled = 320; + if (scrolled < 0) + scrolled = 0; + + for (x = 0; x < SCREENWIDTH; x++) + { + if (x + scrolled < 320) + F_DrawPatchCol(x, p1, x + scrolled); + else + F_DrawPatchCol(x, p2, x + scrolled - 320); + } + + if (finalecount < 1130) + return; + if (finalecount < 1180) + { + V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, + (SCREENHEIGHT - 8 * 8) / 2, 0, W_CacheLumpName("END0", PU_CACHE)); + laststage = 0; + return; + } + + stage = (finalecount - 1180) / 5; + if (stage > 6) + stage = 6; + if (stage > laststage) + { + S_StartSound(0, sfx_pistol); + laststage = stage; + } + + //doom_sprintf(name, "END%i", stage); + doom_strcpy(name, "END"); + doom_concat(name, doom_itoa(stage, 10)); + V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2, 0, W_CacheLumpName(name, PU_CACHE)); +} + + +// +// F_Drawer +// +void F_Drawer(void) +{ + if (finalestage == 2) + { + F_CastDrawer(); + return; + } + + if (!finalestage) + F_TextWrite(); + else + { + switch (gameepisode) + { + case 1: + if (gamemode == retail) + V_DrawPatch(0, 0, 0, + W_CacheLumpName("CREDIT", PU_CACHE)); + else + V_DrawPatch(0, 0, 0, + W_CacheLumpName("HELP2", PU_CACHE)); + break; + case 2: + V_DrawPatch(0, 0, 0, + W_CacheLumpName("VICTORY2", PU_CACHE)); + break; + case 3: + F_BunnyScroll(); + break; + case 4: + V_DrawPatch(0, 0, 0, + W_CacheLumpName("ENDPIC", PU_CACHE)); + break; + } + } +} diff --git a/src/DOOM/f_finale.h b/src/DOOM/f_finale.h new file mode 100644 index 0000000..c922336 --- /dev/null +++ b/src/DOOM/f_finale.h @@ -0,0 +1,52 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// +// +//----------------------------------------------------------------------------- + +#ifndef __F_FINALE__ +#define __F_FINALE__ + + +#include "doomtype.h" +#include "d_event.h" + + +// +// FINALE +// + +// Called by main loop. +doom_boolean F_Responder(event_t* ev); + +// Called by main loop. +void F_Ticker(void); + +// Called by main loop. +void F_Drawer(void); + +void F_StartFinale(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/f_wipe.c b/src/DOOM/f_wipe.c new file mode 100644 index 0000000..6b02a03 --- /dev/null +++ b/src/DOOM/f_wipe.c @@ -0,0 +1,258 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Mission begin melt/wipe screen special effect. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "z_zone.h" +#include "i_video.h" +#include "v_video.h" +#include "m_random.h" +#include "doomdef.h" +#include "f_wipe.h" + + +// +// SCREEN WIPE PACKAGE +// + +// when zero, stop the wipe +static doom_boolean go = 0; + +static byte* wipe_scr_start; +static byte* wipe_scr_end; +static byte* wipe_scr; + +static int* y; + + +void wipe_shittyColMajorXform(short* array, int width, int height) +{ + int x; + int y; + short* dest; + + dest = (short*)Z_Malloc(width * height * sizeof(short), PU_STATIC, 0); + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + dest[x * height + y] = array[y * width + x]; + + doom_memcpy(array, dest, width * height * 2); + + Z_Free(dest); +} + + +int wipe_initColorXForm(int width, int height, int ticks) +{ + doom_memcpy(wipe_scr, wipe_scr_start, width * height); + return 0; +} + + +int wipe_doColorXForm(int width, int height, int ticks) +{ + doom_boolean changed; + byte* w; + byte* e; + int newval; + + changed = false; + w = wipe_scr; + e = wipe_scr_end; + + while (w != wipe_scr + width * height) + { + if (*w != *e) + { + if (*w > *e) + { + newval = *w - ticks; + if (newval < *e) + *w = *e; + else + *w = newval; + changed = true; + } + else if (*w < *e) + { + newval = *w + ticks; + if (newval > *e) + *w = *e; + else + *w = newval; + changed = true; + } + } + w++; + e++; + } + + return !changed; +} + + +int wipe_exitColorXForm(int width, int height, int ticks) +{ + return 0; +} + + +int wipe_initMelt(int width, int height, int ticks) +{ + int i, r; + + // copy start screen to main screen + doom_memcpy(wipe_scr, wipe_scr_start, width * height); + + // makes this wipe faster (in theory) + // to have stuff in column-major format + wipe_shittyColMajorXform((short*)wipe_scr_start, width / 2, height); + wipe_shittyColMajorXform((short*)wipe_scr_end, width / 2, height); + + // setup initial column positions + // (y<0 => not ready to scroll yet) + y = (int*)Z_Malloc(width * sizeof(int), PU_STATIC, 0); + y[0] = -(M_Random() % 16); + for (i = 1; i < width; i++) + { + r = (M_Random() % 3) - 1; + y[i] = y[i - 1] + r; + if (y[i] > 0) y[i] = 0; + else if (y[i] == -16) y[i] = -15; + } + + return 0; +} + + +int wipe_doMelt(int width, int height, int ticks) +{ + int i; + int j; + int dy; + int idx; + + short* s; + short* d; + doom_boolean done = true; + + width /= 2; + + while (ticks--) + { + for (i = 0; i < width; i++) + { + if (y[i] < 0) + { + y[i]++; done = false; + } + else if (y[i] < height) + { + dy = (y[i] < 16) ? y[i] + 1 : 8; + if (y[i] + dy >= height) dy = height - y[i]; + s = &((short*)wipe_scr_end)[i * height + y[i]]; + d = &((short*)wipe_scr)[y[i] * width + i]; + idx = 0; + for (j = dy; j; j--) + { + d[idx] = *(s++); + idx += width; + } + y[i] += dy; + s = &((short*)wipe_scr_start)[i * height]; + d = &((short*)wipe_scr)[y[i] * width + i]; + idx = 0; + for (j = height - y[i]; j; j--) + { + d[idx] = *(s++); + idx += width; + } + done = false; + } + } + } + + return done; +} + + +int wipe_exitMelt(int width, int height, int ticks) +{ + Z_Free(y); + return 0; +} + + +int wipe_StartScreen(int x, int y, int width, int height) +{ + wipe_scr_start = screens[2]; + I_ReadScreen(wipe_scr_start); + return 0; +} + + +int wipe_EndScreen(int x, int y, int width, int height) +{ + wipe_scr_end = screens[3]; + I_ReadScreen(wipe_scr_end); + V_DrawBlock(x, y, 0, width, height, wipe_scr_start); // restore start scr. + return 0; +} + + +int wipe_ScreenWipe(int wipeno, int x, int y, int width, int height, int ticks) +{ + int rc; + static int (*wipes[])(int, int, int) = + { + wipe_initColorXForm, wipe_doColorXForm, wipe_exitColorXForm, + wipe_initMelt, wipe_doMelt, wipe_exitMelt + }; + + void V_MarkRect(int, int, int, int); + + // initial stuff + if (!go) + { + go = 1; + // wipe_scr = (byte *) Z_Malloc(width*height, PU_STATIC, 0); // DEBUG + wipe_scr = screens[0]; + (*wipes[wipeno * 3])(width, height, ticks); + } + + // do a piece of wipe-in + V_MarkRect(0, 0, width, height); + rc = (*wipes[wipeno * 3 + 1])(width, height, ticks); + // V_DrawBlock(x, y, 0, width, height, wipe_scr); // DEBUG + + // final stuff + if (rc) + { + go = 0; + (*wipes[wipeno * 3 + 2])(width, height, ticks); + } + + return !go; +} diff --git a/src/DOOM/f_wipe.h b/src/DOOM/f_wipe.h new file mode 100644 index 0000000..51b68b0 --- /dev/null +++ b/src/DOOM/f_wipe.h @@ -0,0 +1,53 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Mission start screen wipe/melt, special effects. +// +//----------------------------------------------------------------------------- + +#ifndef __F_WIPE_H__ +#define __F_WIPE_H__ + + +// +// SCREEN WIPE PACKAGE +// + +enum +{ + // simple gradual pixel change for 8-bit only + wipe_ColorXForm, + + // weird screen melt + wipe_Melt, + + wipe_NUMWIPES +}; + + +int wipe_StartScreen(int x, int y, int width, int height); +int wipe_EndScreen(int x, int y, int width, int height); +int wipe_ScreenWipe(int wipeno, int x, int y, int width, int height, int ticks); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/g_game.c b/src/DOOM/g_game.c new file mode 100644 index 0000000..01f68dd --- /dev/null +++ b/src/DOOM/g_game.c @@ -0,0 +1,1678 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: none +// +//----------------------------------------------------------------------------- + +#include "doom_config.h" + +#include "doomdef.h" +#include "doomstat.h" +#include "z_zone.h" +#include "f_finale.h" +#include "m_argv.h" +#include "m_misc.h" +#include "m_menu.h" +#include "m_random.h" +#include "i_system.h" +#include "p_setup.h" +#include "p_saveg.h" +#include "p_tick.h" +#include "d_main.h" +#include "wi_stuff.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "am_map.h" +#include "v_video.h" // Needs access to LFB. +#include "w_wad.h" +#include "p_local.h" +#include "s_sound.h" +#include "dstrings.h" // Data. +#include "sounds.h" +#include "r_data.h" // SKY handling - still the wrong place. +#include "r_sky.h" +#include "g_game.h" + + +#define SAVEGAMESIZE 0x2c000 +#define SAVESTRINGSIZE 24 +#define MAXPLMOVE (forwardmove[1]) +#define TURBOTHRESHOLD 0x32 +#define SLOWTURNTICS 6 +#define NUMKEYS 256 +#define BODYQUESIZE 32 +#define VERSIONSIZE 16 +#define DEMOMARKER 0x80 + + +// Prototypes +doom_boolean G_CheckDemoStatus(void); +void G_ReadDemoTiccmd(ticcmd_t* cmd); +void G_WriteDemoTiccmd(ticcmd_t* cmd); +void G_PlayerReborn(int player); +void G_InitNew(skill_t skill, int episode, int map); +void G_DoReborn(int playernum); +void G_DoLoadLevel(void); +void G_DoNewGame(void); +void G_DoLoadGame(void); +void G_DoPlayDemo(void); +void G_DoCompleted(void); +void G_DoWorldDone(void); +void G_DoSaveGame(void); +void P_SpawnPlayer(mapthing_t* mthing); +void R_ExecuteSetViewSize(void); + + +gameaction_t gameaction; +gamestate_t gamestate; +skill_t gameskill; +doom_boolean respawnmonsters; +int gameepisode; +int gamemap; + +doom_boolean paused; +doom_boolean sendpause; // send a pause event next tic +doom_boolean sendsave; // send a save event next tic +doom_boolean usergame; // ok to save / end game + +doom_boolean timingdemo; // if true, exit with report on completion +doom_boolean nodrawers; // for comparative timing purposes +doom_boolean noblit; // for comparative timing purposes +int starttime; // for comparative timing purposes + +doom_boolean viewactive; + +doom_boolean deathmatch; // only if started as net death +doom_boolean netgame; // only true if packets are broadcast +doom_boolean playeringame[MAXPLAYERS]; +player_t players[MAXPLAYERS]; + +int consoleplayer; // player taking events and displaying +int displayplayer; // view being displayed +int gametic; +int levelstarttic; // gametic at level start +int totalkills, totalitems, totalsecret; // for intermission + +char demoname[32]; +doom_boolean demorecording; +doom_boolean demoplayback; +doom_boolean netdemo; +byte* demobuffer; +byte* demo_p; +byte* demoend; +doom_boolean singledemo; // quit after playing a demo from cmdline + +doom_boolean precache = true; // if true, load all graphics at start + +wbstartstruct_t wminfo; // parms for world map / intermission + +short consistancy[MAXPLAYERS][BACKUPTICS]; + +byte* savebuffer; + +// +// controls (have defaults) +// +int key_right; +int key_left; + +int key_up; +int key_down; +int key_strafeleft; +int key_straferight; +int key_fire; +int key_use; +int key_strafe; +int key_speed; + +int mousebfire; +int mousebstrafe; +int mousebforward; +int mousemove; + +int joybfire; +int joybstrafe; +int joybuse; +int joybspeed; + +fixed_t forwardmove[2] = { 0x19, 0x32 }; +fixed_t sidemove[2] = { 0x18, 0x28 }; +fixed_t angleturn[3] = { 640, 1280, 320 }; // + slow turn + +doom_boolean gamekeydown[NUMKEYS]; +int turnheld; // for accelerative turning + +doom_boolean mousearray[4]; +doom_boolean* mousebuttons = &mousearray[1]; // allow [-1] + +// mouse values are used once +int mousex; +int mousey; + +int dclicktime; +int dclickstate; +int dclicks; +int dclicktime2; +int dclickstate2; +int dclicks2; + +// joystick values are repeated +int joyxmove; +int joyymove; +doom_boolean joyarray[5]; +doom_boolean* joybuttons = &joyarray[1]; // allow [-1] + +int savegameslot; +char savedescription[32]; + +mobj_t* bodyque[BODYQUESIZE]; +int bodyqueslot; + +void* statcopy; // for statistics driver + +// DOOM Par Times +int pars[4][10] = +{ + {0}, + {0,30,75,120,90,165,180,180,30,165}, + {0,90,90,90,120,90,360,240,30,170}, + {0,90,45,90,150,90,90,165,30,135} +}; + +// DOOM II Par Times +int cpars[32] = +{ + 30,90,120,120,90,150,120,120,270,90, // 1-10 + 210,150,150,150,210,150,420,150,210,150, // 11-20 + 240,150,180,150,150,300,330,420,300,180, // 21-30 + 120,30 // 31-32 +}; + +doom_boolean secretexit; + +char savename[256]; + +skill_t d_skill; +int d_episode; +int d_map; + +char* defdemoname; + + +extern gamestate_t wipegamestate; +extern char* pagename; +extern doom_boolean setsizeneeded; + +// The sky texture to be used instead of the F_SKY1 dummy. +extern int skytexture; + + +int G_CmdChecksum(ticcmd_t* cmd) +{ + int i; + int sum = 0; + + for (i = 0; i < sizeof(*cmd) / 4 - 1; i++) + sum += ((int*)cmd)[i]; + + return sum; +} + + +// +// G_BuildTiccmd +// Builds a ticcmd from all of the available inputs +// or reads it from the demo buffer. +// If recording a demo, write it out +// +void G_BuildTiccmd(ticcmd_t* cmd) +{ + int i; + doom_boolean strafe; + doom_boolean bstrafe; + int speed; + int tspeed; + int forward; + int side; + + ticcmd_t* base; + + base = I_BaseTiccmd(); // empty, or external driver + doom_memcpy(cmd, base, sizeof(*cmd)); + + cmd->consistancy = + consistancy[consoleplayer][maketic % BACKUPTICS]; + + + strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] + || joybuttons[joybstrafe]; + + extern int always_run; + doom_boolean running = always_run ? (gamekeydown[key_speed] ? false : true) : (gamekeydown[key_speed] ? true : false); + speed = running || joybuttons[joybspeed]; + + forward = side = 0; + + // use two stage accelerative turning + // on the keyboard and joystick + if (joyxmove < 0 + || joyxmove > 0 + || gamekeydown[key_right] + || gamekeydown[key_left]) + turnheld += ticdup; + else + turnheld = 0; + + if (turnheld < SLOWTURNTICS) + tspeed = 2; // slow turn + else + tspeed = speed; + + // let movement keys cancel each other out + if (strafe) + { + if (gamekeydown[key_right]) + { + side += sidemove[speed]; + } + if (gamekeydown[key_left]) + { + side -= sidemove[speed]; + } + if (joyxmove > 0) + side += sidemove[speed]; + if (joyxmove < 0) + side -= sidemove[speed]; + + } + else + { + if (gamekeydown[key_right]) + cmd->angleturn -= angleturn[tspeed]; + if (gamekeydown[key_left]) + cmd->angleturn += angleturn[tspeed]; + if (joyxmove > 0) + cmd->angleturn -= angleturn[tspeed]; + if (joyxmove < 0) + cmd->angleturn += angleturn[tspeed]; + } + + if (gamekeydown[key_up]) + { + forward += forwardmove[speed]; + } + if (gamekeydown[key_down]) + { + forward -= forwardmove[speed]; + } + if (joyymove < 0) + forward += forwardmove[speed]; + if (joyymove > 0) + forward -= forwardmove[speed]; + if (gamekeydown[key_straferight]) + side += sidemove[speed]; + if (gamekeydown[key_strafeleft]) + side -= sidemove[speed]; + + // buttons + cmd->chatchar = HU_dequeueChatChar(); + + if (gamekeydown[key_fire] || mousebuttons[mousebfire] + || joybuttons[joybfire]) + cmd->buttons |= BT_ATTACK; + + if (gamekeydown[key_use] || joybuttons[joybuse]) + { + cmd->buttons |= BT_USE; + // clear double clicks if hit use button + dclicks = 0; + } + + // chainsaw overrides + for (i = 0; i < NUMWEAPONS - 1; i++) + if (gamekeydown['1' + i]) + { + cmd->buttons |= BT_CHANGE; + cmd->buttons |= i << BT_WEAPONSHIFT; + break; + } + + // mouse + if (mousebuttons[mousebforward]) + forward += forwardmove[speed]; + + // forward double click + if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1) + { + dclickstate = mousebuttons[mousebforward]; + if (dclickstate) + dclicks++; + if (dclicks == 2) + { + cmd->buttons |= BT_USE; + dclicks = 0; + } + else + dclicktime = 0; + } + else + { + dclicktime += ticdup; + if (dclicktime > 20) + { + dclicks = 0; + dclickstate = 0; + } + } + + // strafe double click + bstrafe = + mousebuttons[mousebstrafe] + || joybuttons[joybstrafe]; + if (bstrafe != dclickstate2 && dclicktime2 > 1) + { + dclickstate2 = bstrafe; + if (dclickstate2) + dclicks2++; + if (dclicks2 == 2) + { + cmd->buttons |= BT_USE; + dclicks2 = 0; + } + else + dclicktime2 = 0; + } + else + { + dclicktime2 += ticdup; + if (dclicktime2 > 20) + { + dclicks2 = 0; + dclickstate2 = 0; + } + } + + if (mousemove) + forward += mousey; + if (strafe) + side += mousex * 2; + else + cmd->angleturn -= mousex * 0x8; + + mousex = mousey = 0; + + if (forward > MAXPLMOVE) + forward = MAXPLMOVE; + else if (forward < -MAXPLMOVE) + forward = -MAXPLMOVE; + if (side > MAXPLMOVE) + side = MAXPLMOVE; + else if (side < -MAXPLMOVE) + side = -MAXPLMOVE; + + cmd->forwardmove += forward; + cmd->sidemove += side; + + // special buttons + if (sendpause) + { + sendpause = false; + cmd->buttons = BT_SPECIAL | BTS_PAUSE; + } + + if (sendsave) + { + sendsave = false; + cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot << BTS_SAVESHIFT); + } +} + + +// +// G_DoLoadLevel +// +void G_DoLoadLevel(void) +{ + int i; + + // Set the sky map. + // First thing, we have a dummy sky texture name, + // a flat. The data is in the WAD only because + // we look for an actual index, instead of simply + // setting one. + skyflatnum = R_FlatNumForName(SKYFLATNAME); + + // DOOM determines the sky texture to be used + // depending on the current episode, and the game version. + if ((gamemode == commercial) + || (gamemode == pack_tnt) + || (gamemode == pack_plut)) + { + skytexture = R_TextureNumForName("SKY3"); + if (gamemap < 12) + skytexture = R_TextureNumForName("SKY1"); + else + if (gamemap < 21) + skytexture = R_TextureNumForName("SKY2"); + } + + levelstarttic = gametic; // for time calculation + + if (wipegamestate == GS_LEVEL) + wipegamestate = -1; // force a wipe + + gamestate = GS_LEVEL; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].playerstate == PST_DEAD) + players[i].playerstate = PST_REBORN; + doom_memset(players[i].frags, 0, sizeof(players[i].frags)); + } + + P_SetupLevel(gameepisode, gamemap, 0, gameskill); + displayplayer = consoleplayer; // view the guy you are playing + starttime = I_GetTime(); + gameaction = ga_nothing; + Z_CheckHeap(); + + // clear cmd building stuff + doom_memset(gamekeydown, 0, sizeof(gamekeydown)); + joyxmove = joyymove = 0; + mousex = mousey = 0; + sendpause = sendsave = paused = false; + doom_memset(mousebuttons, 0, sizeof(*mousebuttons) * 3); + doom_memset(joybuttons, 0, sizeof(*joybuttons) * 4); +} + + +// +// G_Responder +// Get info needed to make ticcmd_ts for the players. +// +doom_boolean G_Responder(event_t* ev) +{ + // allow spy mode changes even during the demo + if (gamestate == GS_LEVEL && ev->type == ev_keydown + && ev->data1 == KEY_F12 && (singledemo || !deathmatch)) + { + // spy mode + do + { + displayplayer++; + if (displayplayer == MAXPLAYERS) + displayplayer = 0; + } while (!playeringame[displayplayer] && displayplayer != consoleplayer); + return true; + } + + // any other key pops up menu if in demos + if (gameaction == ga_nothing && !singledemo && + (demoplayback || gamestate == GS_DEMOSCREEN) + ) + { + if (ev->type == ev_keydown || + (ev->type == ev_mouse && ev->data1) || + (ev->type == ev_joystick && ev->data1)) + { + M_StartControlPanel(); + return true; + } + return false; + } + + if (gamestate == GS_LEVEL) + { +#if 0 + if (devparm && ev->type == ev_keydown && ev->data1 == ';') + { + G_DeathMatchSpawnPlayer(0); + return true; + } +#endif + if (HU_Responder(ev)) + return true; // chat ate the event + if (ST_Responder(ev)) + return true; // status window ate it + if (AM_Responder(ev)) + return true; // automap ate it + } + + if (gamestate == GS_FINALE) + { + if (F_Responder(ev)) + return true; // finale ate the event + } + + switch (ev->type) + { + case ev_keydown: + if (ev->data1 == KEY_PAUSE) + { + sendpause = true; + return true; + } + if (ev->data1 < NUMKEYS) + gamekeydown[ev->data1] = true; + return true; // eat key down events + + case ev_keyup: + if (ev->data1 < NUMKEYS) + gamekeydown[ev->data1] = false; + return false; // always let key up events filter down + + case ev_mouse: + mousebuttons[0] = ev->data1 & 1; + mousebuttons[1] = ev->data1 & 2; + mousebuttons[2] = ev->data1 & 4; + mousex = ev->data2 * (mouseSensitivity + 5) / 10; + mousey = ev->data3 * (mouseSensitivity + 5) / 10; + return true; // eat events + + case ev_joystick: + joybuttons[0] = ev->data1 & 1; + joybuttons[1] = ev->data1 & 2; + joybuttons[2] = ev->data1 & 4; + joybuttons[3] = ev->data1 & 8; + joyxmove = ev->data2; + joyymove = ev->data3; + return true; // eat events + + default: + break; + } + + return false; +} + + + +// +// G_Ticker +// Make ticcmd_ts for the players. +// +void G_Ticker(void) +{ + int i; + int buf; + ticcmd_t* cmd; + + // do player reborns if needed + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].playerstate == PST_REBORN) + G_DoReborn(i); + + // do things to change the game state + while (gameaction != ga_nothing) + { + switch (gameaction) + { + case ga_loadlevel: + G_DoLoadLevel(); + break; + case ga_newgame: + G_DoNewGame(); + break; + case ga_loadgame: + G_DoLoadGame(); + break; + case ga_savegame: + G_DoSaveGame(); + break; + case ga_playdemo: + G_DoPlayDemo(); + break; + case ga_completed: + G_DoCompleted(); + break; + case ga_victory: + F_StartFinale(); + break; + case ga_worlddone: + G_DoWorldDone(); + break; + case ga_screenshot: + M_ScreenShot(); + gameaction = ga_nothing; + break; + case ga_nothing: + break; + } + } + + // get commands, check consistancy, + // and build new consistancy check + buf = (gametic / ticdup) % BACKUPTICS; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + cmd = &players[i].cmd; + + doom_memcpy(cmd, &netcmds[i][buf], sizeof(ticcmd_t)); + + if (demoplayback) + G_ReadDemoTiccmd(cmd); + if (demorecording) + G_WriteDemoTiccmd(cmd); + + // check for turbo cheats + if (cmd->forwardmove > TURBOTHRESHOLD + && !(gametic & 31) && ((gametic >> 5) & 3) == i) + { + static char turbomessage[80]; + extern char* player_names[4]; + //doom_sprintf(turbomessage, "%s is turbo!", player_names[i]); + doom_strcpy(turbomessage, player_names[i]); + doom_concat(turbomessage, " is turbo!"); + players[consoleplayer].message = turbomessage; + } + + if (netgame && !netdemo && !(gametic % ticdup)) + { + if (gametic > BACKUPTICS + && consistancy[i][buf] != cmd->consistancy) + { + //I_Error("Error: consistency failure (%i should be %i)", + // cmd->consistancy, consistancy[i][buf]); + + doom_strcpy(error_buf, "Error: consistency failure ("); + doom_concat(error_buf, doom_itoa(cmd->consistancy, 10)); + doom_concat(error_buf, " should be "); + doom_concat(error_buf, doom_itoa(consistancy[i][buf], 10)); + doom_concat(error_buf, ")"); + I_Error(error_buf); + } + if (players[i].mo) + consistancy[i][buf] = players[i].mo->x; + else + consistancy[i][buf] = rndindex; + } + } + } + + // check for special buttons + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (players[i].cmd.buttons & BT_SPECIAL) + { + switch (players[i].cmd.buttons & BT_SPECIALMASK) + { + case BTS_PAUSE: + paused ^= 1; + if (paused) + S_PauseSound(); + else + S_ResumeSound(); + break; + + case BTS_SAVEGAME: + if (!savedescription[0]) + doom_strcpy(savedescription, "NET GAME"); + savegameslot = + (players[i].cmd.buttons & BTS_SAVEMASK) >> BTS_SAVESHIFT; + gameaction = ga_savegame; + break; + } + } + } + } + + // do main actions + switch (gamestate) + { + case GS_LEVEL: + P_Ticker(); + ST_Ticker(); + AM_Ticker(); + HU_Ticker(); + break; + + case GS_INTERMISSION: + WI_Ticker(); + break; + + case GS_FINALE: + F_Ticker(); + break; + + case GS_DEMOSCREEN: + D_PageTicker(); + break; + } +} + + +// +// PLAYER STRUCTURE FUNCTIONS +// also see P_SpawnPlayer in P_Things +// + +// +// G_InitPlayer +// Called at the start. +// Called by the game initialization functions. +// +void G_InitPlayer(int player) +{ + player_t* p; + + // set up the saved info + p = &players[player]; + + // clear everything else to defaults + G_PlayerReborn(player); +} + + +// +// G_PlayerFinishLevel +// Can when a player completes a level. +// +void G_PlayerFinishLevel(int player) +{ + player_t* p; + + p = &players[player]; + + doom_memset(p->powers, 0, sizeof(p->powers)); + doom_memset(p->cards, 0, sizeof(p->cards)); + p->mo->flags &= ~MF_SHADOW; // cancel invisibility + p->extralight = 0; // cancel gun flashes + p->fixedcolormap = 0; // cancel ir gogles + p->damagecount = 0; // no palette changes + p->bonuscount = 0; +} + + +// +// G_PlayerReborn +// Called after a player dies +// almost everything is cleared and initialized +// +void G_PlayerReborn(int player) +{ + player_t* p; + int i; + int frags[MAXPLAYERS]; + int killcount; + int itemcount; + int secretcount; + + doom_memcpy(frags, players[player].frags, sizeof(frags)); + killcount = players[player].killcount; + itemcount = players[player].itemcount; + secretcount = players[player].secretcount; + + p = &players[player]; + doom_memset(p, 0, sizeof(*p)); + + doom_memcpy(players[player].frags, frags, sizeof(players[player].frags)); + players[player].killcount = killcount; + players[player].itemcount = itemcount; + players[player].secretcount = secretcount; + + p->usedown = p->attackdown = true; // don't do anything immediately + p->playerstate = PST_LIVE; + p->health = MAXHEALTH; + p->readyweapon = p->pendingweapon = wp_pistol; + p->weaponowned[wp_fist] = true; + p->weaponowned[wp_pistol] = true; + p->ammo[am_clip] = 50; + + for (i = 0; i < NUMAMMO; i++) + p->maxammo[i] = maxammo[i]; +} + +// +// G_CheckSpot +// Returns false if the player cannot be respawned +// at the given mapthing_t spot +// because something is occupying it +// + +doom_boolean G_CheckSpot(int playernum, mapthing_t* mthing) +{ + fixed_t x; + fixed_t y; + subsector_t* ss; + unsigned an; + mobj_t* mo; + int i; + + if (!players[playernum].mo) + { + // first spawn of level, before corpses + for (i = 0; i < playernum; i++) + if (players[i].mo->x == mthing->x << FRACBITS + && players[i].mo->y == mthing->y << FRACBITS) + return false; + return true; + } + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + if (!P_CheckPosition(players[playernum].mo, x, y)) + return false; + + // flush an old corpse if needed + if (bodyqueslot >= BODYQUESIZE) + P_RemoveMobj(bodyque[bodyqueslot % BODYQUESIZE]); + bodyque[bodyqueslot % BODYQUESIZE] = players[playernum].mo; + bodyqueslot++; + + // spawn a teleport fog + ss = R_PointInSubsector(x, y); + an = (ANG45 * (mthing->angle / 45)) >> ANGLETOFINESHIFT; + + mo = P_SpawnMobj(x + 20 * finecosine[an], y + 20 * finesine[an] + , ss->sector->floorheight + , MT_TFOG); + + if (players[consoleplayer].viewz != 1) + S_StartSound(mo, sfx_telept); // don't start sound on first frame + + return true; +} + + +// +// G_DeathMatchSpawnPlayer +// Spawns a player at one of the random death match spots +// called at level load and each death +// +void G_DeathMatchSpawnPlayer(int playernum) +{ + int i, j; + int selections; + + selections = (int)(deathmatch_p - deathmatchstarts); + if (selections < 4) + { + //I_Error("Error: Only %i deathmatch spots, 4 required", selections); + + doom_strcpy(error_buf, "Error: Only "); + doom_concat(error_buf, doom_itoa(selections, 10)); + doom_concat(error_buf, " deathmatch spots, 4 required"); + I_Error(error_buf); + } + + for (j = 0; j < 20; j++) + { + i = P_Random() % selections; + if (G_CheckSpot(playernum, &deathmatchstarts[i])) + { + deathmatchstarts[i].type = playernum + 1; + P_SpawnPlayer(&deathmatchstarts[i]); + return; + } + } + + // no good spot, so the player will probably get stuck + P_SpawnPlayer(&playerstarts[playernum]); +} + +// +// G_DoReborn +// +void G_DoReborn(int playernum) +{ + int i; + + if (!netgame) + { + // reload the level from scratch + gameaction = ga_loadlevel; + } + else + { + // respawn at the start + + // first dissasociate the corpse + players[playernum].mo->player = 0; + + // spawn at random spot if in death match + if (deathmatch) + { + G_DeathMatchSpawnPlayer(playernum); + return; + } + + if (G_CheckSpot(playernum, &playerstarts[playernum])) + { + P_SpawnPlayer(&playerstarts[playernum]); + return; + } + + // try to spawn at one of the other players spots + for (i = 0; i < MAXPLAYERS; i++) + { + if (G_CheckSpot(playernum, &playerstarts[i])) + { + playerstarts[i].type = playernum + 1; // fake as other player + P_SpawnPlayer(&playerstarts[i]); + playerstarts[i].type = i + 1; // restore + return; + } + // he's going to be inside something. Too bad. + } + P_SpawnPlayer(&playerstarts[playernum]); + } +} + + +void G_ScreenShot(void) +{ + gameaction = ga_screenshot; +} + + +// +// G_DoCompleted +// +void G_ExitLevel(void) +{ + secretexit = false; + gameaction = ga_completed; +} + +// Here's for the german edition. +void G_SecretExitLevel(void) +{ + // IF NO WOLF3D LEVELS, NO SECRET EXIT! + if ((gamemode == commercial) + && (W_CheckNumForName("map31") < 0)) + secretexit = false; + else + secretexit = true; + gameaction = ga_completed; +} + +void G_DoCompleted(void) +{ + int i; + + gameaction = ga_nothing; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + G_PlayerFinishLevel(i); // take away cards and stuff + + if (automapactive) + AM_Stop(); + + if (gamemode != commercial) + switch (gamemap) + { + case 8: + gameaction = ga_victory; + return; + case 9: + for (i = 0; i < MAXPLAYERS; i++) + players[i].didsecret = true; + break; + } + + if ((gamemap == 8) + && (gamemode != commercial)) + { + // victory + gameaction = ga_victory; + return; + } + + if ((gamemap == 9) + && (gamemode != commercial)) + { + // exit secret level + for (i = 0; i < MAXPLAYERS; i++) + players[i].didsecret = true; + } + + + wminfo.didsecret = players[consoleplayer].didsecret; + wminfo.epsd = gameepisode - 1; + wminfo.last = gamemap - 1; + + // wminfo.next is 0 biased, unlike gamemap + if (gamemode == commercial) + { + if (secretexit) + switch (gamemap) + { + case 15: wminfo.next = 30; break; + case 31: wminfo.next = 31; break; + } + else + switch (gamemap) + { + case 31: + case 32: wminfo.next = 15; break; + default: wminfo.next = gamemap; + } + } + else + { + if (secretexit) + wminfo.next = 8; // go to secret level + else if (gamemap == 9) + { + // returning from secret level + switch (gameepisode) + { + case 1: + wminfo.next = 3; + break; + case 2: + wminfo.next = 5; + break; + case 3: + wminfo.next = 6; + break; + case 4: + wminfo.next = 2; + break; + } + } + else + wminfo.next = gamemap; // go to next level + } + + wminfo.maxkills = totalkills; + wminfo.maxitems = totalitems; + wminfo.maxsecret = totalsecret; + wminfo.maxfrags = 0; + if (gamemode == commercial) + wminfo.partime = 35 * cpars[gamemap - 1]; + else + wminfo.partime = 35 * pars[gameepisode][gamemap]; + wminfo.pnum = consoleplayer; + + for (i = 0; i < MAXPLAYERS; i++) + { + wminfo.plyr[i].in = playeringame[i]; + wminfo.plyr[i].skills = players[i].killcount; + wminfo.plyr[i].sitems = players[i].itemcount; + wminfo.plyr[i].ssecret = players[i].secretcount; + wminfo.plyr[i].stime = leveltime; + doom_memcpy(wminfo.plyr[i].frags, players[i].frags + , sizeof(wminfo.plyr[i].frags)); + } + + gamestate = GS_INTERMISSION; + viewactive = false; + automapactive = false; + + if (statcopy) + doom_memcpy(statcopy, &wminfo, sizeof(wminfo)); + + WI_Start(&wminfo); +} + + +// +// G_WorldDone +// +void G_WorldDone(void) +{ + gameaction = ga_worlddone; + + if (secretexit) + players[consoleplayer].didsecret = true; + + if (gamemode == commercial) + { + switch (gamemap) + { + case 15: + case 31: + if (!secretexit) + break; + case 6: + case 11: + case 20: + case 30: + F_StartFinale(); + break; + } + } +} + +void G_DoWorldDone(void) +{ + gamestate = GS_LEVEL; + gamemap = wminfo.next + 1; + G_DoLoadLevel(); + gameaction = ga_nothing; + viewactive = true; +} + + + +// +// G_InitFromSavegame +// Can be called by the startup code or the menu task. +// + +void G_LoadGame(char* name) +{ + doom_strcpy(savename, name); + gameaction = ga_loadgame; +} + + +void G_DoLoadGame(void) +{ + int length; + int i; + int a, b, c; + char vcheck[VERSIONSIZE]; + + gameaction = ga_nothing; + + length = M_ReadFile(savename, &savebuffer); + save_p = savebuffer + SAVESTRINGSIZE; + + // skip the description field + doom_memset(vcheck, 0, sizeof(vcheck)); + //doom_sprintf(vcheck, "version %i", VERSION); + doom_strcpy(vcheck, "version "); + doom_concat(vcheck, doom_itoa(VERSION, 10)); + if (doom_strcmp((const char*)save_p, (const char*)vcheck)) + return; // bad version + save_p += VERSIONSIZE; + + gameskill = *save_p++; + gameepisode = *save_p++; + gamemap = *save_p++; + for (i = 0; i < MAXPLAYERS; i++) + playeringame[i] = *save_p++; + + // load a base level + G_InitNew(gameskill, gameepisode, gamemap); + + // get the times + a = *save_p++; + b = *save_p++; + c = *save_p++; + leveltime = (a << 16) + (b << 8) + c; + + // dearchive all the modifications + P_UnArchivePlayers(); + P_UnArchiveWorld(); + P_UnArchiveThinkers(); + P_UnArchiveSpecials(); + + if (*save_p != 0x1d) + I_Error("Error: Bad savegame"); + + // done + Z_Free(savebuffer); + + if (setsizeneeded) + R_ExecuteSetViewSize(); + + // draw the pattern into the back screen + R_FillBackScreen(); +} + + +// +// G_SaveGame +// Called by the menu task. +// Description is a 24 byte text string +// +void G_SaveGame(int slot, char* description) +{ + savegameslot = slot; + doom_strcpy(savedescription, description); + sendsave = true; +} + +void G_DoSaveGame(void) +{ + char name[100]; + char name2[VERSIONSIZE]; + char* description; + int length; + int i; + +#if 0 + if (M_CheckParm("-cdrom")) + doom_sprintf(name, "c:\\doomdata\\"SAVEGAMENAME"%d.dsg", savegameslot); + else +#endif + { + //doom_sprintf(name, SAVEGAMENAME"%d.dsg", savegameslot); + doom_strcpy(name, SAVEGAMENAME); + doom_concat(name, doom_itoa(savegameslot, 10)); + doom_concat(name, ".dsg"); + } + description = savedescription; + + save_p = savebuffer = screens[1] + 0x4000; + + doom_memcpy(save_p, description, SAVESTRINGSIZE); + save_p += SAVESTRINGSIZE; + doom_memset(name2, 0, sizeof(name2)); + //doom_sprintf(name2, "version %i", VERSION); + doom_strcpy(name2, "version "); + doom_concat(name2, doom_itoa(VERSION, 10)); + doom_memcpy(save_p, name2, VERSIONSIZE); + save_p += VERSIONSIZE; + + *save_p++ = gameskill; + *save_p++ = gameepisode; + *save_p++ = gamemap; + for (i = 0; i < MAXPLAYERS; i++) + *save_p++ = playeringame[i]; + *save_p++ = leveltime >> 16; + *save_p++ = leveltime >> 8; + *save_p++ = leveltime; + + P_ArchivePlayers(); + P_ArchiveWorld(); + P_ArchiveThinkers(); + P_ArchiveSpecials(); + + *save_p++ = 0x1d; // consistancy marker + + length = (int)(save_p - savebuffer); + if (length > SAVEGAMESIZE) + I_Error("Error: Savegame buffer overrun"); + M_WriteFile(name, savebuffer, length); + gameaction = ga_nothing; + savedescription[0] = 0; + + players[consoleplayer].message = GGSAVED; + + // draw the pattern into the back screen + R_FillBackScreen(); +} + + +// +// G_InitNew +// Can be called by the startup code or the menu task, +// consoleplayer, displayplayer, playeringame[] should be set. +// + +void G_DeferedInitNew(skill_t skill, int episode, int map) +{ + d_skill = skill; + d_episode = episode; + d_map = map; + gameaction = ga_newgame; +} + + +void G_DoNewGame(void) +{ + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = 0; + respawnparm = false; + fastparm = false; + nomonsters = false; + consoleplayer = 0; + G_InitNew(d_skill, d_episode, d_map); + gameaction = ga_nothing; +} + +void G_InitNew(skill_t skill, int episode, int map) +{ + int i; + + if (paused) + { + paused = false; + S_ResumeSound(); + } + + + if (skill > sk_nightmare) + skill = sk_nightmare; + + + // This was quite messy with SPECIAL and commented parts. + // Supposedly hacks to make the latest edition work. + // It might not work properly. + if (episode < 1) + episode = 1; + + if (gamemode == retail) + { + if (episode > 4) + episode = 4; + } + else if (gamemode == shareware) + { + if (episode > 1) + episode = 1; // only start episode 1 on shareware + } + else + { + if (episode > 3) + episode = 3; + } + + + + if (map < 1) + map = 1; + + if ((map > 9) + && (gamemode != commercial)) + map = 9; + + M_ClearRandom(); + + if (skill == sk_nightmare || respawnparm) + respawnmonsters = true; + else + respawnmonsters = false; + + if (fastparm || (skill == sk_nightmare && gameskill != sk_nightmare)) + { + for (i = S_SARG_RUN1; i <= S_SARG_PAIN2; i++) + states[i].tics >>= 1; + mobjinfo[MT_BRUISERSHOT].speed = 20 * FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 20 * FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 20 * FRACUNIT; + } + else if (skill != sk_nightmare && gameskill == sk_nightmare) + { + for (i = S_SARG_RUN1; i <= S_SARG_PAIN2; i++) + states[i].tics <<= 1; + mobjinfo[MT_BRUISERSHOT].speed = 15 * FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 10 * FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 10 * FRACUNIT; + } + + + // force players to be initialized upon first level load + for (i = 0; i < MAXPLAYERS; i++) + players[i].playerstate = PST_REBORN; + + usergame = true; // will be set false if a demo + paused = false; + demoplayback = false; + automapactive = false; + viewactive = true; + gameepisode = episode; + gamemap = map; + gameskill = skill; + + viewactive = true; + + // set the sky map for the episode + if (gamemode == commercial) + { + skytexture = R_TextureNumForName("SKY3"); + if (gamemap < 12) + skytexture = R_TextureNumForName("SKY1"); + else + if (gamemap < 21) + skytexture = R_TextureNumForName("SKY2"); + } + else + switch (episode) + { + case 1: + skytexture = R_TextureNumForName("SKY1"); + break; + case 2: + skytexture = R_TextureNumForName("SKY2"); + break; + case 3: + skytexture = R_TextureNumForName("SKY3"); + break; + case 4: // Special Edition sky + skytexture = R_TextureNumForName("SKY4"); + break; + } + + G_DoLoadLevel(); +} + + +// +// DEMO RECORDING +// + +void G_ReadDemoTiccmd(ticcmd_t* cmd) +{ + if (*demo_p == DEMOMARKER) + { + // end of demo data stream + G_CheckDemoStatus(); + return; + } + cmd->forwardmove = ((signed char)*demo_p++); + cmd->sidemove = ((signed char)*demo_p++); + cmd->angleturn = ((unsigned char)*demo_p++) << 8; + cmd->buttons = (unsigned char)*demo_p++; +} + + +void G_WriteDemoTiccmd(ticcmd_t* cmd) +{ + if (gamekeydown['q']) // press q to end demo recording + G_CheckDemoStatus(); + *demo_p++ = cmd->forwardmove; + *demo_p++ = cmd->sidemove; + *demo_p++ = (cmd->angleturn + 128) >> 8; + *demo_p++ = cmd->buttons; + demo_p -= 4; + if (demo_p > demoend - 16) + { + // no more space + G_CheckDemoStatus(); + return; + } + + G_ReadDemoTiccmd(cmd); // make SURE it is exactly the same +} + + +// +// G_RecordDemo +// +void G_RecordDemo(char* name) +{ + int i; + int maxsize; + + usergame = false; + doom_strcpy(demoname, name); + doom_concat(demoname, ".lmp"); + maxsize = 0x20000; + i = M_CheckParm("-maxdemo"); + if (i && i < myargc - 1) + maxsize = doom_atoi(myargv[i + 1]) * 1024; + demobuffer = Z_Malloc(maxsize, PU_STATIC, 0); + demoend = demobuffer + maxsize; + + demorecording = true; +} + + +void G_BeginRecording(void) +{ + int i; + + demo_p = demobuffer; + + *demo_p++ = VERSION; + *demo_p++ = gameskill; + *demo_p++ = gameepisode; + *demo_p++ = gamemap; + *demo_p++ = deathmatch; + *demo_p++ = respawnparm; + *demo_p++ = fastparm; + *demo_p++ = nomonsters; + *demo_p++ = consoleplayer; + + for (i = 0; i < MAXPLAYERS; i++) + *demo_p++ = playeringame[i]; +} + + +// +// G_PlayDemo +// + + +void G_DeferedPlayDemo(char* name) +{ + defdemoname = name; + gameaction = ga_playdemo; +} + +void G_DoPlayDemo(void) +{ + skill_t skill; + int i, episode, map; + + gameaction = ga_nothing; + demobuffer = demo_p = W_CacheLumpName(defdemoname, PU_STATIC); + byte demo_version = *demo_p++; + if (demo_version != VERSION && + demo_version != 109) // Demos seem to run fine with version 109 + { + //doom_print("Demo is from a different game version! Demo Verson = %i, this version = %i\n", (int)demo_version, VERSION); + doom_print("Demo is from a different game version! Demo Verson = "); + doom_print(doom_itoa((int)demo_version, 10)); + doom_print(", this version = "); + doom_print(doom_itoa(VERSION, 10)); + doom_print("\n"); + gameaction = ga_nothing; + return; + } + + skill = *demo_p++; + episode = *demo_p++; + map = *demo_p++; + deathmatch = *demo_p++; + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + consoleplayer = *demo_p++; + + for (i = 0; i < MAXPLAYERS; i++) + playeringame[i] = *demo_p++; + if (playeringame[1]) + { + netgame = true; + netdemo = true; + } + + // don't spend a lot of time in loadlevel + precache = false; + G_InitNew(skill, episode, map); + precache = true; + + usergame = false; + demoplayback = true; +} + +// +// G_TimeDemo +// +void G_TimeDemo(char* name) +{ + nodrawers = M_CheckParm("-nodraw"); + noblit = M_CheckParm("-noblit"); + timingdemo = true; + singletics = true; + + defdemoname = name; + gameaction = ga_playdemo; +} + + +/* +=================== += += G_CheckDemoStatus += += Called after a death or level completion to allow demos to be cleaned up += Returns true if a new demo loop action will take place +=================== +*/ + +doom_boolean G_CheckDemoStatus(void) +{ + int endtime; + + if (timingdemo) + { + endtime = I_GetTime(); + //I_Error("Error: timed %i gametics in %i realtics", gametic + // , endtime - starttime); + + doom_strcpy(error_buf, "Error: timed "); + doom_concat(error_buf, doom_itoa(gametic, 10)); + doom_concat(error_buf, " gametics in "); + doom_concat(error_buf, doom_itoa(endtime - starttime, 10)); + doom_concat(error_buf, " realtics"); + I_Error(error_buf); + } + + if (demoplayback) + { + if (singledemo) + I_Quit(); + + Z_ChangeTag(demobuffer, PU_CACHE); + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = 0; + respawnparm = false; + fastparm = false; + nomonsters = false; + consoleplayer = 0; + D_AdvanceDemo(); + return true; + } + + if (demorecording) + { + *demo_p++ = DEMOMARKER; + M_WriteFile(demoname, demobuffer, (int)(demo_p - demobuffer)); + Z_Free(demobuffer); + demorecording = false; + //I_Error("Error: Demo %s recorded", demoname); + + doom_strcpy(error_buf, "Error: Demo "); + doom_concat(error_buf, demoname); + doom_concat(error_buf, " recorded"); + I_Error(error_buf); + } + + return false; +} diff --git a/src/DOOM/g_game.h b/src/DOOM/g_game.h new file mode 100644 index 0000000..1d6f2be --- /dev/null +++ b/src/DOOM/g_game.h @@ -0,0 +1,78 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Duh. +// +//----------------------------------------------------------------------------- + +#ifndef __G_GAME__ +#define __G_GAME__ + + +#include "doomdef.h" +#include "d_event.h" + + +// +// GAME +// +void G_DeathMatchSpawnPlayer(int playernum); + +void G_InitNew(skill_t skill, int episode, int map); + +// Can be called by the startup code or M_Responder. +// A normal game starts at map 1, +// but a warp test can start elsewhere +void G_DeferedInitNew(skill_t skill, int episode, int map); + +void G_DeferedPlayDemo(char* demo); + +// Can be called by the startup code or M_Responder, +// calls P_SetupLevel or W_EnterWorld. +void G_LoadGame(char* name); + +void G_DoLoadGame(void); + +// Called by M_Responder. +void G_SaveGame(int slot, char* description); + +// Only called by startup code. +void G_RecordDemo(char* name); + +void G_BeginRecording(void); + +void G_TimeDemo(char* name); +doom_boolean G_CheckDemoStatus(void); + +void G_ExitLevel(void); +void G_SecretExitLevel(void); + +void G_WorldDone(void); + +void G_Ticker(void); +doom_boolean G_Responder(event_t* ev); + +void G_ScreenShot(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/hu_lib.c b/src/DOOM/hu_lib.c new file mode 100644 index 0000000..43b1d9f --- /dev/null +++ b/src/DOOM/hu_lib.c @@ -0,0 +1,329 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: heads-up text and input code +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "doomdef.h" +#include "v_video.h" +#include "m_swap.h" +#include "hu_lib.h" +#include "r_local.h" +#include "r_draw.h" + + +// doom_boolean : whether the screen is always erased +#define noterased viewwindowx + + +extern doom_boolean automapactive; // in AM_map.c + + +void HUlib_init(void) +{ +} + + +void HUlib_clearTextLine(hu_textline_t* t) +{ + t->len = 0; + t->l[0] = 0; + t->needsupdate = true; +} + + +void HUlib_initTextLine(hu_textline_t* t, int x, int y, patch_t** f, int sc) +{ + t->x = x; + t->y = y; + t->f = f; + t->sc = sc; + HUlib_clearTextLine(t); +} + + +doom_boolean HUlib_addCharToTextLine(hu_textline_t* t, char ch) +{ + if (t->len == HU_MAXLINELENGTH) + return false; + else + { + t->l[t->len++] = ch; + t->l[t->len] = 0; + t->needsupdate = 4; + return true; + } +} + + +doom_boolean HUlib_delCharFromTextLine(hu_textline_t* t) +{ + if (!t->len) return false; + else + { + t->l[--t->len] = 0; + t->needsupdate = 4; + return true; + } +} + + +void HUlib_drawTextLine(hu_textline_t* l, doom_boolean drawcursor) +{ + int i; + int w; + int x; + unsigned char c; + + // draw the new stuff + x = l->x; + for (i = 0; i < l->len; i++) + { + c = doom_toupper(l->l[i]); + if (c != ' ' + && c >= l->sc + && c <= '_') + { + w = SHORT(l->f[c - l->sc]->width); + if (x + w > SCREENWIDTH) + break; + V_DrawPatchDirect(x, l->y, FG, l->f[c - l->sc]); + x += w; + } + else + { + x += 4; + if (x >= SCREENWIDTH) + break; + } + } + + // draw the cursor if requested + if (drawcursor + && x + SHORT(l->f['_' - l->sc]->width) <= SCREENWIDTH) + { + V_DrawPatchDirect(x, l->y, FG, l->f['_' - l->sc]); + } +} + + +// sorta called by HU_Erase and just better darn get things straight +void HUlib_eraseTextLine(hu_textline_t* l) +{ + int lh; + int y; + int yoffset; + static doom_boolean lastautomapactive = true; + + // Only erases when NOT in automap and the screen is reduced, + // and the text must either need updating or refreshing + // (because of a recent change back from the automap) + + if (!automapactive && + viewwindowx && l->needsupdate) + { + lh = SHORT(l->f[0]->height) + 1; + for (y = l->y, yoffset = y * SCREENWIDTH; y < l->y + lh; y++, yoffset += SCREENWIDTH) + { + if (y < viewwindowy || y >= viewwindowy + viewheight) + R_VideoErase(yoffset, SCREENWIDTH); // erase entire line + else + { + R_VideoErase(yoffset, viewwindowx); // erase left border + R_VideoErase(yoffset + viewwindowx + viewwidth, viewwindowx); + // erase right border + } + } + } + + lastautomapactive = automapactive; + if (l->needsupdate) l->needsupdate--; +} + + +void HUlib_initSText(hu_stext_t* s, + int x, + int y, + int h, + patch_t** font, + int startchar, + doom_boolean* on) +{ + int i; + + s->h = h; + s->on = on; + s->laston = true; + s->cl = 0; + for (i = 0; i < h; i++) + HUlib_initTextLine(&s->l[i], + x, y - i * (SHORT(font[0]->height) + 1), + font, startchar); +} + + +void HUlib_addLineToSText(hu_stext_t* s) +{ + + int i; + + // add a clear line + if (++s->cl == s->h) + s->cl = 0; + HUlib_clearTextLine(&s->l[s->cl]); + + // everything needs updating + for (i = 0; i < s->h; i++) + s->l[i].needsupdate = 4; +} + + +void HUlib_addMessageToSText(hu_stext_t* s, char* prefix, char* msg) +{ + HUlib_addLineToSText(s); + if (prefix) + while (*prefix) + HUlib_addCharToTextLine(&s->l[s->cl], *(prefix++)); + + while (*msg) + HUlib_addCharToTextLine(&s->l[s->cl], *(msg++)); +} + + +void HUlib_drawSText(hu_stext_t* s) +{ + int i, idx; + hu_textline_t* l; + + if (!*s->on) + return; // if not on, don't draw + + // draw everything + for (i = 0; i < s->h; i++) + { + idx = s->cl - i; + if (idx < 0) + idx += s->h; // handle queue of lines + + l = &s->l[idx]; + + // need a decision made here on whether to skip the draw + HUlib_drawTextLine(l, false); // no cursor, please + } +} + + +void HUlib_eraseSText(hu_stext_t* s) +{ + int i; + + for (i = 0; i < s->h; i++) + { + if (s->laston && !*s->on) + s->l[i].needsupdate = 4; + HUlib_eraseTextLine(&s->l[i]); + } + s->laston = *s->on; +} + + +void HUlib_initIText(hu_itext_t* it, + int x, + int y, + patch_t** font, + int startchar, + doom_boolean* on) +{ + it->lm = 0; // default left margin is start of text + it->on = on; + it->laston = true; + HUlib_initTextLine(&it->l, x, y, font, startchar); +} + + +// The following deletion routines adhere to the left margin restriction +void HUlib_delCharFromIText(hu_itext_t* it) +{ + if (it->l.len != it->lm) + HUlib_delCharFromTextLine(&it->l); +} + + +void HUlib_eraseLineFromIText(hu_itext_t* it) +{ + while (it->lm != it->l.len) + HUlib_delCharFromTextLine(&it->l); +} + + +// Resets left margin as well +void HUlib_resetIText(hu_itext_t* it) +{ + it->lm = 0; + HUlib_clearTextLine(&it->l); +} + + +void HUlib_addPrefixToIText(hu_itext_t* it, char* str) +{ + while (*str) + HUlib_addCharToTextLine(&it->l, *(str++)); + it->lm = it->l.len; +} + + +// wrapper function for handling general keyed input. +// returns true if it ate the key +doom_boolean HUlib_keyInIText(hu_itext_t* it, unsigned char ch) +{ + if (ch >= ' ' && ch <= '_') + HUlib_addCharToTextLine(&it->l, (char)ch); + else + if (ch == KEY_BACKSPACE) + HUlib_delCharFromIText(it); + else + if (ch != KEY_ENTER) + return false; // did not eat key + + return true; // ate the key +} + + +void HUlib_drawIText(hu_itext_t* it) +{ + hu_textline_t* l = &it->l; + + if (!*it->on) + return; + HUlib_drawTextLine(l, true); // draw the line w/ cursor +} + + +void HUlib_eraseIText(hu_itext_t* it) +{ + if (it->laston && !*it->on) + it->l.needsupdate = 4; + HUlib_eraseTextLine(&it->l); + it->laston = *it->on; +} diff --git a/src/DOOM/hu_lib.h b/src/DOOM/hu_lib.h new file mode 100644 index 0000000..662b438 --- /dev/null +++ b/src/DOOM/hu_lib.h @@ -0,0 +1,181 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: none +// +//----------------------------------------------------------------------------- + +#ifndef __HULIB__ +#define __HULIB__ + + +// We are referring to patches. +#include "r_defs.h" + + +// background and foreground screen numbers +// different from other modules. +#define BG 1 +#define FG 0 + +// font stuff +#define HU_CHARERASE KEY_BACKSPACE + +#define HU_MAXLINES 4 +#define HU_MAXLINELENGTH 80 + + +// +// Typedefs of widgets +// + +// Text Line widget +// (parent of Scrolling Text and Input Text widgets) +typedef struct +{ + // left-justified position of scrolling text window + int x; + int y; + + patch_t** f; // font + int sc; // start character + char l[HU_MAXLINELENGTH + 1]; // line of text + int len; // current line length + + // whether this line needs to be udpated + int needsupdate; +} hu_textline_t; + + +// Scrolling Text window widget +// (child of Text Line widget) +typedef struct +{ + hu_textline_t l[HU_MAXLINES]; // text lines to draw + int h; // height in lines + int cl; // current line number + + // pointer to doom_boolean stating whether to update window + doom_boolean* on; + doom_boolean laston; // last value of *->on. +} hu_stext_t; + + +// Input Text Line widget +// (child of Text Line widget) +typedef struct +{ + hu_textline_t l; // text line to input on + + // left margin past which I am not to delete characters + int lm; + + // pointer to doom_boolean stating whether to update window + doom_boolean* on; + doom_boolean laston; // last value of *->on; +} hu_itext_t; + + +// +// Widget creation, access, and update routines +// + +// initializes heads-up widget library +void HUlib_init(void); + +// +// textline code +// + +// clear a line of text +void HUlib_clearTextLine(hu_textline_t* t); + +void HUlib_initTextLine(hu_textline_t* t, int x, int y, patch_t** f, int sc); + +// returns success +doom_boolean HUlib_addCharToTextLine(hu_textline_t* t, char ch); + +// returns success +doom_boolean HUlib_delCharFromTextLine(hu_textline_t* t); + +// draws tline +void HUlib_drawTextLine(hu_textline_t* l, doom_boolean drawcursor); + +// erases text line +void HUlib_eraseTextLine(hu_textline_t* l); + + +// +// Scrolling Text window widget routines +// + +// ? +void HUlib_initSText(hu_stext_t* s, + int x, + int y, + int h, + patch_t** font, + int startchar, + doom_boolean* on); + +// add a new line +void HUlib_addLineToSText(hu_stext_t* s); + +// ? +void HUlib_addMessageToSText(hu_stext_t* s, char* prefix, char* msg); + +// draws stext +void HUlib_drawSText(hu_stext_t* s); + +// erases all stext lines +void HUlib_eraseSText(hu_stext_t* s); + +// Input Text Line widget routines +void HUlib_initIText(hu_itext_t* it, + int x, + int y, + patch_t** font, + int startchar, + doom_boolean* on); + +// enforces left margin +void HUlib_delCharFromIText(hu_itext_t* it); + +// enforces left margin +void HUlib_eraseLineFromIText(hu_itext_t* it); + +// resets line and left margin +void HUlib_resetIText(hu_itext_t* it); + +// left of left-margin +void HUlib_addPrefixToIText(hu_itext_t* it, char* str); + +// whether eaten +doom_boolean HUlib_keyInIText(hu_itext_t* it, unsigned char ch); + +void HUlib_drawIText(hu_itext_t* it); + +// erases all itext lines +void HUlib_eraseIText(hu_itext_t* it); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/hu_stuff.c b/src/DOOM/hu_stuff.c new file mode 100644 index 0000000..ca4231c --- /dev/null +++ b/src/DOOM/hu_stuff.c @@ -0,0 +1,733 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: Heads-up displays +// +//----------------------------------------------------------------------------- + +#include "doom_config.h" + + + +#include "doomdef.h" +#include "z_zone.h" +#include "m_swap.h" +#include "hu_stuff.h" +#include "hu_lib.h" +#include "w_wad.h" +#include "s_sound.h" +#include "doomstat.h" +#include "dstrings.h" // Data. +#include "sounds.h" + + +// +// Locally used constants, shortcuts. +// +#define HU_TITLE (mapnames[(gameepisode-1)*9+gamemap-1]) +#define HU_TITLE2 (mapnames2[gamemap-1]) +#define HU_TITLEP (mapnamesp[gamemap-1]) +#define HU_TITLET (mapnamest[gamemap-1]) +#define HU_TITLEHEIGHT 1 +#define HU_TITLEX 0 +#define HU_TITLEY (167 - SHORT(hu_font[0]->height)) +#define HU_INPUTTOGGLE 't' +#define HU_INPUTX HU_MSGX +#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0]->height) +1)) +#define HU_INPUTWIDTH 64 +#define HU_INPUTHEIGHT 1 +#define QUEUESIZE 128 + + +static player_t* plr; +static hu_textline_t w_title; +static hu_itext_t w_chat; +static doom_boolean always_off = false; +static char chat_dest[MAXPLAYERS]; +static hu_itext_t w_inputbuffer[MAXPLAYERS]; +static doom_boolean message_on; +static doom_boolean message_nottobefuckedwith; +static hu_stext_t w_message; +static int message_counter; +static doom_boolean headsupactive = false; +static char chatchars[QUEUESIZE]; +static int head = 0; +static int tail = 0; + +char* chat_macros[] = +{ + HUSTR_CHATMACRO0, + HUSTR_CHATMACRO1, + HUSTR_CHATMACRO2, + HUSTR_CHATMACRO3, + HUSTR_CHATMACRO4, + HUSTR_CHATMACRO5, + HUSTR_CHATMACRO6, + HUSTR_CHATMACRO7, + HUSTR_CHATMACRO8, + HUSTR_CHATMACRO9 +}; + +char* player_names[] = +{ + HUSTR_PLRGREEN, + HUSTR_PLRINDIGO, + HUSTR_PLRBROWN, + HUSTR_PLRRED +}; + +const char* shiftxform; + +const char french_shiftxform[] = +{ + 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, + ' ', '!', '"', '#', '$', '%', '&', + '"', // shift-' + '(', ')', '*', '+', + '?', // shift-, + '_', // shift-- + '>', // shift-. + '?', // shift-/ + '0', // shift-0 + '1', // shift-1 + '2', // shift-2 + '3', // shift-3 + '4', // shift-4 + '5', // shift-5 + '6', // shift-6 + '7', // shift-7 + '8', // shift-8 + '9', // shift-9 + '/', + '.', // shift-; + '<', + '+', // shift-= + '>', '?', '@', + 'A', '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', + '[', // shift-[ + '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK + ']', // shift-] + '"', '_', + '\'', // shift-` + 'A', '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', + '{', '|', '}', '~', 127 + +}; + +const char english_shiftxform[] = +{ + 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, + ' ', '!', '"', '#', '$', '%', '&', + '"', // shift-' + '(', ')', '*', '+', + '<', // shift-, + '_', // shift-- + '>', // shift-. + '?', // shift-/ + ')', // shift-0 + '!', // shift-1 + '@', // shift-2 + '#', // shift-3 + '$', // shift-4 + '%', // shift-5 + '^', // shift-6 + '&', // shift-7 + '*', // shift-8 + '(', // shift-9 + ':', + ':', // shift-; + '<', + '+', // shift-= + '>', '?', '@', + 'A', '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', + '[', // shift-[ + '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK + ']', // shift-] + '"', '_', + '\'', // shift-` + 'A', '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', + '{', '|', '}', '~', 127 +}; + +char frenchKeyMap[128] = +{ + 0, + 1,2,3,4,5,6,7,8,9,10, + 11,12,13,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30, + 31, + ' ','!','"','#','$','%','&','%','(',')','*','+',';','-',':','!', + '0','1','2','3','4','5','6','7','8','9',':','M','<','=','>','?', + '@','Q','B','C','D','E','F','G','H','I','J','K','L',',','N','O', + 'P','A','R','S','T','U','V','Z','X','Y','W','^','\\','$','^','_', + '@','Q','B','C','D','E','F','G','H','I','J','K','L',',','N','O', + 'P','A','R','S','T','U','V','Z','X','Y','W','^','\\','$','^',127 +}; + +char chat_char; // remove later. +patch_t* hu_font[HU_FONTSIZE]; +doom_boolean chat_on; +doom_boolean message_dontfuckwithme; + +extern int showMessages; +extern doom_boolean automapactive; + + +// +// Builtin map names. +// The actual names can be found in DStrings.h. +// + +char* mapnames[] = // DOOM shareware/registered/retail (Ultimate) names. +{ + + HUSTR_E1M1, + HUSTR_E1M2, + HUSTR_E1M3, + HUSTR_E1M4, + HUSTR_E1M5, + HUSTR_E1M6, + HUSTR_E1M7, + HUSTR_E1M8, + HUSTR_E1M9, + + HUSTR_E2M1, + HUSTR_E2M2, + HUSTR_E2M3, + HUSTR_E2M4, + HUSTR_E2M5, + HUSTR_E2M6, + HUSTR_E2M7, + HUSTR_E2M8, + HUSTR_E2M9, + + HUSTR_E3M1, + HUSTR_E3M2, + HUSTR_E3M3, + HUSTR_E3M4, + HUSTR_E3M5, + HUSTR_E3M6, + HUSTR_E3M7, + HUSTR_E3M8, + HUSTR_E3M9, + + HUSTR_E4M1, + HUSTR_E4M2, + HUSTR_E4M3, + HUSTR_E4M4, + HUSTR_E4M5, + HUSTR_E4M6, + HUSTR_E4M7, + HUSTR_E4M8, + HUSTR_E4M9, + + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL", + "NEWLEVEL" +}; + +char* mapnames2[] = // DOOM 2 map names. +{ + HUSTR_1, + HUSTR_2, + HUSTR_3, + HUSTR_4, + HUSTR_5, + HUSTR_6, + HUSTR_7, + HUSTR_8, + HUSTR_9, + HUSTR_10, + HUSTR_11, + + HUSTR_12, + HUSTR_13, + HUSTR_14, + HUSTR_15, + HUSTR_16, + HUSTR_17, + HUSTR_18, + HUSTR_19, + HUSTR_20, + + HUSTR_21, + HUSTR_22, + HUSTR_23, + HUSTR_24, + HUSTR_25, + HUSTR_26, + HUSTR_27, + HUSTR_28, + HUSTR_29, + HUSTR_30, + HUSTR_31, + HUSTR_32 +}; + + +char* mapnamesp[] = // Plutonia WAD map names. +{ + PHUSTR_1, + PHUSTR_2, + PHUSTR_3, + PHUSTR_4, + PHUSTR_5, + PHUSTR_6, + PHUSTR_7, + PHUSTR_8, + PHUSTR_9, + PHUSTR_10, + PHUSTR_11, + + PHUSTR_12, + PHUSTR_13, + PHUSTR_14, + PHUSTR_15, + PHUSTR_16, + PHUSTR_17, + PHUSTR_18, + PHUSTR_19, + PHUSTR_20, + + PHUSTR_21, + PHUSTR_22, + PHUSTR_23, + PHUSTR_24, + PHUSTR_25, + PHUSTR_26, + PHUSTR_27, + PHUSTR_28, + PHUSTR_29, + PHUSTR_30, + PHUSTR_31, + PHUSTR_32 +}; + + +char* mapnamest[] = // TNT WAD map names. +{ + THUSTR_1, + THUSTR_2, + THUSTR_3, + THUSTR_4, + THUSTR_5, + THUSTR_6, + THUSTR_7, + THUSTR_8, + THUSTR_9, + THUSTR_10, + THUSTR_11, + + THUSTR_12, + THUSTR_13, + THUSTR_14, + THUSTR_15, + THUSTR_16, + THUSTR_17, + THUSTR_18, + THUSTR_19, + THUSTR_20, + + THUSTR_21, + THUSTR_22, + THUSTR_23, + THUSTR_24, + THUSTR_25, + THUSTR_26, + THUSTR_27, + THUSTR_28, + THUSTR_29, + THUSTR_30, + THUSTR_31, + THUSTR_32 +}; + +char ForeignTranslation(unsigned char ch) +{ + return ch < 128 ? frenchKeyMap[ch] : ch; +} + +void HU_Init(void) +{ + + int i; + int j; + char buffer[9]; + + if (french) + shiftxform = french_shiftxform; + else + shiftxform = english_shiftxform; + + // load the heads-up font + j = HU_FONTSTART; + for (i = 0; i < HU_FONTSIZE; i++) + { + //if (j == 40) __debugbreak(); + //doom_sprintf(buffer, "STCFN%.3d", j++); + doom_strcpy(buffer, "STCFN"); + if (j < 100) doom_concat(buffer, "0"); + if (j < 10) doom_concat(buffer, "0"); + doom_concat(buffer, doom_itoa(j++, 10)); + hu_font[i] = (patch_t*)W_CacheLumpName(buffer, PU_STATIC); + } +} + +void HU_Stop(void) +{ + headsupactive = false; +} + +void HU_Start(void) +{ + int i; + char* s; + + if (headsupactive) + HU_Stop(); + + plr = &players[consoleplayer]; + message_on = false; + message_dontfuckwithme = false; + message_nottobefuckedwith = false; + chat_on = false; + + // create the message widget + HUlib_initSText(&w_message, + HU_MSGX, HU_MSGY, HU_MSGHEIGHT, + hu_font, + HU_FONTSTART, &message_on); + + // create the map title widget + HUlib_initTextLine(&w_title, + HU_TITLEX, HU_TITLEY, + hu_font, + HU_FONTSTART); + + switch (gamemode) + { + case shareware: + case registered: + case retail: + s = HU_TITLE; + break; + + /* FIXME + case pack_plut: + s = HU_TITLEP; + break; + case pack_tnt: + s = HU_TITLET; + break; + */ + + case commercial: + default: + s = HU_TITLE2; + break; + } + + while (*s) + HUlib_addCharToTextLine(&w_title, *(s++)); + + // create the chat widget + HUlib_initIText(&w_chat, + HU_INPUTX, HU_INPUTY, + hu_font, + HU_FONTSTART, &chat_on); + + // create the inputbuffer widgets + for (i = 0; i < MAXPLAYERS; i++) + HUlib_initIText(&w_inputbuffer[i], 0, 0, 0, 0, &always_off); + + headsupactive = true; +} + +void HU_Drawer(void) +{ + HUlib_drawSText(&w_message); + HUlib_drawIText(&w_chat); + if (automapactive) + HUlib_drawTextLine(&w_title, false); +} + +void HU_Erase(void) +{ + HUlib_eraseSText(&w_message); + HUlib_eraseIText(&w_chat); + HUlib_eraseTextLine(&w_title); +} + +void HU_Ticker(void) +{ + int i, rc; + char c; + + // tick down message counter if message is up + if (message_counter && !--message_counter) + { + message_on = false; + message_nottobefuckedwith = false; + } + + if (showMessages || message_dontfuckwithme) + { + + // display message if necessary + if ((plr->message && !message_nottobefuckedwith) + || (plr->message && message_dontfuckwithme)) + { + HUlib_addMessageToSText(&w_message, 0, plr->message); + plr->message = 0; + message_on = true; + message_counter = HU_MSGTIMEOUT; + message_nottobefuckedwith = message_dontfuckwithme; + message_dontfuckwithme = 0; + } + + } // else message_on = false; + + // check for incoming chat characters + if (netgame) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + if (i != consoleplayer + && (c = players[i].cmd.chatchar)) + { + if (c <= HU_BROADCAST) + chat_dest[i] = c; + else + { + if (c >= 'a' && c <= 'z') + c = (char)shiftxform[(unsigned char)c]; + rc = HUlib_keyInIText(&w_inputbuffer[i], c); + if (rc && c == KEY_ENTER) + { + if (w_inputbuffer[i].l.len + && (chat_dest[i] == consoleplayer + 1 + || chat_dest[i] == HU_BROADCAST)) + { + HUlib_addMessageToSText(&w_message, + player_names[i], + w_inputbuffer[i].l.l); + + message_nottobefuckedwith = true; + message_on = true; + message_counter = HU_MSGTIMEOUT; + if (gamemode == commercial) + S_StartSound(0, sfx_radio); + else + S_StartSound(0, sfx_tink); + } + HUlib_resetIText(&w_inputbuffer[i]); + } + } + players[i].cmd.chatchar = 0; + } + } + } +} + +void HU_queueChatChar(char c) +{ + if (((head + 1) & (QUEUESIZE - 1)) == tail) + { + plr->message = HUSTR_MSGU; + } + else + { + chatchars[head] = c; + head = (head + 1) & (QUEUESIZE - 1); + } +} + +char HU_dequeueChatChar(void) +{ + char c; + + if (head != tail) + { + c = chatchars[tail]; + tail = (tail + 1) & (QUEUESIZE - 1); + } + else + { + c = 0; + } + + return c; +} + +doom_boolean HU_Responder(event_t* ev) +{ + + static char lastmessage[HU_MAXLINELENGTH + 1]; + char* macromessage; + doom_boolean eatkey = false; + static doom_boolean shiftdown = false; + static doom_boolean altdown = false; + unsigned char c; + int i; + int numplayers; + + static char destination_keys[MAXPLAYERS] = + { + HUSTR_KEYGREEN, + HUSTR_KEYINDIGO, + HUSTR_KEYBROWN, + HUSTR_KEYRED + }; + + static int num_nobrainers = 0; + + numplayers = 0; + for (i = 0; i < MAXPLAYERS; i++) + numplayers += playeringame[i]; + + if (ev->data1 == KEY_RSHIFT) + { + shiftdown = ev->type == ev_keydown; + return false; + } + else if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT) + { + altdown = ev->type == ev_keydown; + return false; + } + + if (ev->type != ev_keydown) + return false; + + if (!chat_on) + { + if (ev->data1 == HU_MSGREFRESH) + { + message_on = true; + message_counter = HU_MSGTIMEOUT; + eatkey = true; + } + else if (netgame && ev->data1 == HU_INPUTTOGGLE) + { + eatkey = chat_on = true; + HUlib_resetIText(&w_chat); + HU_queueChatChar(HU_BROADCAST); + } + else if (netgame && numplayers > 2) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (ev->data1 == destination_keys[i]) + { + if (playeringame[i] && i != consoleplayer) + { + eatkey = chat_on = true; + HUlib_resetIText(&w_chat); + HU_queueChatChar(i + 1); + break; + } + else if (i == consoleplayer) + { + num_nobrainers++; + if (num_nobrainers < 3) + plr->message = HUSTR_TALKTOSELF1; + else if (num_nobrainers < 6) + plr->message = HUSTR_TALKTOSELF2; + else if (num_nobrainers < 9) + plr->message = HUSTR_TALKTOSELF3; + else if (num_nobrainers < 32) + plr->message = HUSTR_TALKTOSELF4; + else + plr->message = HUSTR_TALKTOSELF5; + } + } + } + } + } + else + { + c = ev->data1; + // send a macro + if (altdown) + { + c = c - '0'; + if (c > 9) + return false; + macromessage = chat_macros[c]; + + // kill last message with a '\n' + HU_queueChatChar(KEY_ENTER); // DEBUG!!! + + // send the macro message + while (*macromessage) + HU_queueChatChar(*macromessage++); + HU_queueChatChar(KEY_ENTER); + + // leave chat mode and notify that it was sent + chat_on = false; + doom_strcpy(lastmessage, chat_macros[c]); + plr->message = lastmessage; + eatkey = true; + } + else + { + if (french) + c = ForeignTranslation(c); + if (shiftdown || (c >= 'a' && c <= 'z')) + c = shiftxform[c]; + eatkey = HUlib_keyInIText(&w_chat, c); + if (eatkey) + { + HU_queueChatChar(c); + } + if (c == KEY_ENTER) + { + chat_on = false; + if (w_chat.l.len) + { + doom_strcpy(lastmessage, w_chat.l.l); + plr->message = lastmessage; + } + } + else if (c == KEY_ESCAPE) + chat_on = false; + } + } + + return eatkey; +} diff --git a/src/DOOM/hu_stuff.h b/src/DOOM/hu_stuff.h new file mode 100644 index 0000000..663b2df --- /dev/null +++ b/src/DOOM/hu_stuff.h @@ -0,0 +1,67 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: Head up display +// +//----------------------------------------------------------------------------- + +#ifndef __HU_STUFF_H__ +#define __HU_STUFF_H__ + + +#include "d_event.h" + + +// +// Globally visible constants. +// +#define HU_FONTSTART '!' // the first font characters +#define HU_FONTEND '_' // the last font characters + +// Calculate # of glyphs in font. +#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) + +#define HU_BROADCAST 5 + +#define HU_MSGREFRESH KEY_ENTER +#define HU_MSGX 0 +#define HU_MSGY 0 +#define HU_MSGWIDTH 64 // in characters +#define HU_MSGHEIGHT 1 // in lines + +#define HU_MSGTIMEOUT (4*TICRATE) + + +// +// HEADS UP TEXT +// + +void HU_Init(void); +void HU_Start(void); +doom_boolean HU_Responder(event_t* ev); +void HU_Ticker(void); +void HU_Drawer(void); +char HU_dequeueChatChar(void); +void HU_Erase(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/i_net.c b/src/DOOM/i_net.c new file mode 100644 index 0000000..6a618fe --- /dev/null +++ b/src/DOOM/i_net.c @@ -0,0 +1,457 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// +//----------------------------------------------------------------------------- + + +//#define I_NET_ENABLED + +#if defined(I_NET_ENABLED) +#define _CRT_SECURE_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#endif +#include "doom_config.h" + +#if defined(I_NET_ENABLED) +#if defined(DOOM_WIN32) +#define _CRT_SECURE_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#include +#define IPPORT_USERRESERVED 5000 +#pragma comment(lib, "ws2_32.lib") +#else + #include + #include + #include + #include +#define SOCKET int +#endif +#else +#define IPPORT_USERRESERVED 5000 +#endif + +#include "i_system.h" +#include "d_event.h" +#include "d_net.h" +#include "m_argv.h" +#include "doomstat.h" +#include "i_net.h" + + +// For some odd reason... +#if !defined(DOOM_APPLE) // It doesn't complain on Win32? O_o +#define ntohl(x) \ + ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \ + (((unsigned long int)(x) & 0x0000ff00U) << 8) | \ + (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \ + (((unsigned long int)(x) & 0xff000000U) >> 24))) + +#define ntohs(x) \ + ((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \ + (((unsigned short int)(x) & 0xff00) >> 8))) \ + +#define htonl(x) ntohl(x) +#define htons(x) ntohs(x) +#endif + + +// +// NETWORKING +// + +int DOOMPORT = (IPPORT_USERRESERVED + 0x1d); +int DOOMPORT_SEND = (IPPORT_USERRESERVED + 0x1e); + +#if defined(I_NET_ENABLED) +SOCKET sendsocket; +SOCKET insocket; + +struct sockaddr_in sendaddress[MAXNETNODES]; +#endif + +void (*netget) (void); +void (*netsend) (void); + + +void NetSend(void); +doom_boolean NetListen(void); + + +// +// UDPsocket +// +#if defined(I_NET_ENABLED) +SOCKET UDPsocket(void) +{ + SOCKET s; + + // allocate a socket + s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0) + { + //I_Error("Error: can't create socket: %s", strerror(errno)); + + doom_strcpy(error_buf, "Error: can't create socket: "); + doom_concat(error_buf, strerror(errno)); + I_Error(error_buf); + } + + return s; +} +#endif + + +// +// BindToLocalPort +// +#if defined(I_NET_ENABLED) +void BindToLocalPort(SOCKET s, int port) +{ + int v; + struct sockaddr_in address; + + doom_memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = port; + + v = bind(s, (void*)&address, sizeof(address)); + if (v == -1) + { + //I_Error("Error: BindToPort: bind: %s", strerror(errno)); + + doom_strcpy(error_buf, "Error: BindToPort: bind: "); + doom_concat(error_buf, strerror(errno)); + I_Error(error_buf); + } +} +#endif + + +// +// PacketSend +// +void PacketSend(void) +{ +#if defined(I_NET_ENABLED) + int c; + doomdata_t sw; + + // byte swap + sw.checksum = htonl(netbuffer->checksum); + sw.player = netbuffer->player; + sw.retransmitfrom = netbuffer->retransmitfrom; + sw.starttic = netbuffer->starttic; + sw.numtics = netbuffer->numtics; + for (c = 0; c < netbuffer->numtics; c++) + { + sw.cmds[c].forwardmove = netbuffer->cmds[c].forwardmove; + sw.cmds[c].sidemove = netbuffer->cmds[c].sidemove; + sw.cmds[c].angleturn = htons(netbuffer->cmds[c].angleturn); + sw.cmds[c].consistancy = htons(netbuffer->cmds[c].consistancy); + sw.cmds[c].chatchar = netbuffer->cmds[c].chatchar; + sw.cmds[c].buttons = netbuffer->cmds[c].buttons; + } + + //doom_print ("sending %i\n",gametic); + c = sendto(sendsocket, (const char*)&sw, doomcom->datalength + , 0, (void*)&sendaddress[doomcom->remotenode] + , sizeof(sendaddress[doomcom->remotenode])); +#endif +} + + +// +// PacketGet +// +void PacketGet(void) +{ +#if defined(I_NET_ENABLED) + int i; + int c; + struct sockaddr_in fromaddress; +#if defined(__APPLE__) + socklen_t fromlen; +#else + int fromlen; +#endif + doomdata_t sw; + + fromlen = sizeof(fromaddress); + c = recvfrom(insocket, (char*)&sw, sizeof(sw), 0 + , (struct sockaddr*)&fromaddress, &fromlen); + if (c == -1) + { +#if defined(DOOM_WIN32) + int r = WSAGetLastError(); + if (r != WSAEWOULDBLOCK) + { + //I_Error("Error: GetPacket: %i", r); + + doom_strcpy(error_buf, "Error: GetPacket: "); + doom_concat(error_buf, doom_itoa(r, 10)); + I_Error(error_buf); + } +#else + if (errno != EWOULDBLOCK) + { + //I_Error("Error: GetPacket: %s", strerror(errno)); + + doom_strcpy(error_buf, "Error: GetPacket: "); + doom_concat(error_buf, strerror(errno)); + I_Error(error_buf); + } +#endif + doomcom->remotenode = -1; // no packet + return; + } + + { + static int first = 1; + if (first) + { + //doom_print("len=%d:p=[0x%x 0x%x] \n", c, *(int*)&sw, *((int*)&sw + 1)); + doom_print("len="); + doom_print(doom_itoa(c, 10)); + doom_print(":p=[0x"); + doom_print(doom_itoa(*(int*)&sw, 16)); + doom_print(" 0x"); + doom_print(doom_itoa(*((int*)&sw + 1), 16)); + doom_print("] \n"); + } + first = 0; + } + + // find remote node number + for (i = 0; i < doomcom->numnodes; i++) + if (fromaddress.sin_addr.s_addr == sendaddress[i].sin_addr.s_addr) + break; + + if (i == doomcom->numnodes) + { + // packet is not from one of the players (new game broadcast) + doomcom->remotenode = -1; // no packet + return; + } + + doomcom->remotenode = i; // good packet from a game player + doomcom->datalength = c; + + // byte swap + netbuffer->checksum = ntohl(sw.checksum); + netbuffer->player = sw.player; + netbuffer->retransmitfrom = sw.retransmitfrom; + netbuffer->starttic = sw.starttic; + netbuffer->numtics = sw.numtics; + + for (c = 0; c < netbuffer->numtics; c++) + { + netbuffer->cmds[c].forwardmove = sw.cmds[c].forwardmove; + netbuffer->cmds[c].sidemove = sw.cmds[c].sidemove; + netbuffer->cmds[c].angleturn = ntohs(sw.cmds[c].angleturn); + netbuffer->cmds[c].consistancy = ntohs(sw.cmds[c].consistancy); + netbuffer->cmds[c].chatchar = sw.cmds[c].chatchar; + netbuffer->cmds[c].buttons = sw.cmds[c].buttons; + } +#endif +} + + +int GetLocalAddress(void) +{ +#if defined(I_NET_ENABLED) + char hostname[1024]; + struct hostent* hostentry; // host information entry + int v; + + // get local address + v = gethostname(hostname, sizeof(hostname)); + if (v == -1) + { + //I_Error("Error: GetLocalAddress : gethostname: errno %d", errno); + + doom_strcpy(error_buf, "Error: GetLocalAddress : gethostname: errno "); + doom_concat(error_buf, strerror(errno)); + I_Error(error_buf); + } + + hostentry = gethostbyname(hostname); + if (!hostentry) + { + I_Error("Error: GetLocalAddress : gethostbyname: couldn't get local host"); + } + + return *(int*)hostentry->h_addr_list[0]; +#else + return 0; +#endif +} + + +// +// I_InitNetwork +// +void I_InitNetwork(void) +{ +#if defined(I_NET_ENABLED) +#if defined(DOOM_WIN32) + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); + + u_long trueval = 1; +#else + doom_boolean trueval = true; +#endif +#endif + int i; + int p; + struct hostent* hostentry; // host information entry + + doomcom = doom_malloc(sizeof(*doomcom)); + doom_memset(doomcom, 0, sizeof(*doomcom)); + + // set up for network + i = M_CheckParm("-dup"); + if (i && i < myargc - 1) + { + doomcom->ticdup = myargv[i + 1][0] - '0'; + if (doomcom->ticdup < 1) + doomcom->ticdup = 1; + if (doomcom->ticdup > 9) + doomcom->ticdup = 9; + } + else + doomcom->ticdup = 1; + + if (M_CheckParm("-extratic")) + doomcom->extratics = 1; + else + doomcom->extratics = 0; + + p = M_CheckParm("-port"); + if (p && p < myargc - 1) + { + DOOMPORT = doom_atoi(myargv[p + 1]); + //doom_print("using alternate port %i\n", DOOMPORT); + doom_print("using alternate port "); + doom_print(doom_itoa(DOOMPORT, 10)); + doom_print("\n"); + } + + p = M_CheckParm("-sendport"); + if (p && p < myargc - 1) + { + DOOMPORT_SEND = doom_atoi(myargv[p + 1]); + //doom_print("using alternate send port %i\n", DOOMPORT_SEND); + doom_print("using alternate send port "); + doom_print(doom_itoa(DOOMPORT_SEND, 10)); + doom_print("\n"); + } + + // parse network game options, + // -net ... + i = M_CheckParm("-net"); + if (!i) + { + // single player game + netgame = false; + doomcom->id = DOOMCOM_ID; + doomcom->numplayers = doomcom->numnodes = 1; + doomcom->deathmatch = false; + doomcom->consoleplayer = 0; + return; + } + +#if defined(I_NET_ENABLED) + netsend = PacketSend; + netget = PacketGet; + netgame = true; + + // parse player number and host list + doomcom->consoleplayer = myargv[i + 1][0] - '1'; + + doomcom->numnodes = 1; // this node for sure + + i++; + while (++i < myargc && myargv[i][0] != '-') + { + sendaddress[doomcom->numnodes].sin_family = AF_INET; + sendaddress[doomcom->numnodes].sin_port = htons(DOOMPORT); + if (myargv[i][0] == '.') + { + sendaddress[doomcom->numnodes].sin_addr.s_addr + = inet_addr(myargv[i] + 1); + } + else + { + hostentry = gethostbyname(myargv[i]); + if (!hostentry) + { + //I_Error("Error: gethostbyname: couldn't find %s", myargv[i]); + + doom_strcpy(error_buf, "Error: gethostbyname: couldn't find "); + doom_concat(error_buf, myargv[i]); + I_Error(error_buf); + } + sendaddress[doomcom->numnodes].sin_addr.s_addr + = *(int*)hostentry->h_addr_list[0]; + } + doomcom->numnodes++; + } + + doomcom->id = DOOMCOM_ID; + doomcom->numplayers = doomcom->numnodes; + + // build message to receive + insocket = UDPsocket(); + BindToLocalPort(insocket, htons(DOOMPORT)); +#if defined(DOOM_WIN32) + ioctlsocket(insocket, FIONBIO, &trueval); +#else + ioctl(insocket, FIONBIO, &trueval); +#endif + + sendsocket = UDPsocket(); +#endif +} + + +void I_NetCmd(void) +{ +#if defined(I_NET_ENABLED) + if (doomcom->command == CMD_SEND) + { + netsend(); + } + else if (doomcom->command == CMD_GET) + { + netget(); + } + else + { + //I_Error("Error: Bad net cmd: %i\n", doomcom->command); + + doom_strcpy(error_buf, "Error: Bad net cmd: "); + doom_concat(error_buf, doom_itoa(doomcom->command, 10)); + I_Error(error_buf); + } +#endif +} diff --git a/src/DOOM/i_net.h b/src/DOOM/i_net.h new file mode 100644 index 0000000..7f91503 --- /dev/null +++ b/src/DOOM/i_net.h @@ -0,0 +1,38 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// System specific network interface stuff. +// +//----------------------------------------------------------------------------- + +#ifndef __I_NET__ +#define __I_NET__ + + +// Called by D_DoomMain. + +void I_InitNetwork (void); +void I_NetCmd (void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/i_sound.c b/src/DOOM/i_sound.c new file mode 100644 index 0000000..db76ea6 --- /dev/null +++ b/src/DOOM/i_sound.c @@ -0,0 +1,1168 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// System interface for sound. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "z_zone.h" +#include "i_system.h" +#include "i_sound.h" +#include "m_argv.h" +#include "m_misc.h" +#include "w_wad.h" +#include "doomdef.h" + + +// Needed for calling the actual sound output. +#define SAMPLECOUNT 512 +#define NUM_CHANNELS 8 +// It is 2 for 16bit, and 2 for two channels. +#define BUFMUL 4 +#define MIXBUFFERSIZE (SAMPLECOUNT*BUFMUL) + +#define SAMPLERATE 11025 // Hz +#define SAMPLESIZE 2 // 16bit + +#define MAX_QUEUED_MIDI_MSGS 256 + +#define EVENT_RELEASE_NOTE 0 +#define EVENT_PLAY_NOTE 1 +#define EVENT_PITCH_BEND 2 +#define EVENT_SYSTEM_EVENT 3 +#define EVENT_CONTROLLER 4 +#define EVENT_END_OF_MEASURE 5 +#define EVENT_FINISH 6 +#define EVENT_UNUSED 7 + +#define CONTROLLER_EVENT_ALL_SOUNDS_OFF 10 +#define CONTROLLER_EVENT_ALL_NOTES_OFF 11 +#define CONTROLLER_EVENT_MONO 12 +#define CONTROLLER_EVENT_POLY 13 +#define CONTROLLER_EVENT_RESET_ALL_CONTROLLERS 14 +#define CONTROLLER_EVENT_EVENT 15 + +#define CONTROLLER_CHANGE_INSTRUMENT 0 +#define CONTROLLER_BANK_SELECT 1 +#define CONTROLLER_MODULATION 2 +#define CONTROLLER_VOLUME 3 +#define CONTROLLER_PAN 4 +#define CONTROLLER_EXPRESSION 5 +#define CONTROLLER_REVERB 6 +#define CONTROLLER_CHORUS 7 +#define CONTROLLER_SUSTAIN 8 +#define CONTROLLER_SOFT 9 + + +typedef struct +{ + char ID[4]; + unsigned short scoreLen; + unsigned short scoreStart; + unsigned short channels; + unsigned short sec_channels; + unsigned short instrCnt; + unsigned short dummy; +} mus_header_t; + + +// A quick hack to establish a protocol between +// synchronous mix buffer updates and asynchronous +// audio writes. Probably redundant with gametic. +static int flag = 0; + +static unsigned char* mus_data = 0; +static mus_header_t mus_header; +static int mus_offset = 0; +static int mus_delay = 0; +static doom_boolean mus_loop = false; +static doom_boolean mus_playing = false; +static int mus_volume = 127; +static int mus_channel_volumes[16] = { 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 }; + +static int looping = 0; +static int musicdies = -1; + + +// The number of internal mixing channels, +// the samples calculated for each mixing step, +// the size of the 16bit, 2 hardware channel (stereo) +// mixing buffer, and the samplerate of the raw data. + +// The actual lengths of all sound effects. +int lengths[NUMSFX]; + +// The actual output device. +int audio_fd; + +// The global mixing buffer. +// Basically, samples from all active internal channels +// are modifed and added, and stored in the buffer +// that is submitted to the audio device. +signed short mixbuffer[MIXBUFFERSIZE]; + + +// The channel step amount... +unsigned int channelstep[NUM_CHANNELS]; +// ... and a 0.16 bit remainder of last step. +unsigned int channelstepremainder[NUM_CHANNELS]; + +// The channel data pointers, start and end. +unsigned char* channels[NUM_CHANNELS]; +unsigned char* channelsend[NUM_CHANNELS]; + +// Time/gametic that the channel started playing, +// used to determine oldest, which automatically +// has lowest priority. +// In case number of active sounds exceeds +// available channels. +int channelstart[NUM_CHANNELS]; + +// The sound in channel handles, +// determined on registration, +// might be used to unregister/stop/modify, +// currently unused. +int channelhandles[NUM_CHANNELS]; + +// SFX id of the playing sound effect. +// Used to catch duplicates (like chainsaw). +int channelids[NUM_CHANNELS]; + +// Pitch to stepping lookup, unused. +int steptable[256]; + +// Volume lookups. +int vol_lookup[128 * 256]; + +// Hardware left and right channel volume lookup. +int* channelleftvol_lookup[NUM_CHANNELS]; +int* channelrightvol_lookup[NUM_CHANNELS]; + +unsigned long queued_midi_msgs[MAX_QUEUED_MIDI_MSGS]; +int queue_midi_head = 0; +int queue_midi_tail = 0; + + +void TickSong(); + + +// +// This function loads the sound data from the WAD lump, +// for single sound. +// +void* getsfx(char* sfxname, int* len) +{ + unsigned char* sfx; + unsigned char* paddedsfx; + int i; + int size; + int paddedsize; + char name[20]; + int sfxlump; + + // Get the sound data from the WAD, allocate lump + // in zone memory. + //doom_sprintf(name, "ds%s", sfxname); + doom_strcpy(name, "ds"); + doom_concat(name, sfxname); + + // Now, there is a severe problem with the + // sound handling, in it is not (yet/anymore) + // gamemode aware. That means, sounds from + // DOOM II will be requested even with DOOM + // shareware. + // The sound list is wired into sounds.c, + // which sets the external variable. + // I do not do runtime patches to that + // variable. Instead, we will use a + // default sound for replacement. + if (W_CheckNumForName(name) == -1) + sfxlump = W_GetNumForName("dspistol"); + else + sfxlump = W_GetNumForName(name); + + size = W_LumpLength(sfxlump); + + sfx = (unsigned char*)W_CacheLumpNum(sfxlump, PU_STATIC); + + // Pads the sound effect out to the mixing buffer size. + // The original realloc would interfere with zone memory. + paddedsize = ((size - 8 + (SAMPLECOUNT - 1)) / SAMPLECOUNT) * SAMPLECOUNT; + + // Allocate from zone memory. + paddedsfx = (unsigned char*)Z_Malloc(paddedsize + 8, PU_STATIC, 0); + // ddt: (unsigned char *) realloc(sfx, paddedsize+8); + // This should interfere with zone memory handling, + // which does not kick in in the soundserver. + + // Now copy and pad. + doom_memcpy(paddedsfx, sfx, size); + for (i = size; i < paddedsize + 8; i++) + paddedsfx[i] = 128; + + // Remove the cached lump. + Z_Free(sfx); + + // Preserve padded length. + *len = paddedsize; + + // Return allocated padded data. + return (void*)(paddedsfx + 8); +} + + +// +// This function adds a sound to the +// list of currently active sounds, +// which is maintained as a given number +// (eight, usually) of internal channels. +// Returns a handle. +// +int addsfx(int sfxid, int volume, int step, int seperation) +{ + static unsigned short handlenums = 0; + + int i; + int rc = -1; + + int oldest = gametic; + int oldestnum = 0; + int slot; + + int rightvol; + int leftvol; + + // Chainsaw troubles. + // Play these sound effects only one at a time. + if (sfxid == sfx_sawup + || sfxid == sfx_sawidl + || sfxid == sfx_sawful + || sfxid == sfx_sawhit + || sfxid == sfx_stnmov + || sfxid == sfx_pistol) + { + // Loop all channels, check. + for (i = 0; i < NUM_CHANNELS; i++) + { + // Active, and using the same SFX? + if ((channels[i]) + && (channelids[i] == sfxid)) + { + // Reset. + channels[i] = 0; + // We are sure that iff, + // there will only be one. + break; + } + } + } + + // Loop all channels to find oldest SFX. + for (i = 0; (i < NUM_CHANNELS) && (channels[i]); i++) + { + if (channelstart[i] < oldest) + { + oldestnum = i; + oldest = channelstart[i]; + } + } + + // Tales from the cryptic. + // If we found a channel, fine. + // If not, we simply overwrite the first one, 0. + // Probably only happens at startup. + if (i == NUM_CHANNELS) + slot = oldestnum; + else + slot = i; + + // Okay, in the less recent channel, + // we will handle the new SFX. + // Set pointer to raw data. + channels[slot] = (unsigned char*)S_sfx[sfxid].data; + // Set pointer to end of raw data. + channelsend[slot] = channels[slot] + lengths[sfxid]; + + // Reset current handle number, limited to 0..100. + if (!handlenums) + handlenums = 100; + + // Assign current handle number. + // Preserved so sounds could be stopped (unused). + channelhandles[slot] = rc = handlenums++; + + // Set stepping??? + // Kinda getting the impression this is never used. + channelstep[slot] = step; + // ??? + channelstepremainder[slot] = 0; + // Should be gametic, I presume. + channelstart[slot] = gametic; + + // Separation, that is, orientation/stereo. + // range is: 1 - 256 + seperation += 1; + + // Per left/right channel. + // x^2 seperation, + // adjust volume properly. + leftvol = + volume - ((volume * seperation * seperation) >> 16); ///(256*256); + seperation = seperation - 257; + rightvol = + volume - ((volume * seperation * seperation) >> 16); + + // Sanity check, clamp volume. + if (rightvol < 0 || rightvol > 127) + I_Error("Error: rightvol out of bounds"); + + if (leftvol < 0 || leftvol > 127) + I_Error("Error: leftvol out of bounds"); + + // Get the proper lookup table piece + // for this volume level??? + channelleftvol_lookup[slot] = &vol_lookup[leftvol * 256]; + channelrightvol_lookup[slot] = &vol_lookup[rightvol * 256]; + + // Preserve sound SFX id, + // e.g. for avoiding duplicates of chainsaw. + channelids[slot] = sfxid; + + // You tell me. + return rc; +} + + +// +// SFX API +// Note: this was called by S_Init. +// However, whatever they did in the +// old DPMS based DOS version, this +// were simply dummies in the Linux +// version. +// See soundserver initdata(). +// +void I_SetChannels() +{ + // Init internal lookups (raw data, mixing buffer, channels). + // This function sets up internal lookups used during + // the mixing process. + int i; + int j; + + int* steptablemid = steptable + 128; + + // Okay, reset internal mixing channels to zero. + /*for (i=0; iname); + doom_strcpy(namebuf, "ds"); + doom_concat(namebuf, sfx->name); + return W_GetNumForName(namebuf); +} + + +// +// Starting a sound means adding it +// to the current list of active sounds +// in the internal channels. +// As the SFX info struct contains +// e.g. a pointer to the raw data, +// it is ignored. +// As our sound handling does not handle +// priority, it is ignored. +// Pitching (that is, increased speed of playback) +// is set, but currently not used by mixing. +// +int I_StartSound(int id, int vol, int sep, int pitch, int priority) +{ + // Returns a handle (not used). + id = addsfx(id, vol, steptable[pitch], sep); + return id; +} + + +void I_StopSound(int handle) +{ + // You need the handle returned by StartSound. + // Would be looping all channels, + // tracking down the handle, + // an setting the channel to zero. +} + + +int I_SoundIsPlaying(int handle) +{ + // Ouch. + return gametic < handle; +} + + +// +// This function loops all active (internal) sound +// channels, retrieves a given number of samples +// from the raw sound data, modifies it according +// to the current (internal) channel parameters, +// mixes the per channel samples into the global +// mixbuffer, clamping it to the allowed range, +// and sets up everything for transferring the +// contents of the mixbuffer to the (two) +// hardware channels (left and right, that is). +// +// This function currently supports only 16bit. +// +void I_UpdateSound(void) +{ + static int song_tick_progress = 0; + + // Mix current sound data. + // Data, from raw sound, for right and left. + register unsigned int sample; + register int dl; + register int dr; + + // Pointers in global mixbuffer, left, right, end. + signed short* leftout; + signed short* rightout; + signed short* leftend; + // Step in mixbuffer, left and right, thus two. + int step; + + // Mixing channel index. + int chan; + + // Do music first. [dsl] TODO: if we have embedded synth + //static int song_progress = 0; + //song_progress += 512; + //while (song_progress > 0) + //{ + // TickSong(); // About 7 music ticks per sound sampling (X for doubt) + // song_progress -= 78; + //} + + // Left and right channel + // are in global mixbuffer, alternating. + leftout = mixbuffer; + rightout = mixbuffer + 1; + step = 2; + + // Determine end, for left channel only + // (right channel is implicit). + leftend = mixbuffer + SAMPLECOUNT * step; + + // Mix sounds into the mixing buffer. + // Loop over step*SAMPLECOUNT, + // that is 512 values for two channels. + while (leftout != leftend) + { + // Reset left/right value. + dl = 0; + dr = 0; + + // Love thy L2 chache - made this a loop. + // Now more channels could be set at compile time + // as well. Thus loop those channels. + for (chan = 0; chan < NUM_CHANNELS; chan++) + { + // Check channel, if active. + if (channels[chan]) + { + // Get the raw data from the channel. + sample = *channels[chan]; + // Add left and right part + // for this channel (sound) + // to the current data. + // Adjust volume accordingly. + dl += channelleftvol_lookup[chan][sample]; + dr += channelrightvol_lookup[chan][sample]; + // Increment index ??? + channelstepremainder[chan] += channelstep[chan]; + // MSB is next sample??? + channels[chan] += channelstepremainder[chan] >> 16; + // Limit to LSB??? + channelstepremainder[chan] &= 65536 - 1; + + // Check whether we are done. + if (channels[chan] >= channelsend[chan]) + channels[chan] = 0; + } + } + + // Clamp to range. Left hardware channel. + // Has been char instead of short. + // if (dl > 127) *leftout = 127; + // else if (dl < -128) *leftout = -128; + // else *leftout = dl; + + if (dl > 0x7fff) + *leftout = 0x7fff; + else if (dl < -0x8000) + *leftout = -0x8000; + else + *leftout = dl; + + // Same for right hardware channel. + if (dr > 0x7fff) + *rightout = 0x7fff; + else if (dr < -0x8000) + *rightout = -0x8000; + else + *rightout = dr; + + // Increment current pointers in mixbuffer. + leftout += step; + rightout += step; + } +} + + +//extern doom_sound_callbacks_t doom_sound_callbacks; +//extern doom_boolean skip_next_sound_update; + +// +// This would be used to write out the mixbuffer +// during each game loop update. +// Updates sound buffer and audio device at runtime. +// It is called during Timer interrupt with SNDINTR. +// Mixing now done synchronous, and +// only output be done asynchronous? +// +void I_SubmitSound(void) +{ +} + + +void I_UpdateSoundParams(int handle, int vol, int sep, int pitch) +{ + // I fail too see that this is used. + // Would be using the handle to identify + // on which channel the sound might be active, + // and resetting the channel parameters. +} + + +void I_ShutdownSound(void) +{ + // Wait till all pending sounds are finished. + int done = 0; + int i; + + // FIXME (below). + doom_print("I_ShutdownSound: NOT finishing pending sounds\n"); + + while (!done) + { + for (i = 0; i < 8 && !channels[i]; i++); + + // FIXME. No proper channel output. + //if (i==8) + done = 1; + } + + // Done. + return; +} + + +void I_InitSound() +{ + int i; + + // Secure and configure sound device first. + doom_print("I_InitSound: "); + + // Initialize external data (all sounds) at start, keep static. + doom_print("I_InitSound: "); + + for (i = 1; i < NUMSFX; i++) + { + // Alias? Example is the chaingun sound linked to pistol. + if (!S_sfx[i].link) + { + // Load data from WAD file. + S_sfx[i].data = getsfx(S_sfx[i].name, &lengths[i]); + } + else + { + // Previously loaded already? + S_sfx[i].data = S_sfx[i].link->data; + lengths[i] = lengths[(S_sfx[i].link - S_sfx) / sizeof(sfxinfo_t)]; + } + } + + doom_print(" pre-cached all sound data\n"); + + // Now initialize mixbuffer with zero. + for (i = 0; i < MIXBUFFERSIZE; i++) + mixbuffer[i] = 0; + + // Finished initialization. + doom_print("I_InitSound: sound module ready\n"); +} + + +// +// MUSIC API. +// +void I_InitMusic(void) +{ +} + + +void I_ShutdownMusic(void) +{ +} + + +void I_PlaySong(int handle, int looping) +{ + musicdies = gametic + TICRATE * 30; + + mus_loop = looping ? true : false; + mus_playing = true; +} + + +void I_PauseSong(int handle) +{ + mus_playing = false; +} + + +void I_ResumeSong(int handle) +{ + if (mus_data) mus_playing = true; +} + + +static void reset_all_channels() +{ + for (int i = 0; i < 16; ++i) + queued_midi_msgs[(queue_midi_tail++) % MAX_QUEUED_MIDI_MSGS] = 0b10110000 | i | (123 << 8); +} + + +void I_StopSong(int handle) +{ + mus_data = 0; + mus_delay = 0; + mus_offset = 0; + mus_playing = false; + + reset_all_channels(); +} + + +void I_UnRegisterSong(int handle) +{ + I_StopSong(handle); +} + + +int I_RegisterSong(void* data) +{ + doom_memcpy(&mus_header, data, sizeof(mus_header_t)); + if (doom_strncmp(mus_header.ID, "MUS", 3) != 0 || mus_header.ID[3] != 0x1A) return 0; + + mus_data = (unsigned char*)data; + mus_delay = 0; + mus_offset = mus_header.scoreStart; + mus_playing = false; + + return 1; +} + + +// Is the song playing? +int I_QrySongPlaying(int handle) +{ + return mus_playing; +} + + +unsigned long I_TickSong() +{ + unsigned long midi_event = 0; + + // Dequeue MIDI events + if (queue_midi_head != queue_midi_tail) + return queued_midi_msgs[(queue_midi_head++) % MAX_QUEUED_MIDI_MSGS]; + + if (!mus_playing || !mus_data) return 0; + + if (mus_delay <= 0) + { + int event = (int)mus_data[mus_offset++]; + int type = (event & 0b01110000) >> 4; + int channel = event & 0b00001111; + + if (channel == 15) channel = 9; // Percussion is 9 on GM + else if (channel == 9) channel = 15; + + switch (type) + { + case EVENT_RELEASE_NOTE: + { + int note = (int)mus_data[mus_offset++] & 0b01111111; + midi_event = (0x00000080 | channel | (note << 8)); + break; + } + case EVENT_PLAY_NOTE: + { + int note_bytes = (int)mus_data[mus_offset++]; + int note = note_bytes & 0b01111111; + int vol = 127; + if (note_bytes & 0b10000000) vol = (int)mus_data[mus_offset++] & 0b01111111; + midi_event = (0x00000090 | channel | (note << 8) | (vol << 16)); + break; + } + case EVENT_PITCH_BEND: + { + int bend_amount = (int)mus_data[mus_offset++] * 64; + int l = bend_amount & 0b01111111; + int m = (bend_amount & 0b1111111110000000) >> 7; + midi_event = (0x000000E0 | channel | (l << 8) | (m << 16)); + break; + } + case EVENT_SYSTEM_EVENT: + { + int controller = (int)mus_data[mus_offset++] & 0b01111111; + switch (controller) + { + case CONTROLLER_EVENT_ALL_SOUNDS_OFF: + midi_event = (0x000000B0 | channel | (120 << 8)); + break; + case CONTROLLER_EVENT_ALL_NOTES_OFF: + midi_event = (0x000000B0 | channel | (123 << 8)); + break; + case CONTROLLER_EVENT_MONO: + midi_event = (0x000000B0 | channel | (126 << 8)); + break; + case CONTROLLER_EVENT_POLY: + midi_event = (0x000000B0 | channel | (127 << 8)); + break; + case CONTROLLER_EVENT_RESET_ALL_CONTROLLERS: + midi_event = (0x000000B0 | channel | (121 << 8)); + break; + case CONTROLLER_EVENT_EVENT: // Doom never implemented + break; + } + break; + } + case EVENT_CONTROLLER: + { + int controller = (int)mus_data[mus_offset++] & 0b01111111; + int value = (int)mus_data[mus_offset++] & 0b01111111; + switch (controller) + { + case CONTROLLER_CHANGE_INSTRUMENT: + midi_event = (0x000000C0 | channel | (value << 8)); + break; + case CONTROLLER_BANK_SELECT: + midi_event = (0x000000B0 | channel | 0x2000 | (value << 16)); + break; + case CONTROLLER_MODULATION: + midi_event = (0x000000B0 | channel | 0x0100 | (value << 16)); + break; + case CONTROLLER_VOLUME: + mus_channel_volumes[channel] = value; + midi_event = (0x000000B0 | channel | 0x0700 | (((mus_channel_volumes[channel] * mus_volume) / 127) << 16)); + break; + case CONTROLLER_PAN: + midi_event = (0x000000B0 | channel | 0x0A00 | (value << 16)); + break; + case CONTROLLER_EXPRESSION: + midi_event = (0x000000B0 | channel | 0x0B00 | (value << 16)); + break; + case CONTROLLER_REVERB: + midi_event = (0x000000B0 | channel | 0x5B00 | (value << 16)); + break; + case CONTROLLER_CHORUS: + midi_event = (0x000000B0 | channel | 0x5D00 | (value << 16)); + break; + case CONTROLLER_SUSTAIN: + midi_event = (0x000000B0 | channel | 0x4000 | (value << 16)); + break; + case CONTROLLER_SOFT: + midi_event = (0x000000B0 | channel | 0x4300 | (value << 16)); + break; + } + break; + } + case EVENT_END_OF_MEASURE: + { + break; + } + case EVENT_FINISH: + { + // Loop + if (mus_loop) + { + mus_delay = 0; + mus_offset = mus_header.scoreStart; + break; + } + else + { + mus_playing = false; + return 0; + } + } + case EVENT_UNUSED: + { + int dummy = (int)mus_data[mus_offset++]; + break; + } + } + + if (event & 0b10000000) // Followed by delay + { + mus_delay = 0; + int delay_byte = 0; + do + { + delay_byte = mus_data[mus_offset++]; + mus_delay = mus_delay * 128 + delay_byte & 0b01111111; + } while (delay_byte & 0b10000000); + + return midi_event; + } + } + + mus_delay--; + + return midi_event; +} diff --git a/src/DOOM/i_sound.h b/src/DOOM/i_sound.h new file mode 100644 index 0000000..9cc9b33 --- /dev/null +++ b/src/DOOM/i_sound.h @@ -0,0 +1,110 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// +// DESCRIPTION: +// System interface, sound. +// +//----------------------------------------------------------------------------- + +#ifndef __I_SOUND__ +#define __I_SOUND__ + + +#include "doomdef.h" + + + +#include "doomstat.h" +#include "sounds.h" + + +// Init at program start... +void I_InitSound(); + +// ... update sound buffer and audio device at runtime... +void I_UpdateSound(void); +void I_SubmitSound(void); + +// ... shut down and relase at program termination. +void I_ShutdownSound(void); + + +// +// SFX I/O +// + +// Initialize channels? +void I_SetChannels(); + +// Get raw data lump index for sound descriptor. +int I_GetSfxLumpNum(sfxinfo_t* sfxinfo); + +// Starts a sound in a particular sound channel. +int I_StartSound(int id, int vol, int sep, int pitch, int priority); + +// Stops a sound channel. +void I_StopSound(int handle); + +// Called by S_*() functions +// to see if a channel is still playing. +// Returns 0 if no longer playing, 1 if playing. +int I_SoundIsPlaying(int handle); + +// Updates the volume, separation, +// and pitch of a sound channel. +void I_UpdateSoundParams(int handle, int vol, int sep, int pitch); + + +// +// MUSIC I/O +// +void I_InitMusic(void); +void I_ShutdownMusic(void); + +// Volume. +void I_SetMusicVolume(int volume); + +// PAUSE game handling. +void I_PauseSong(int handle); +void I_ResumeSong(int handle); + +// Registers a song handle to song data. +int I_RegisterSong(void* data); + +// Called by anything that wishes to start music. +// plays a song, and when the song is done, +// starts playing it again in an endless loop. +// Horrible thing to do, considering. +void I_PlaySong(int handle, int looping); + +// Stops a song over 3 seconds. +void I_StopSong(int handle); + +// See above (register), then think backwards +void I_UnRegisterSong(int handle); + +// Get next MIDI message +unsigned long I_TickSong(); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/i_system.c b/src/DOOM/i_system.c new file mode 100644 index 0000000..7f9a336 --- /dev/null +++ b/src/DOOM/i_system.c @@ -0,0 +1,160 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "doomdef.h" +#include "m_misc.h" +#include "i_video.h" +#include "i_sound.h" +#include "d_net.h" +#include "g_game.h" +#include "i_system.h" + + +int mb_used = 6 * (sizeof(void*) / 4); +ticcmd_t emptycmd; + + +extern doom_boolean demorecording; + + +void I_Tactile(int on, int off, int total) +{ +} + + +ticcmd_t* I_BaseTiccmd(void) +{ + return &emptycmd; +} + + +int I_GetHeapSize(void) +{ + return mb_used * 1024 * 1024; +} + + +byte* I_ZoneBase(int* size) +{ + *size = mb_used * 1024 * 1024; + return (byte*)doom_malloc(*size); +} + + +// +// I_GetTime +// returns time in 1/70th second tics +// +int I_GetTime(void) +{ + int sec, usec; + int newtics; + static int basetime = 0; + + doom_gettime(&sec, &usec); + if (!basetime) + basetime = sec; + newtics = (sec - basetime) * TICRATE + usec * TICRATE / 1000000; + return newtics; +} + + +// +// I_Init +// +void I_Init(void) +{ + I_InitSound(); +} + + +// +// I_Quit +// +void I_Quit(void) +{ + D_QuitNetGame(); + I_ShutdownSound(); + I_ShutdownMusic(); + M_SaveDefaults(); + I_ShutdownGraphics(); + doom_exit(0); +} + + +void I_WaitVBL(int count) +{ +#if 0 // [pd] Never sleep in main thread +#ifdef SGI + sginap(1); +#else +#ifdef SUN + sleep(0); +#else + usleep(count * (1000000 / 70)); +#endif +#endif +#endif +} + + +void I_BeginRead(void) +{ +} + + +void I_EndRead(void) +{ +} + + +byte* I_AllocLow(int length) +{ + byte* mem; + + mem = (byte*)doom_malloc(length); + doom_memset(mem, 0, length); + return mem; +} + + +// +// I_Error +// +void I_Error(char* error) +{ + // Message first. + if (error) doom_print(error); + doom_print("\n"); + + // Shutdown. Here might be other errors. + if (demorecording) + G_CheckDemoStatus(); + + D_QuitNetGame(); + I_ShutdownGraphics(); + + doom_exit(-1); +} diff --git a/src/DOOM/i_system.h b/src/DOOM/i_system.h new file mode 100644 index 0000000..30ba31f --- /dev/null +++ b/src/DOOM/i_system.h @@ -0,0 +1,85 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// System specific interface stuff. +// +//----------------------------------------------------------------------------- + +#ifndef __I_SYSTEM__ +#define __I_SYSTEM__ + + +#include "d_ticcmd.h" +#include "d_event.h" + + +// Called by DoomMain. +void I_Init(void); + +// Called by startup code +// to get the ammount of memory to malloc +// for the zone management. +byte* I_ZoneBase(int* size); + +// Called by D_DoomLoop, +// returns current time in tics. +int I_GetTime(void); + +// Called by D_DoomLoop, +// called before processing any tics in a frame +// (just after displaying a frame). +// Time consuming syncronous operations +// are performed here (joystick reading). +// Can call D_PostEvent. +void I_StartFrame(void); + +// Called by D_DoomLoop, +// called before processing each tic in a frame. +// Quick syncronous operations are performed here. +// Can call D_PostEvent. +void I_StartTic(void); + +// Asynchronous interrupt functions should maintain private queues +// that are read by the synchronous functions +// to be converted into events. + +// Either returns a null ticcmd, +// or calls a loadable driver to build it. +// This ticcmd will then be modified by the gameloop +// for normal input. +ticcmd_t* I_BaseTiccmd(void); + +// Called by M_Responder when quit is selected. +// Clean exit, displays sell blurb. +void I_Quit(void); + +// Allocates from low memory under dos, +// just mallocs under unix +byte* I_AllocLow(int length); + +void I_Tactile(int on, int off, int total); + +void I_Error(char* error); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/i_video.c b/src/DOOM/i_video.c new file mode 100644 index 0000000..d1c2709 --- /dev/null +++ b/src/DOOM/i_video.c @@ -0,0 +1,143 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// DOOM graphics stuff for X11, UNIX. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "doomstat.h" +#include "i_system.h" +#include "v_video.h" +#include "m_argv.h" +#include "d_main.h" +#include "doomdef.h" + + +#define POINTER_WARP_COUNTDOWN 1 + + +// Fake mouse handling. +// This cannot work properly w/o DGA. +// Needs an invisible mouse cursor at least. +doom_boolean grabMouse; +int doPointerWarp = POINTER_WARP_COUNTDOWN; + +unsigned char screen_palette[256 * 3]; + +doom_boolean mousemoved = false; +doom_boolean shmFinished; + + +// Blocky mode, +// replace each 320x200 pixel with multiply*multiply pixels. +// According to Dave Taylor, it still is a bonehead thing +// to use .... +static int multiply = 1; +static int lastmousex = 0; +static int lastmousey = 0; + + +void I_ShutdownGraphics(void) +{ +} + + +// +// I_StartFrame +// +void I_StartFrame(void) +{ +} + + +void I_GetEvent(void) +{ +} + + +// +// I_StartTic +// +void I_StartTic(void) +{ +} + + +// +// I_UpdateNoBlit +// +void I_UpdateNoBlit(void) +{ + // what is this? +} + + +// +// I_FinishUpdate +// +void I_FinishUpdate(void) +{ + static int lasttic; + int tics; + int i; + + // draws little dots on the bottom of the screen + if (devparm) + { + i = I_GetTime(); + tics = i - lasttic; + lasttic = i; + if (tics > 20) tics = 20; + + for (i = 0; i < tics * 2; i += 2) + screens[0][(SCREENHEIGHT - 1) * SCREENWIDTH + i] = 0xff; + for (; i < 20 * 2; i += 2) + screens[0][(SCREENHEIGHT - 1) * SCREENWIDTH + i] = 0x0; + + } +} + + +// +// I_ReadScreen +// +void I_ReadScreen(byte* scr) +{ + doom_memcpy(scr, screens[0], SCREENWIDTH * SCREENHEIGHT); +} + + +// +// I_SetPalette +// +void I_SetPalette(byte* palette) +{ + doom_memcpy(screen_palette, palette, 256 * 3); +} + + +void I_InitGraphics(void) +{ + screens[0] = (unsigned char*)doom_malloc(SCREENWIDTH * SCREENHEIGHT); +} diff --git a/src/DOOM/i_video.h b/src/DOOM/i_video.h new file mode 100644 index 0000000..f6856ab --- /dev/null +++ b/src/DOOM/i_video.h @@ -0,0 +1,57 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// System specific interface stuff. +// +//----------------------------------------------------------------------------- + +#ifndef __I_VIDEO__ +#define __I_VIDEO__ + + +#include "doomtype.h" + + +// Called by D_DoomMain, +// determines the hardware configuration +// and sets up the video mode +void I_InitGraphics(void); + +void I_ShutdownGraphics(void); + +// Takes full 8 bit values. +void I_SetPalette(byte* palette); + +void I_UpdateNoBlit(void); +void I_FinishUpdate(void); + +// Wait for vertical retrace or pause a bit. +void I_WaitVBL(int count); + +void I_ReadScreen(byte* scr); + +void I_BeginRead(void); +void I_EndRead(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/info.c b/src/DOOM/info.c new file mode 100644 index 0000000..c0fd34c --- /dev/null +++ b/src/DOOM/info.c @@ -0,0 +1,4663 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Thing frame/state LUT, +// generated by multigen utilitiy. +// This one is the original DOOM version, preserved. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "sounds.h" // Data. +#include "m_fixed.h" // Data. +#include "info.h" +#include "p_mobj.h" + + +char* sprnames[NUMSPRITES + 1] = { + "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",0 +}; + + +void A_Light0(); +void A_WeaponReady(); +void A_Lower(); +void A_Raise(); +void A_Punch(); +void A_ReFire(); +void A_FirePistol(); +void A_Light1(); +void A_FireShotgun(); +void A_Light2(); +void A_FireShotgun2(); +void A_CheckReload(); +void A_OpenShotgun2(); +void A_LoadShotgun2(); +void A_CloseShotgun2(); +void A_FireCGun(); +void A_GunFlash(); +void A_FireMissile(); +void A_Saw(); +void A_FirePlasma(); +void A_BFGsound(); +void A_FireBFG(); +void A_BFGSpray(); +void A_Explode(); +void A_Pain(); +void A_PlayerScream(); +void A_Fall(); +void A_XScream(); +void A_Look(); +void A_Chase(); +void A_FaceTarget(); +void A_PosAttack(); +void A_Scream(); +void A_SPosAttack(); +void A_VileChase(); +void A_VileStart(); +void A_VileTarget(); +void A_VileAttack(); +void A_StartFire(); +void A_Fire(); +void A_FireCrackle(); +void A_Tracer(); +void A_SkelWhoosh(); +void A_SkelFist(); +void A_SkelMissile(); +void A_FatRaise(); +void A_FatAttack1(); +void A_FatAttack2(); +void A_FatAttack3(); +void A_BossDeath(); +void A_CPosAttack(); +void A_CPosRefire(); +void A_TroopAttack(); +void A_SargAttack(); +void A_HeadAttack(); +void A_BruisAttack(); +void A_SkullAttack(); +void A_Metal(); +void A_SpidRefire(); +void A_BabyMetal(); +void A_BspiAttack(); +void A_Hoof(); +void A_CyberAttack(); +void A_PainAttack(); +void A_PainDie(); +void A_KeenDie(); +void A_BrainPain(); +void A_BrainScream(); +void A_BrainDie(); +void A_BrainAwake(); +void A_BrainSpit(); +void A_SpawnSound(); +void A_SpawnFly(); +void A_BrainExplode(); + + +state_t states[NUMSTATES] = { + {SPR_TROO,0,-1,{0},S_NULL,0,0}, // S_NULL + {SPR_SHTG,4,0,{(actionf_p1)A_Light0},S_NULL,0,0}, // S_LIGHTDONE + {SPR_PUNG,0,1,{(actionf_p1)A_WeaponReady},S_PUNCH,0,0}, // S_PUNCH + {SPR_PUNG,0,1,{(actionf_p1)A_Lower},S_PUNCHDOWN,0,0}, // S_PUNCHDOWN + {SPR_PUNG,0,1,{(actionf_p1)A_Raise},S_PUNCHUP,0,0}, // S_PUNCHUP + {SPR_PUNG,1,4,{0},S_PUNCH2,0,0}, // S_PUNCH1 + {SPR_PUNG,2,4,{(actionf_p1)A_Punch},S_PUNCH3,0,0}, // S_PUNCH2 + {SPR_PUNG,3,5,{0},S_PUNCH4,0,0}, // S_PUNCH3 + {SPR_PUNG,2,4,{0},S_PUNCH5,0,0}, // S_PUNCH4 + {SPR_PUNG,1,5,{(actionf_p1)A_ReFire},S_PUNCH,0,0}, // S_PUNCH5 + {SPR_PISG,0,1,{(actionf_p1)A_WeaponReady},S_PISTOL,0,0},// S_PISTOL + {SPR_PISG,0,1,{(actionf_p1)A_Lower},S_PISTOLDOWN,0,0}, // S_PISTOLDOWN + {SPR_PISG,0,1,{(actionf_p1)A_Raise},S_PISTOLUP,0,0}, // S_PISTOLUP + {SPR_PISG,0,4,{0},S_PISTOL2,0,0}, // S_PISTOL1 + {SPR_PISG,1,6,{(actionf_p1)A_FirePistol},S_PISTOL3,0,0},// S_PISTOL2 + {SPR_PISG,2,4,{0},S_PISTOL4,0,0}, // S_PISTOL3 + {SPR_PISG,1,5,{(actionf_p1)A_ReFire},S_PISTOL,0,0}, // S_PISTOL4 + {SPR_PISF,32768,7,{(actionf_p1)A_Light1},S_LIGHTDONE,0,0}, // S_PISTOLFLASH + {SPR_SHTG,0,1,{(actionf_p1)A_WeaponReady},S_SGUN,0,0}, // S_SGUN + {SPR_SHTG,0,1,{(actionf_p1)A_Lower},S_SGUNDOWN,0,0}, // S_SGUNDOWN + {SPR_SHTG,0,1,{(actionf_p1)A_Raise},S_SGUNUP,0,0}, // S_SGUNUP + {SPR_SHTG,0,3,{0},S_SGUN2,0,0}, // S_SGUN1 + {SPR_SHTG,0,7,{(actionf_p1)A_FireShotgun},S_SGUN3,0,0}, // S_SGUN2 + {SPR_SHTG,1,5,{0},S_SGUN4,0,0}, // S_SGUN3 + {SPR_SHTG,2,5,{0},S_SGUN5,0,0}, // S_SGUN4 + {SPR_SHTG,3,4,{0},S_SGUN6,0,0}, // S_SGUN5 + {SPR_SHTG,2,5,{0},S_SGUN7,0,0}, // S_SGUN6 + {SPR_SHTG,1,5,{0},S_SGUN8,0,0}, // S_SGUN7 + {SPR_SHTG,0,3,{0},S_SGUN9,0,0}, // S_SGUN8 + {SPR_SHTG,0,7,{(actionf_p1)A_ReFire},S_SGUN,0,0}, // S_SGUN9 + {SPR_SHTF,32768,4,{(actionf_p1)A_Light1},S_SGUNFLASH2,0,0}, // S_SGUNFLASH1 + {SPR_SHTF,32769,3,{(actionf_p1)A_Light2},S_LIGHTDONE,0,0}, // S_SGUNFLASH2 + {SPR_SHT2,0,1,{(actionf_p1)A_WeaponReady},S_DSGUN,0,0}, // S_DSGUN + {SPR_SHT2,0,1,{(actionf_p1)A_Lower},S_DSGUNDOWN,0,0}, // S_DSGUNDOWN + {SPR_SHT2,0,1,{(actionf_p1)A_Raise},S_DSGUNUP,0,0}, // S_DSGUNUP + {SPR_SHT2,0,3,{0},S_DSGUN2,0,0}, // S_DSGUN1 + {SPR_SHT2,0,7,{(actionf_p1)A_FireShotgun2},S_DSGUN3,0,0}, // S_DSGUN2 + {SPR_SHT2,1,7,{0},S_DSGUN4,0,0}, // S_DSGUN3 + {SPR_SHT2,2,7,{(actionf_p1)A_CheckReload},S_DSGUN5,0,0}, // S_DSGUN4 + {SPR_SHT2,3,7,{(actionf_p1)A_OpenShotgun2},S_DSGUN6,0,0}, // S_DSGUN5 + {SPR_SHT2,4,7,{0},S_DSGUN7,0,0}, // S_DSGUN6 + {SPR_SHT2,5,7,{(actionf_p1)A_LoadShotgun2},S_DSGUN8,0,0}, // S_DSGUN7 + {SPR_SHT2,6,6,{0},S_DSGUN9,0,0}, // S_DSGUN8 + {SPR_SHT2,7,6,{(actionf_p1)A_CloseShotgun2},S_DSGUN10,0,0}, // S_DSGUN9 + {SPR_SHT2,0,5,{(actionf_p1)A_ReFire},S_DSGUN,0,0}, // S_DSGUN10 + {SPR_SHT2,1,7,{0},S_DSNR2,0,0}, // S_DSNR1 + {SPR_SHT2,0,3,{0},S_DSGUNDOWN,0,0}, // S_DSNR2 + {SPR_SHT2,32776,5,{(actionf_p1)A_Light1},S_DSGUNFLASH2,0,0}, // S_DSGUNFLASH1 + {SPR_SHT2,32777,4,{(actionf_p1)A_Light2},S_LIGHTDONE,0,0}, // S_DSGUNFLASH2 + {SPR_CHGG,0,1,{(actionf_p1)A_WeaponReady},S_CHAIN,0,0}, // S_CHAIN + {SPR_CHGG,0,1,{(actionf_p1)A_Lower},S_CHAINDOWN,0,0}, // S_CHAINDOWN + {SPR_CHGG,0,1,{(actionf_p1)A_Raise},S_CHAINUP,0,0}, // S_CHAINUP + {SPR_CHGG,0,4,{(actionf_p1)A_FireCGun},S_CHAIN2,0,0}, // S_CHAIN1 + {SPR_CHGG,1,4,{(actionf_p1)A_FireCGun},S_CHAIN3,0,0}, // S_CHAIN2 + {SPR_CHGG,1,0,{(actionf_p1)A_ReFire},S_CHAIN,0,0}, // S_CHAIN3 + {SPR_CHGF,32768,5,{(actionf_p1)A_Light1},S_LIGHTDONE,0,0}, // S_CHAINFLASH1 + {SPR_CHGF,32769,5,{(actionf_p1)A_Light2},S_LIGHTDONE,0,0}, // S_CHAINFLASH2 + {SPR_MISG,0,1,{(actionf_p1)A_WeaponReady},S_MISSILE,0,0}, // S_MISSILE + {SPR_MISG,0,1,{(actionf_p1)A_Lower},S_MISSILEDOWN,0,0}, // S_MISSILEDOWN + {SPR_MISG,0,1,{(actionf_p1)A_Raise},S_MISSILEUP,0,0}, // S_MISSILEUP + {SPR_MISG,1,8,{(actionf_p1)A_GunFlash},S_MISSILE2,0,0}, // S_MISSILE1 + {SPR_MISG,1,12,{(actionf_p1)A_FireMissile},S_MISSILE3,0,0}, // S_MISSILE2 + {SPR_MISG,1,0,{(actionf_p1)A_ReFire},S_MISSILE,0,0}, // S_MISSILE3 + {SPR_MISF,32768,3,{(actionf_p1)A_Light1},S_MISSILEFLASH2,0,0}, // S_MISSILEFLASH1 + {SPR_MISF,32769,4,{0},S_MISSILEFLASH3,0,0}, // S_MISSILEFLASH2 + {SPR_MISF,32770,4,{(actionf_p1)A_Light2},S_MISSILEFLASH4,0,0}, // S_MISSILEFLASH3 + {SPR_MISF,32771,4,{(actionf_p1)A_Light2},S_LIGHTDONE,0,0}, // S_MISSILEFLASH4 + {SPR_SAWG,2,4,{(actionf_p1)A_WeaponReady},S_SAWB,0,0}, // S_SAW + {SPR_SAWG,3,4,{(actionf_p1)A_WeaponReady},S_SAW,0,0}, // S_SAWB + {SPR_SAWG,2,1,{(actionf_p1)A_Lower},S_SAWDOWN,0,0}, // S_SAWDOWN + {SPR_SAWG,2,1,{(actionf_p1)A_Raise},S_SAWUP,0,0}, // S_SAWUP + {SPR_SAWG,0,4,{(actionf_p1)A_Saw},S_SAW2,0,0}, // S_SAW1 + {SPR_SAWG,1,4,{(actionf_p1)A_Saw},S_SAW3,0,0}, // S_SAW2 + {SPR_SAWG,1,0,{(actionf_p1)A_ReFire},S_SAW,0,0}, // S_SAW3 + {SPR_PLSG,0,1,{(actionf_p1)A_WeaponReady},S_PLASMA,0,0}, // S_PLASMA + {SPR_PLSG,0,1,{(actionf_p1)A_Lower},S_PLASMADOWN,0,0}, // S_PLASMADOWN + {SPR_PLSG,0,1,{(actionf_p1)A_Raise},S_PLASMAUP,0,0}, // S_PLASMAUP + {SPR_PLSG,0,3,{(actionf_p1)A_FirePlasma},S_PLASMA2,0,0}, // S_PLASMA1 + {SPR_PLSG,1,20,{(actionf_p1)A_ReFire},S_PLASMA,0,0}, // S_PLASMA2 + {SPR_PLSF,32768,4,{(actionf_p1)A_Light1},S_LIGHTDONE,0,0}, // S_PLASMAFLASH1 + {SPR_PLSF,32769,4,{(actionf_p1)A_Light1},S_LIGHTDONE,0,0}, // S_PLASMAFLASH2 + {SPR_BFGG,0,1,{(actionf_p1)A_WeaponReady},S_BFG,0,0}, // S_BFG + {SPR_BFGG,0,1,{(actionf_p1)A_Lower},S_BFGDOWN,0,0}, // S_BFGDOWN + {SPR_BFGG,0,1,{(actionf_p1)A_Raise},S_BFGUP,0,0}, // S_BFGUP + {SPR_BFGG,0,20,{(actionf_p1)A_BFGsound},S_BFG2,0,0}, // S_BFG1 + {SPR_BFGG,1,10,{(actionf_p1)A_GunFlash},S_BFG3,0,0}, // S_BFG2 + {SPR_BFGG,1,10,{(actionf_p1)A_FireBFG},S_BFG4,0,0}, // S_BFG3 + {SPR_BFGG,1,20,{(actionf_p1)A_ReFire},S_BFG,0,0}, // S_BFG4 + {SPR_BFGF,32768,11,{(actionf_p1)A_Light1},S_BFGFLASH2,0,0}, // S_BFGFLASH1 + {SPR_BFGF,32769,6,{(actionf_p1)A_Light2},S_LIGHTDONE,0,0}, // S_BFGFLASH2 + {SPR_BLUD,2,8,{0},S_BLOOD2,0,0}, // S_BLOOD1 + {SPR_BLUD,1,8,{0},S_BLOOD3,0,0}, // S_BLOOD2 + {SPR_BLUD,0,8,{0},S_NULL,0,0}, // S_BLOOD3 + {SPR_PUFF,32768,4,{0},S_PUFF2,0,0}, // S_PUFF1 + {SPR_PUFF,1,4,{0},S_PUFF3,0,0}, // S_PUFF2 + {SPR_PUFF,2,4,{0},S_PUFF4,0,0}, // S_PUFF3 + {SPR_PUFF,3,4,{0},S_NULL,0,0}, // S_PUFF4 + {SPR_BAL1,32768,4,{0},S_TBALL2,0,0}, // S_TBALL1 + {SPR_BAL1,32769,4,{0},S_TBALL1,0,0}, // S_TBALL2 + {SPR_BAL1,32770,6,{0},S_TBALLX2,0,0}, // S_TBALLX1 + {SPR_BAL1,32771,6,{0},S_TBALLX3,0,0}, // S_TBALLX2 + {SPR_BAL1,32772,6,{0},S_NULL,0,0}, // S_TBALLX3 + {SPR_BAL2,32768,4,{0},S_RBALL2,0,0}, // S_RBALL1 + {SPR_BAL2,32769,4,{0},S_RBALL1,0,0}, // S_RBALL2 + {SPR_BAL2,32770,6,{0},S_RBALLX2,0,0}, // S_RBALLX1 + {SPR_BAL2,32771,6,{0},S_RBALLX3,0,0}, // S_RBALLX2 + {SPR_BAL2,32772,6,{0},S_NULL,0,0}, // S_RBALLX3 + {SPR_PLSS,32768,6,{0},S_PLASBALL2,0,0}, // S_PLASBALL + {SPR_PLSS,32769,6,{0},S_PLASBALL,0,0}, // S_PLASBALL2 + {SPR_PLSE,32768,4,{0},S_PLASEXP2,0,0}, // S_PLASEXP + {SPR_PLSE,32769,4,{0},S_PLASEXP3,0,0}, // S_PLASEXP2 + {SPR_PLSE,32770,4,{0},S_PLASEXP4,0,0}, // S_PLASEXP3 + {SPR_PLSE,32771,4,{0},S_PLASEXP5,0,0}, // S_PLASEXP4 + {SPR_PLSE,32772,4,{0},S_NULL,0,0}, // S_PLASEXP5 + {SPR_MISL,32768,1,{0},S_ROCKET,0,0}, // S_ROCKET + {SPR_BFS1,32768,4,{0},S_BFGSHOT2,0,0}, // S_BFGSHOT + {SPR_BFS1,32769,4,{0},S_BFGSHOT,0,0}, // S_BFGSHOT2 + {SPR_BFE1,32768,8,{0},S_BFGLAND2,0,0}, // S_BFGLAND + {SPR_BFE1,32769,8,{0},S_BFGLAND3,0,0}, // S_BFGLAND2 + {SPR_BFE1,32770,8,{(actionf_p1)A_BFGSpray},S_BFGLAND4,0,0}, // S_BFGLAND3 + {SPR_BFE1,32771,8,{0},S_BFGLAND5,0,0}, // S_BFGLAND4 + {SPR_BFE1,32772,8,{0},S_BFGLAND6,0,0}, // S_BFGLAND5 + {SPR_BFE1,32773,8,{0},S_NULL,0,0}, // S_BFGLAND6 + {SPR_BFE2,32768,8,{0},S_BFGEXP2,0,0}, // S_BFGEXP + {SPR_BFE2,32769,8,{0},S_BFGEXP3,0,0}, // S_BFGEXP2 + {SPR_BFE2,32770,8,{0},S_BFGEXP4,0,0}, // S_BFGEXP3 + {SPR_BFE2,32771,8,{0},S_NULL,0,0}, // S_BFGEXP4 + {SPR_MISL,32769,8,{(actionf_p1)A_Explode},S_EXPLODE2,0,0}, // S_EXPLODE1 + {SPR_MISL,32770,6,{0},S_EXPLODE3,0,0}, // S_EXPLODE2 + {SPR_MISL,32771,4,{0},S_NULL,0,0}, // S_EXPLODE3 + {SPR_TFOG,32768,6,{0},S_TFOG01,0,0}, // S_TFOG + {SPR_TFOG,32769,6,{0},S_TFOG02,0,0}, // S_TFOG01 + {SPR_TFOG,32768,6,{0},S_TFOG2,0,0}, // S_TFOG02 + {SPR_TFOG,32769,6,{0},S_TFOG3,0,0}, // S_TFOG2 + {SPR_TFOG,32770,6,{0},S_TFOG4,0,0}, // S_TFOG3 + {SPR_TFOG,32771,6,{0},S_TFOG5,0,0}, // S_TFOG4 + {SPR_TFOG,32772,6,{0},S_TFOG6,0,0}, // S_TFOG5 + {SPR_TFOG,32773,6,{0},S_TFOG7,0,0}, // S_TFOG6 + {SPR_TFOG,32774,6,{0},S_TFOG8,0,0}, // S_TFOG7 + {SPR_TFOG,32775,6,{0},S_TFOG9,0,0}, // S_TFOG8 + {SPR_TFOG,32776,6,{0},S_TFOG10,0,0}, // S_TFOG9 + {SPR_TFOG,32777,6,{0},S_NULL,0,0}, // S_TFOG10 + {SPR_IFOG,32768,6,{0},S_IFOG01,0,0}, // S_IFOG + {SPR_IFOG,32769,6,{0},S_IFOG02,0,0}, // S_IFOG01 + {SPR_IFOG,32768,6,{0},S_IFOG2,0,0}, // S_IFOG02 + {SPR_IFOG,32769,6,{0},S_IFOG3,0,0}, // S_IFOG2 + {SPR_IFOG,32770,6,{0},S_IFOG4,0,0}, // S_IFOG3 + {SPR_IFOG,32771,6,{0},S_IFOG5,0,0}, // S_IFOG4 + {SPR_IFOG,32772,6,{0},S_NULL,0,0}, // S_IFOG5 + {SPR_PLAY,0,-1,{0},S_NULL,0,0}, // S_PLAY + {SPR_PLAY,0,4,{0},S_PLAY_RUN2,0,0}, // S_PLAY_RUN1 + {SPR_PLAY,1,4,{0},S_PLAY_RUN3,0,0}, // S_PLAY_RUN2 + {SPR_PLAY,2,4,{0},S_PLAY_RUN4,0,0}, // S_PLAY_RUN3 + {SPR_PLAY,3,4,{0},S_PLAY_RUN1,0,0}, // S_PLAY_RUN4 + {SPR_PLAY,4,12,{0},S_PLAY,0,0}, // S_PLAY_ATK1 + {SPR_PLAY,32773,6,{0},S_PLAY_ATK1,0,0}, // S_PLAY_ATK2 + {SPR_PLAY,6,4,{0},S_PLAY_PAIN2,0,0}, // S_PLAY_PAIN + {SPR_PLAY,6,4,{(actionf_p1)A_Pain},S_PLAY,0,0}, // S_PLAY_PAIN2 + {SPR_PLAY,7,10,{0},S_PLAY_DIE2,0,0}, // S_PLAY_DIE1 + {SPR_PLAY,8,10,{(actionf_p1)A_PlayerScream},S_PLAY_DIE3,0,0}, // S_PLAY_DIE2 + {SPR_PLAY,9,10,{(actionf_p1)A_Fall},S_PLAY_DIE4,0,0}, // S_PLAY_DIE3 + {SPR_PLAY,10,10,{0},S_PLAY_DIE5,0,0}, // S_PLAY_DIE4 + {SPR_PLAY,11,10,{0},S_PLAY_DIE6,0,0}, // S_PLAY_DIE5 + {SPR_PLAY,12,10,{0},S_PLAY_DIE7,0,0}, // S_PLAY_DIE6 + {SPR_PLAY,13,-1,{0},S_NULL,0,0}, // S_PLAY_DIE7 + {SPR_PLAY,14,5,{0},S_PLAY_XDIE2,0,0}, // S_PLAY_XDIE1 + {SPR_PLAY,15,5,{(actionf_p1)A_XScream},S_PLAY_XDIE3,0,0}, // S_PLAY_XDIE2 + {SPR_PLAY,16,5,{(actionf_p1)A_Fall},S_PLAY_XDIE4,0,0}, // S_PLAY_XDIE3 + {SPR_PLAY,17,5,{0},S_PLAY_XDIE5,0,0}, // S_PLAY_XDIE4 + {SPR_PLAY,18,5,{0},S_PLAY_XDIE6,0,0}, // S_PLAY_XDIE5 + {SPR_PLAY,19,5,{0},S_PLAY_XDIE7,0,0}, // S_PLAY_XDIE6 + {SPR_PLAY,20,5,{0},S_PLAY_XDIE8,0,0}, // S_PLAY_XDIE7 + {SPR_PLAY,21,5,{0},S_PLAY_XDIE9,0,0}, // S_PLAY_XDIE8 + {SPR_PLAY,22,-1,{0},S_NULL,0,0}, // S_PLAY_XDIE9 + {SPR_POSS,0,10,{(actionf_p1)A_Look},S_POSS_STND2,0,0}, // S_POSS_STND + {SPR_POSS,1,10,{(actionf_p1)A_Look},S_POSS_STND,0,0}, // S_POSS_STND2 + {SPR_POSS,0,4,{(actionf_p1)A_Chase},S_POSS_RUN2,0,0}, // S_POSS_RUN1 + {SPR_POSS,0,4,{(actionf_p1)A_Chase},S_POSS_RUN3,0,0}, // S_POSS_RUN2 + {SPR_POSS,1,4,{(actionf_p1)A_Chase},S_POSS_RUN4,0,0}, // S_POSS_RUN3 + {SPR_POSS,1,4,{(actionf_p1)A_Chase},S_POSS_RUN5,0,0}, // S_POSS_RUN4 + {SPR_POSS,2,4,{(actionf_p1)A_Chase},S_POSS_RUN6,0,0}, // S_POSS_RUN5 + {SPR_POSS,2,4,{(actionf_p1)A_Chase},S_POSS_RUN7,0,0}, // S_POSS_RUN6 + {SPR_POSS,3,4,{(actionf_p1)A_Chase},S_POSS_RUN8,0,0}, // S_POSS_RUN7 + {SPR_POSS,3,4,{(actionf_p1)A_Chase},S_POSS_RUN1,0,0}, // S_POSS_RUN8 + {SPR_POSS,4,10,{(actionf_p1)A_FaceTarget},S_POSS_ATK2,0,0}, // S_POSS_ATK1 + {SPR_POSS,5,8,{(actionf_p1)A_PosAttack},S_POSS_ATK3,0,0}, // S_POSS_ATK2 + {SPR_POSS,4,8,{0},S_POSS_RUN1,0,0}, // S_POSS_ATK3 + {SPR_POSS,6,3,{0},S_POSS_PAIN2,0,0}, // S_POSS_PAIN + {SPR_POSS,6,3,{(actionf_p1)A_Pain},S_POSS_RUN1,0,0}, // S_POSS_PAIN2 + {SPR_POSS,7,5,{0},S_POSS_DIE2,0,0}, // S_POSS_DIE1 + {SPR_POSS,8,5,{(actionf_p1)A_Scream},S_POSS_DIE3,0,0}, // S_POSS_DIE2 + {SPR_POSS,9,5,{(actionf_p1)A_Fall},S_POSS_DIE4,0,0}, // S_POSS_DIE3 + {SPR_POSS,10,5,{0},S_POSS_DIE5,0,0}, // S_POSS_DIE4 + {SPR_POSS,11,-1,{0},S_NULL,0,0}, // S_POSS_DIE5 + {SPR_POSS,12,5,{0},S_POSS_XDIE2,0,0}, // S_POSS_XDIE1 + {SPR_POSS,13,5,{(actionf_p1)A_XScream},S_POSS_XDIE3,0,0}, // S_POSS_XDIE2 + {SPR_POSS,14,5,{(actionf_p1)A_Fall},S_POSS_XDIE4,0,0}, // S_POSS_XDIE3 + {SPR_POSS,15,5,{0},S_POSS_XDIE5,0,0}, // S_POSS_XDIE4 + {SPR_POSS,16,5,{0},S_POSS_XDIE6,0,0}, // S_POSS_XDIE5 + {SPR_POSS,17,5,{0},S_POSS_XDIE7,0,0}, // S_POSS_XDIE6 + {SPR_POSS,18,5,{0},S_POSS_XDIE8,0,0}, // S_POSS_XDIE7 + {SPR_POSS,19,5,{0},S_POSS_XDIE9,0,0}, // S_POSS_XDIE8 + {SPR_POSS,20,-1,{0},S_NULL,0,0}, // S_POSS_XDIE9 + {SPR_POSS,10,5,{0},S_POSS_RAISE2,0,0}, // S_POSS_RAISE1 + {SPR_POSS,9,5,{0},S_POSS_RAISE3,0,0}, // S_POSS_RAISE2 + {SPR_POSS,8,5,{0},S_POSS_RAISE4,0,0}, // S_POSS_RAISE3 + {SPR_POSS,7,5,{0},S_POSS_RUN1,0,0}, // S_POSS_RAISE4 + {SPR_SPOS,0,10,{(actionf_p1)A_Look},S_SPOS_STND2,0,0}, // S_SPOS_STND + {SPR_SPOS,1,10,{(actionf_p1)A_Look},S_SPOS_STND,0,0}, // S_SPOS_STND2 + {SPR_SPOS,0,3,{(actionf_p1)A_Chase},S_SPOS_RUN2,0,0}, // S_SPOS_RUN1 + {SPR_SPOS,0,3,{(actionf_p1)A_Chase},S_SPOS_RUN3,0,0}, // S_SPOS_RUN2 + {SPR_SPOS,1,3,{(actionf_p1)A_Chase},S_SPOS_RUN4,0,0}, // S_SPOS_RUN3 + {SPR_SPOS,1,3,{(actionf_p1)A_Chase},S_SPOS_RUN5,0,0}, // S_SPOS_RUN4 + {SPR_SPOS,2,3,{(actionf_p1)A_Chase},S_SPOS_RUN6,0,0}, // S_SPOS_RUN5 + {SPR_SPOS,2,3,{(actionf_p1)A_Chase},S_SPOS_RUN7,0,0}, // S_SPOS_RUN6 + {SPR_SPOS,3,3,{(actionf_p1)A_Chase},S_SPOS_RUN8,0,0}, // S_SPOS_RUN7 + {SPR_SPOS,3,3,{(actionf_p1)A_Chase},S_SPOS_RUN1,0,0}, // S_SPOS_RUN8 + {SPR_SPOS,4,10,{(actionf_p1)A_FaceTarget},S_SPOS_ATK2,0,0}, // S_SPOS_ATK1 + {SPR_SPOS,32773,10,{(actionf_p1)A_SPosAttack},S_SPOS_ATK3,0,0}, // S_SPOS_ATK2 + {SPR_SPOS,4,10,{0},S_SPOS_RUN1,0,0}, // S_SPOS_ATK3 + {SPR_SPOS,6,3,{0},S_SPOS_PAIN2,0,0}, // S_SPOS_PAIN + {SPR_SPOS,6,3,{(actionf_p1)A_Pain},S_SPOS_RUN1,0,0}, // S_SPOS_PAIN2 + {SPR_SPOS,7,5,{0},S_SPOS_DIE2,0,0}, // S_SPOS_DIE1 + {SPR_SPOS,8,5,{(actionf_p1)A_Scream},S_SPOS_DIE3,0,0}, // S_SPOS_DIE2 + {SPR_SPOS,9,5,{(actionf_p1)A_Fall},S_SPOS_DIE4,0,0}, // S_SPOS_DIE3 + {SPR_SPOS,10,5,{0},S_SPOS_DIE5,0,0}, // S_SPOS_DIE4 + {SPR_SPOS,11,-1,{0},S_NULL,0,0}, // S_SPOS_DIE5 + {SPR_SPOS,12,5,{0},S_SPOS_XDIE2,0,0}, // S_SPOS_XDIE1 + {SPR_SPOS,13,5,{(actionf_p1)A_XScream},S_SPOS_XDIE3,0,0}, // S_SPOS_XDIE2 + {SPR_SPOS,14,5,{(actionf_p1)A_Fall},S_SPOS_XDIE4,0,0}, // S_SPOS_XDIE3 + {SPR_SPOS,15,5,{0},S_SPOS_XDIE5,0,0}, // S_SPOS_XDIE4 + {SPR_SPOS,16,5,{0},S_SPOS_XDIE6,0,0}, // S_SPOS_XDIE5 + {SPR_SPOS,17,5,{0},S_SPOS_XDIE7,0,0}, // S_SPOS_XDIE6 + {SPR_SPOS,18,5,{0},S_SPOS_XDIE8,0,0}, // S_SPOS_XDIE7 + {SPR_SPOS,19,5,{0},S_SPOS_XDIE9,0,0}, // S_SPOS_XDIE8 + {SPR_SPOS,20,-1,{0},S_NULL,0,0}, // S_SPOS_XDIE9 + {SPR_SPOS,11,5,{0},S_SPOS_RAISE2,0,0}, // S_SPOS_RAISE1 + {SPR_SPOS,10,5,{0},S_SPOS_RAISE3,0,0}, // S_SPOS_RAISE2 + {SPR_SPOS,9,5,{0},S_SPOS_RAISE4,0,0}, // S_SPOS_RAISE3 + {SPR_SPOS,8,5,{0},S_SPOS_RAISE5,0,0}, // S_SPOS_RAISE4 + {SPR_SPOS,7,5,{0},S_SPOS_RUN1,0,0}, // S_SPOS_RAISE5 + {SPR_VILE,0,10,{(actionf_p1)A_Look},S_VILE_STND2,0,0}, // S_VILE_STND + {SPR_VILE,1,10,{(actionf_p1)A_Look},S_VILE_STND,0,0}, // S_VILE_STND2 + {SPR_VILE,0,2,{(actionf_p1)A_VileChase},S_VILE_RUN2,0,0}, // S_VILE_RUN1 + {SPR_VILE,0,2,{(actionf_p1)A_VileChase},S_VILE_RUN3,0,0}, // S_VILE_RUN2 + {SPR_VILE,1,2,{(actionf_p1)A_VileChase},S_VILE_RUN4,0,0}, // S_VILE_RUN3 + {SPR_VILE,1,2,{(actionf_p1)A_VileChase},S_VILE_RUN5,0,0}, // S_VILE_RUN4 + {SPR_VILE,2,2,{(actionf_p1)A_VileChase},S_VILE_RUN6,0,0}, // S_VILE_RUN5 + {SPR_VILE,2,2,{(actionf_p1)A_VileChase},S_VILE_RUN7,0,0}, // S_VILE_RUN6 + {SPR_VILE,3,2,{(actionf_p1)A_VileChase},S_VILE_RUN8,0,0}, // S_VILE_RUN7 + {SPR_VILE,3,2,{(actionf_p1)A_VileChase},S_VILE_RUN9,0,0}, // S_VILE_RUN8 + {SPR_VILE,4,2,{(actionf_p1)A_VileChase},S_VILE_RUN10,0,0}, // S_VILE_RUN9 + {SPR_VILE,4,2,{(actionf_p1)A_VileChase},S_VILE_RUN11,0,0}, // S_VILE_RUN10 + {SPR_VILE,5,2,{(actionf_p1)A_VileChase},S_VILE_RUN12,0,0}, // S_VILE_RUN11 + {SPR_VILE,5,2,{(actionf_p1)A_VileChase},S_VILE_RUN1,0,0}, // S_VILE_RUN12 + {SPR_VILE,32774,0,{(actionf_p1)A_VileStart},S_VILE_ATK2,0,0}, // S_VILE_ATK1 + {SPR_VILE,32774,10,{(actionf_p1)A_FaceTarget},S_VILE_ATK3,0,0}, // S_VILE_ATK2 + {SPR_VILE,32775,8,{(actionf_p1)A_VileTarget},S_VILE_ATK4,0,0}, // S_VILE_ATK3 + {SPR_VILE,32776,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK5,0,0}, // S_VILE_ATK4 + {SPR_VILE,32777,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK6,0,0}, // S_VILE_ATK5 + {SPR_VILE,32778,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK7,0,0}, // S_VILE_ATK6 + {SPR_VILE,32779,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK8,0,0}, // S_VILE_ATK7 + {SPR_VILE,32780,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK9,0,0}, // S_VILE_ATK8 + {SPR_VILE,32781,8,{(actionf_p1)A_FaceTarget},S_VILE_ATK10,0,0}, // S_VILE_ATK9 + {SPR_VILE,32782,8,{(actionf_p1)A_VileAttack},S_VILE_ATK11,0,0}, // S_VILE_ATK10 + {SPR_VILE,32783,20,{0},S_VILE_RUN1,0,0}, // S_VILE_ATK11 + {SPR_VILE,32794,10,{0},S_VILE_HEAL2,0,0}, // S_VILE_HEAL1 + {SPR_VILE,32795,10,{0},S_VILE_HEAL3,0,0}, // S_VILE_HEAL2 + {SPR_VILE,32796,10,{0},S_VILE_RUN1,0,0}, // S_VILE_HEAL3 + {SPR_VILE,16,5,{0},S_VILE_PAIN2,0,0}, // S_VILE_PAIN + {SPR_VILE,16,5,{(actionf_p1)A_Pain},S_VILE_RUN1,0,0}, // S_VILE_PAIN2 + {SPR_VILE,16,7,{0},S_VILE_DIE2,0,0}, // S_VILE_DIE1 + {SPR_VILE,17,7,{(actionf_p1)A_Scream},S_VILE_DIE3,0,0}, // S_VILE_DIE2 + {SPR_VILE,18,7,{(actionf_p1)A_Fall},S_VILE_DIE4,0,0}, // S_VILE_DIE3 + {SPR_VILE,19,7,{0},S_VILE_DIE5,0,0}, // S_VILE_DIE4 + {SPR_VILE,20,7,{0},S_VILE_DIE6,0,0}, // S_VILE_DIE5 + {SPR_VILE,21,7,{0},S_VILE_DIE7,0,0}, // S_VILE_DIE6 + {SPR_VILE,22,7,{0},S_VILE_DIE8,0,0}, // S_VILE_DIE7 + {SPR_VILE,23,5,{0},S_VILE_DIE9,0,0}, // S_VILE_DIE8 + {SPR_VILE,24,5,{0},S_VILE_DIE10,0,0}, // S_VILE_DIE9 + {SPR_VILE,25,-1,{0},S_NULL,0,0}, // S_VILE_DIE10 + {SPR_FIRE,32768,2,{(actionf_p1)A_StartFire},S_FIRE2,0,0}, // S_FIRE1 + {SPR_FIRE,32769,2,{(actionf_p1)A_Fire},S_FIRE3,0,0}, // S_FIRE2 + {SPR_FIRE,32768,2,{(actionf_p1)A_Fire},S_FIRE4,0,0}, // S_FIRE3 + {SPR_FIRE,32769,2,{(actionf_p1)A_Fire},S_FIRE5,0,0}, // S_FIRE4 + {SPR_FIRE,32770,2,{(actionf_p1)A_FireCrackle},S_FIRE6,0,0}, // S_FIRE5 + {SPR_FIRE,32769,2,{(actionf_p1)A_Fire},S_FIRE7,0,0}, // S_FIRE6 + {SPR_FIRE,32770,2,{(actionf_p1)A_Fire},S_FIRE8,0,0}, // S_FIRE7 + {SPR_FIRE,32769,2,{(actionf_p1)A_Fire},S_FIRE9,0,0}, // S_FIRE8 + {SPR_FIRE,32770,2,{(actionf_p1)A_Fire},S_FIRE10,0,0}, // S_FIRE9 + {SPR_FIRE,32771,2,{(actionf_p1)A_Fire},S_FIRE11,0,0}, // S_FIRE10 + {SPR_FIRE,32770,2,{(actionf_p1)A_Fire},S_FIRE12,0,0}, // S_FIRE11 + {SPR_FIRE,32771,2,{(actionf_p1)A_Fire},S_FIRE13,0,0}, // S_FIRE12 + {SPR_FIRE,32770,2,{(actionf_p1)A_Fire},S_FIRE14,0,0}, // S_FIRE13 + {SPR_FIRE,32771,2,{(actionf_p1)A_Fire},S_FIRE15,0,0}, // S_FIRE14 + {SPR_FIRE,32772,2,{(actionf_p1)A_Fire},S_FIRE16,0,0}, // S_FIRE15 + {SPR_FIRE,32771,2,{(actionf_p1)A_Fire},S_FIRE17,0,0}, // S_FIRE16 + {SPR_FIRE,32772,2,{(actionf_p1)A_Fire},S_FIRE18,0,0}, // S_FIRE17 + {SPR_FIRE,32771,2,{(actionf_p1)A_Fire},S_FIRE19,0,0}, // S_FIRE18 + {SPR_FIRE,32772,2,{(actionf_p1)A_FireCrackle},S_FIRE20,0,0}, // S_FIRE19 + {SPR_FIRE,32773,2,{(actionf_p1)A_Fire},S_FIRE21,0,0}, // S_FIRE20 + {SPR_FIRE,32772,2,{(actionf_p1)A_Fire},S_FIRE22,0,0}, // S_FIRE21 + {SPR_FIRE,32773,2,{(actionf_p1)A_Fire},S_FIRE23,0,0}, // S_FIRE22 + {SPR_FIRE,32772,2,{(actionf_p1)A_Fire},S_FIRE24,0,0}, // S_FIRE23 + {SPR_FIRE,32773,2,{(actionf_p1)A_Fire},S_FIRE25,0,0}, // S_FIRE24 + {SPR_FIRE,32774,2,{(actionf_p1)A_Fire},S_FIRE26,0,0}, // S_FIRE25 + {SPR_FIRE,32775,2,{(actionf_p1)A_Fire},S_FIRE27,0,0}, // S_FIRE26 + {SPR_FIRE,32774,2,{(actionf_p1)A_Fire},S_FIRE28,0,0}, // S_FIRE27 + {SPR_FIRE,32775,2,{(actionf_p1)A_Fire},S_FIRE29,0,0}, // S_FIRE28 + {SPR_FIRE,32774,2,{(actionf_p1)A_Fire},S_FIRE30,0,0}, // S_FIRE29 + {SPR_FIRE,32775,2,{(actionf_p1)A_Fire},S_NULL,0,0}, // S_FIRE30 + {SPR_PUFF,1,4,{0},S_SMOKE2,0,0}, // S_SMOKE1 + {SPR_PUFF,2,4,{0},S_SMOKE3,0,0}, // S_SMOKE2 + {SPR_PUFF,1,4,{0},S_SMOKE4,0,0}, // S_SMOKE3 + {SPR_PUFF,2,4,{0},S_SMOKE5,0,0}, // S_SMOKE4 + {SPR_PUFF,3,4,{0},S_NULL,0,0}, // S_SMOKE5 + {SPR_FATB,32768,2,{(actionf_p1)A_Tracer},S_TRACER2,0,0}, // S_TRACER + {SPR_FATB,32769,2,{(actionf_p1)A_Tracer},S_TRACER,0,0}, // S_TRACER2 + {SPR_FBXP,32768,8,{0},S_TRACEEXP2,0,0}, // S_TRACEEXP1 + {SPR_FBXP,32769,6,{0},S_TRACEEXP3,0,0}, // S_TRACEEXP2 + {SPR_FBXP,32770,4,{0},S_NULL,0,0}, // S_TRACEEXP3 + {SPR_SKEL,0,10,{(actionf_p1)A_Look},S_SKEL_STND2,0,0}, // S_SKEL_STND + {SPR_SKEL,1,10,{(actionf_p1)A_Look},S_SKEL_STND,0,0}, // S_SKEL_STND2 + {SPR_SKEL,0,2,{(actionf_p1)A_Chase},S_SKEL_RUN2,0,0}, // S_SKEL_RUN1 + {SPR_SKEL,0,2,{(actionf_p1)A_Chase},S_SKEL_RUN3,0,0}, // S_SKEL_RUN2 + {SPR_SKEL,1,2,{(actionf_p1)A_Chase},S_SKEL_RUN4,0,0}, // S_SKEL_RUN3 + {SPR_SKEL,1,2,{(actionf_p1)A_Chase},S_SKEL_RUN5,0,0}, // S_SKEL_RUN4 + {SPR_SKEL,2,2,{(actionf_p1)A_Chase},S_SKEL_RUN6,0,0}, // S_SKEL_RUN5 + {SPR_SKEL,2,2,{(actionf_p1)A_Chase},S_SKEL_RUN7,0,0}, // S_SKEL_RUN6 + {SPR_SKEL,3,2,{(actionf_p1)A_Chase},S_SKEL_RUN8,0,0}, // S_SKEL_RUN7 + {SPR_SKEL,3,2,{(actionf_p1)A_Chase},S_SKEL_RUN9,0,0}, // S_SKEL_RUN8 + {SPR_SKEL,4,2,{(actionf_p1)A_Chase},S_SKEL_RUN10,0,0}, // S_SKEL_RUN9 + {SPR_SKEL,4,2,{(actionf_p1)A_Chase},S_SKEL_RUN11,0,0}, // S_SKEL_RUN10 + {SPR_SKEL,5,2,{(actionf_p1)A_Chase},S_SKEL_RUN12,0,0}, // S_SKEL_RUN11 + {SPR_SKEL,5,2,{(actionf_p1)A_Chase},S_SKEL_RUN1,0,0}, // S_SKEL_RUN12 + {SPR_SKEL,6,0,{(actionf_p1)A_FaceTarget},S_SKEL_FIST2,0,0}, // S_SKEL_FIST1 + {SPR_SKEL,6,6,{(actionf_p1)A_SkelWhoosh},S_SKEL_FIST3,0,0}, // S_SKEL_FIST2 + {SPR_SKEL,7,6,{(actionf_p1)A_FaceTarget},S_SKEL_FIST4,0,0}, // S_SKEL_FIST3 + {SPR_SKEL,8,6,{(actionf_p1)A_SkelFist},S_SKEL_RUN1,0,0}, // S_SKEL_FIST4 + {SPR_SKEL,32777,0,{(actionf_p1)A_FaceTarget},S_SKEL_MISS2,0,0}, // S_SKEL_MISS1 + {SPR_SKEL,32777,10,{(actionf_p1)A_FaceTarget},S_SKEL_MISS3,0,0}, // S_SKEL_MISS2 + {SPR_SKEL,10,10,{(actionf_p1)A_SkelMissile},S_SKEL_MISS4,0,0}, // S_SKEL_MISS3 + {SPR_SKEL,10,10,{(actionf_p1)A_FaceTarget},S_SKEL_RUN1,0,0}, // S_SKEL_MISS4 + {SPR_SKEL,11,5,{0},S_SKEL_PAIN2,0,0}, // S_SKEL_PAIN + {SPR_SKEL,11,5,{(actionf_p1)A_Pain},S_SKEL_RUN1,0,0}, // S_SKEL_PAIN2 + {SPR_SKEL,11,7,{0},S_SKEL_DIE2,0,0}, // S_SKEL_DIE1 + {SPR_SKEL,12,7,{0},S_SKEL_DIE3,0,0}, // S_SKEL_DIE2 + {SPR_SKEL,13,7,{(actionf_p1)A_Scream},S_SKEL_DIE4,0,0}, // S_SKEL_DIE3 + {SPR_SKEL,14,7,{(actionf_p1)A_Fall},S_SKEL_DIE5,0,0}, // S_SKEL_DIE4 + {SPR_SKEL,15,7,{0},S_SKEL_DIE6,0,0}, // S_SKEL_DIE5 + {SPR_SKEL,16,-1,{0},S_NULL,0,0}, // S_SKEL_DIE6 + {SPR_SKEL,16,5,{0},S_SKEL_RAISE2,0,0}, // S_SKEL_RAISE1 + {SPR_SKEL,15,5,{0},S_SKEL_RAISE3,0,0}, // S_SKEL_RAISE2 + {SPR_SKEL,14,5,{0},S_SKEL_RAISE4,0,0}, // S_SKEL_RAISE3 + {SPR_SKEL,13,5,{0},S_SKEL_RAISE5,0,0}, // S_SKEL_RAISE4 + {SPR_SKEL,12,5,{0},S_SKEL_RAISE6,0,0}, // S_SKEL_RAISE5 + {SPR_SKEL,11,5,{0},S_SKEL_RUN1,0,0}, // S_SKEL_RAISE6 + {SPR_MANF,32768,4,{0},S_FATSHOT2,0,0}, // S_FATSHOT1 + {SPR_MANF,32769,4,{0},S_FATSHOT1,0,0}, // S_FATSHOT2 + {SPR_MISL,32769,8,{0},S_FATSHOTX2,0,0}, // S_FATSHOTX1 + {SPR_MISL,32770,6,{0},S_FATSHOTX3,0,0}, // S_FATSHOTX2 + {SPR_MISL,32771,4,{0},S_NULL,0,0}, // S_FATSHOTX3 + {SPR_FATT,0,15,{(actionf_p1)A_Look},S_FATT_STND2,0,0}, // S_FATT_STND + {SPR_FATT,1,15,{(actionf_p1)A_Look},S_FATT_STND,0,0}, // S_FATT_STND2 + {SPR_FATT,0,4,{(actionf_p1)A_Chase},S_FATT_RUN2,0,0}, // S_FATT_RUN1 + {SPR_FATT,0,4,{(actionf_p1)A_Chase},S_FATT_RUN3,0,0}, // S_FATT_RUN2 + {SPR_FATT,1,4,{(actionf_p1)A_Chase},S_FATT_RUN4,0,0}, // S_FATT_RUN3 + {SPR_FATT,1,4,{(actionf_p1)A_Chase},S_FATT_RUN5,0,0}, // S_FATT_RUN4 + {SPR_FATT,2,4,{(actionf_p1)A_Chase},S_FATT_RUN6,0,0}, // S_FATT_RUN5 + {SPR_FATT,2,4,{(actionf_p1)A_Chase},S_FATT_RUN7,0,0}, // S_FATT_RUN6 + {SPR_FATT,3,4,{(actionf_p1)A_Chase},S_FATT_RUN8,0,0}, // S_FATT_RUN7 + {SPR_FATT,3,4,{(actionf_p1)A_Chase},S_FATT_RUN9,0,0}, // S_FATT_RUN8 + {SPR_FATT,4,4,{(actionf_p1)A_Chase},S_FATT_RUN10,0,0}, // S_FATT_RUN9 + {SPR_FATT,4,4,{(actionf_p1)A_Chase},S_FATT_RUN11,0,0}, // S_FATT_RUN10 + {SPR_FATT,5,4,{(actionf_p1)A_Chase},S_FATT_RUN12,0,0}, // S_FATT_RUN11 + {SPR_FATT,5,4,{(actionf_p1)A_Chase},S_FATT_RUN1,0,0}, // S_FATT_RUN12 + {SPR_FATT,6,20,{(actionf_p1)A_FatRaise},S_FATT_ATK2,0,0}, // S_FATT_ATK1 + {SPR_FATT,32775,10,{(actionf_p1)A_FatAttack1},S_FATT_ATK3,0,0}, // S_FATT_ATK2 + {SPR_FATT,8,5,{(actionf_p1)A_FaceTarget},S_FATT_ATK4,0,0}, // S_FATT_ATK3 + {SPR_FATT,6,5,{(actionf_p1)A_FaceTarget},S_FATT_ATK5,0,0}, // S_FATT_ATK4 + {SPR_FATT,32775,10,{(actionf_p1)A_FatAttack2},S_FATT_ATK6,0,0}, // S_FATT_ATK5 + {SPR_FATT,8,5,{(actionf_p1)A_FaceTarget},S_FATT_ATK7,0,0}, // S_FATT_ATK6 + {SPR_FATT,6,5,{(actionf_p1)A_FaceTarget},S_FATT_ATK8,0,0}, // S_FATT_ATK7 + {SPR_FATT,32775,10,{(actionf_p1)A_FatAttack3},S_FATT_ATK9,0,0}, // S_FATT_ATK8 + {SPR_FATT,8,5,{(actionf_p1)A_FaceTarget},S_FATT_ATK10,0,0}, // S_FATT_ATK9 + {SPR_FATT,6,5,{(actionf_p1)A_FaceTarget},S_FATT_RUN1,0,0}, // S_FATT_ATK10 + {SPR_FATT,9,3,{0},S_FATT_PAIN2,0,0}, // S_FATT_PAIN + {SPR_FATT,9,3,{(actionf_p1)A_Pain},S_FATT_RUN1,0,0}, // S_FATT_PAIN2 + {SPR_FATT,10,6,{0},S_FATT_DIE2,0,0}, // S_FATT_DIE1 + {SPR_FATT,11,6,{(actionf_p1)A_Scream},S_FATT_DIE3,0,0}, // S_FATT_DIE2 + {SPR_FATT,12,6,{(actionf_p1)A_Fall},S_FATT_DIE4,0,0}, // S_FATT_DIE3 + {SPR_FATT,13,6,{0},S_FATT_DIE5,0,0}, // S_FATT_DIE4 + {SPR_FATT,14,6,{0},S_FATT_DIE6,0,0}, // S_FATT_DIE5 + {SPR_FATT,15,6,{0},S_FATT_DIE7,0,0}, // S_FATT_DIE6 + {SPR_FATT,16,6,{0},S_FATT_DIE8,0,0}, // S_FATT_DIE7 + {SPR_FATT,17,6,{0},S_FATT_DIE9,0,0}, // S_FATT_DIE8 + {SPR_FATT,18,6,{0},S_FATT_DIE10,0,0}, // S_FATT_DIE9 + {SPR_FATT,19,-1,{(actionf_p1)A_BossDeath},S_NULL,0,0}, // S_FATT_DIE10 + {SPR_FATT,17,5,{0},S_FATT_RAISE2,0,0}, // S_FATT_RAISE1 + {SPR_FATT,16,5,{0},S_FATT_RAISE3,0,0}, // S_FATT_RAISE2 + {SPR_FATT,15,5,{0},S_FATT_RAISE4,0,0}, // S_FATT_RAISE3 + {SPR_FATT,14,5,{0},S_FATT_RAISE5,0,0}, // S_FATT_RAISE4 + {SPR_FATT,13,5,{0},S_FATT_RAISE6,0,0}, // S_FATT_RAISE5 + {SPR_FATT,12,5,{0},S_FATT_RAISE7,0,0}, // S_FATT_RAISE6 + {SPR_FATT,11,5,{0},S_FATT_RAISE8,0,0}, // S_FATT_RAISE7 + {SPR_FATT,10,5,{0},S_FATT_RUN1,0,0}, // S_FATT_RAISE8 + {SPR_CPOS,0,10,{(actionf_p1)A_Look},S_CPOS_STND2,0,0}, // S_CPOS_STND + {SPR_CPOS,1,10,{(actionf_p1)A_Look},S_CPOS_STND,0,0}, // S_CPOS_STND2 + {SPR_CPOS,0,3,{(actionf_p1)A_Chase},S_CPOS_RUN2,0,0}, // S_CPOS_RUN1 + {SPR_CPOS,0,3,{(actionf_p1)A_Chase},S_CPOS_RUN3,0,0}, // S_CPOS_RUN2 + {SPR_CPOS,1,3,{(actionf_p1)A_Chase},S_CPOS_RUN4,0,0}, // S_CPOS_RUN3 + {SPR_CPOS,1,3,{(actionf_p1)A_Chase},S_CPOS_RUN5,0,0}, // S_CPOS_RUN4 + {SPR_CPOS,2,3,{(actionf_p1)A_Chase},S_CPOS_RUN6,0,0}, // S_CPOS_RUN5 + {SPR_CPOS,2,3,{(actionf_p1)A_Chase},S_CPOS_RUN7,0,0}, // S_CPOS_RUN6 + {SPR_CPOS,3,3,{(actionf_p1)A_Chase},S_CPOS_RUN8,0,0}, // S_CPOS_RUN7 + {SPR_CPOS,3,3,{(actionf_p1)A_Chase},S_CPOS_RUN1,0,0}, // S_CPOS_RUN8 + {SPR_CPOS,4,10,{(actionf_p1)A_FaceTarget},S_CPOS_ATK2,0,0}, // S_CPOS_ATK1 + {SPR_CPOS,32773,4,{(actionf_p1)A_CPosAttack},S_CPOS_ATK3,0,0}, // S_CPOS_ATK2 + {SPR_CPOS,32772,4,{(actionf_p1)A_CPosAttack},S_CPOS_ATK4,0,0}, // S_CPOS_ATK3 + {SPR_CPOS,5,1,{(actionf_p1)A_CPosRefire},S_CPOS_ATK2,0,0}, // S_CPOS_ATK4 + {SPR_CPOS,6,3,{0},S_CPOS_PAIN2,0,0}, // S_CPOS_PAIN + {SPR_CPOS,6,3,{(actionf_p1)A_Pain},S_CPOS_RUN1,0,0}, // S_CPOS_PAIN2 + {SPR_CPOS,7,5,{0},S_CPOS_DIE2,0,0}, // S_CPOS_DIE1 + {SPR_CPOS,8,5,{(actionf_p1)A_Scream},S_CPOS_DIE3,0,0}, // S_CPOS_DIE2 + {SPR_CPOS,9,5,{(actionf_p1)A_Fall},S_CPOS_DIE4,0,0}, // S_CPOS_DIE3 + {SPR_CPOS,10,5,{0},S_CPOS_DIE5,0,0}, // S_CPOS_DIE4 + {SPR_CPOS,11,5,{0},S_CPOS_DIE6,0,0}, // S_CPOS_DIE5 + {SPR_CPOS,12,5,{0},S_CPOS_DIE7,0,0}, // S_CPOS_DIE6 + {SPR_CPOS,13,-1,{0},S_NULL,0,0}, // S_CPOS_DIE7 + {SPR_CPOS,14,5,{0},S_CPOS_XDIE2,0,0}, // S_CPOS_XDIE1 + {SPR_CPOS,15,5,{(actionf_p1)A_XScream},S_CPOS_XDIE3,0,0}, // S_CPOS_XDIE2 + {SPR_CPOS,16,5,{(actionf_p1)A_Fall},S_CPOS_XDIE4,0,0}, // S_CPOS_XDIE3 + {SPR_CPOS,17,5,{0},S_CPOS_XDIE5,0,0}, // S_CPOS_XDIE4 + {SPR_CPOS,18,5,{0},S_CPOS_XDIE6,0,0}, // S_CPOS_XDIE5 + {SPR_CPOS,19,-1,{0},S_NULL,0,0}, // S_CPOS_XDIE6 + {SPR_CPOS,13,5,{0},S_CPOS_RAISE2,0,0}, // S_CPOS_RAISE1 + {SPR_CPOS,12,5,{0},S_CPOS_RAISE3,0,0}, // S_CPOS_RAISE2 + {SPR_CPOS,11,5,{0},S_CPOS_RAISE4,0,0}, // S_CPOS_RAISE3 + {SPR_CPOS,10,5,{0},S_CPOS_RAISE5,0,0}, // S_CPOS_RAISE4 + {SPR_CPOS,9,5,{0},S_CPOS_RAISE6,0,0}, // S_CPOS_RAISE5 + {SPR_CPOS,8,5,{0},S_CPOS_RAISE7,0,0}, // S_CPOS_RAISE6 + {SPR_CPOS,7,5,{0},S_CPOS_RUN1,0,0}, // S_CPOS_RAISE7 + {SPR_TROO,0,10,{(actionf_p1)A_Look},S_TROO_STND2,0,0}, // S_TROO_STND + {SPR_TROO,1,10,{(actionf_p1)A_Look},S_TROO_STND,0,0}, // S_TROO_STND2 + {SPR_TROO,0,3,{(actionf_p1)A_Chase},S_TROO_RUN2,0,0}, // S_TROO_RUN1 + {SPR_TROO,0,3,{(actionf_p1)A_Chase},S_TROO_RUN3,0,0}, // S_TROO_RUN2 + {SPR_TROO,1,3,{(actionf_p1)A_Chase},S_TROO_RUN4,0,0}, // S_TROO_RUN3 + {SPR_TROO,1,3,{(actionf_p1)A_Chase},S_TROO_RUN5,0,0}, // S_TROO_RUN4 + {SPR_TROO,2,3,{(actionf_p1)A_Chase},S_TROO_RUN6,0,0}, // S_TROO_RUN5 + {SPR_TROO,2,3,{(actionf_p1)A_Chase},S_TROO_RUN7,0,0}, // S_TROO_RUN6 + {SPR_TROO,3,3,{(actionf_p1)A_Chase},S_TROO_RUN8,0,0}, // S_TROO_RUN7 + {SPR_TROO,3,3,{(actionf_p1)A_Chase},S_TROO_RUN1,0,0}, // S_TROO_RUN8 + {SPR_TROO,4,8,{(actionf_p1)A_FaceTarget},S_TROO_ATK2,0,0}, // S_TROO_ATK1 + {SPR_TROO,5,8,{(actionf_p1)A_FaceTarget},S_TROO_ATK3,0,0}, // S_TROO_ATK2 + {SPR_TROO,6,6,{(actionf_p1)A_TroopAttack},S_TROO_RUN1,0,0}, // S_TROO_ATK3 + {SPR_TROO,7,2,{0},S_TROO_PAIN2,0,0}, // S_TROO_PAIN + {SPR_TROO,7,2,{(actionf_p1)A_Pain},S_TROO_RUN1,0,0}, // S_TROO_PAIN2 + {SPR_TROO,8,8,{0},S_TROO_DIE2,0,0}, // S_TROO_DIE1 + {SPR_TROO,9,8,{(actionf_p1)A_Scream},S_TROO_DIE3,0,0}, // S_TROO_DIE2 + {SPR_TROO,10,6,{0},S_TROO_DIE4,0,0}, // S_TROO_DIE3 + {SPR_TROO,11,6,{(actionf_p1)A_Fall},S_TROO_DIE5,0,0}, // S_TROO_DIE4 + {SPR_TROO,12,-1,{0},S_NULL,0,0}, // S_TROO_DIE5 + {SPR_TROO,13,5,{0},S_TROO_XDIE2,0,0}, // S_TROO_XDIE1 + {SPR_TROO,14,5,{(actionf_p1)A_XScream},S_TROO_XDIE3,0,0}, // S_TROO_XDIE2 + {SPR_TROO,15,5,{0},S_TROO_XDIE4,0,0}, // S_TROO_XDIE3 + {SPR_TROO,16,5,{(actionf_p1)A_Fall},S_TROO_XDIE5,0,0}, // S_TROO_XDIE4 + {SPR_TROO,17,5,{0},S_TROO_XDIE6,0,0}, // S_TROO_XDIE5 + {SPR_TROO,18,5,{0},S_TROO_XDIE7,0,0}, // S_TROO_XDIE6 + {SPR_TROO,19,5,{0},S_TROO_XDIE8,0,0}, // S_TROO_XDIE7 + {SPR_TROO,20,-1,{0},S_NULL,0,0}, // S_TROO_XDIE8 + {SPR_TROO,12,8,{0},S_TROO_RAISE2,0,0}, // S_TROO_RAISE1 + {SPR_TROO,11,8,{0},S_TROO_RAISE3,0,0}, // S_TROO_RAISE2 + {SPR_TROO,10,6,{0},S_TROO_RAISE4,0,0}, // S_TROO_RAISE3 + {SPR_TROO,9,6,{0},S_TROO_RAISE5,0,0}, // S_TROO_RAISE4 + {SPR_TROO,8,6,{0},S_TROO_RUN1,0,0}, // S_TROO_RAISE5 + {SPR_SARG,0,10,{(actionf_p1)A_Look},S_SARG_STND2,0,0}, // S_SARG_STND + {SPR_SARG,1,10,{(actionf_p1)A_Look},S_SARG_STND,0,0}, // S_SARG_STND2 + {SPR_SARG,0,2,{(actionf_p1)A_Chase},S_SARG_RUN2,0,0}, // S_SARG_RUN1 + {SPR_SARG,0,2,{(actionf_p1)A_Chase},S_SARG_RUN3,0,0}, // S_SARG_RUN2 + {SPR_SARG,1,2,{(actionf_p1)A_Chase},S_SARG_RUN4,0,0}, // S_SARG_RUN3 + {SPR_SARG,1,2,{(actionf_p1)A_Chase},S_SARG_RUN5,0,0}, // S_SARG_RUN4 + {SPR_SARG,2,2,{(actionf_p1)A_Chase},S_SARG_RUN6,0,0}, // S_SARG_RUN5 + {SPR_SARG,2,2,{(actionf_p1)A_Chase},S_SARG_RUN7,0,0}, // S_SARG_RUN6 + {SPR_SARG,3,2,{(actionf_p1)A_Chase},S_SARG_RUN8,0,0}, // S_SARG_RUN7 + {SPR_SARG,3,2,{(actionf_p1)A_Chase},S_SARG_RUN1,0,0}, // S_SARG_RUN8 + {SPR_SARG,4,8,{(actionf_p1)A_FaceTarget},S_SARG_ATK2,0,0}, // S_SARG_ATK1 + {SPR_SARG,5,8,{(actionf_p1)A_FaceTarget},S_SARG_ATK3,0,0}, // S_SARG_ATK2 + {SPR_SARG,6,8,{(actionf_p1)A_SargAttack},S_SARG_RUN1,0,0}, // S_SARG_ATK3 + {SPR_SARG,7,2,{0},S_SARG_PAIN2,0,0}, // S_SARG_PAIN + {SPR_SARG,7,2,{(actionf_p1)A_Pain},S_SARG_RUN1,0,0}, // S_SARG_PAIN2 + {SPR_SARG,8,8,{0},S_SARG_DIE2,0,0}, // S_SARG_DIE1 + {SPR_SARG,9,8,{(actionf_p1)A_Scream},S_SARG_DIE3,0,0}, // S_SARG_DIE2 + {SPR_SARG,10,4,{0},S_SARG_DIE4,0,0}, // S_SARG_DIE3 + {SPR_SARG,11,4,{(actionf_p1)A_Fall},S_SARG_DIE5,0,0}, // S_SARG_DIE4 + {SPR_SARG,12,4,{0},S_SARG_DIE6,0,0}, // S_SARG_DIE5 + {SPR_SARG,13,-1,{0},S_NULL,0,0}, // S_SARG_DIE6 + {SPR_SARG,13,5,{0},S_SARG_RAISE2,0,0}, // S_SARG_RAISE1 + {SPR_SARG,12,5,{0},S_SARG_RAISE3,0,0}, // S_SARG_RAISE2 + {SPR_SARG,11,5,{0},S_SARG_RAISE4,0,0}, // S_SARG_RAISE3 + {SPR_SARG,10,5,{0},S_SARG_RAISE5,0,0}, // S_SARG_RAISE4 + {SPR_SARG,9,5,{0},S_SARG_RAISE6,0,0}, // S_SARG_RAISE5 + {SPR_SARG,8,5,{0},S_SARG_RUN1,0,0}, // S_SARG_RAISE6 + {SPR_HEAD,0,10,{(actionf_p1)A_Look},S_HEAD_STND,0,0}, // S_HEAD_STND + {SPR_HEAD,0,3,{(actionf_p1)A_Chase},S_HEAD_RUN1,0,0}, // S_HEAD_RUN1 + {SPR_HEAD,1,5,{(actionf_p1)A_FaceTarget},S_HEAD_ATK2,0,0}, // S_HEAD_ATK1 + {SPR_HEAD,2,5,{(actionf_p1)A_FaceTarget},S_HEAD_ATK3,0,0}, // S_HEAD_ATK2 + {SPR_HEAD,32771,5,{(actionf_p1)A_HeadAttack},S_HEAD_RUN1,0,0}, // S_HEAD_ATK3 + {SPR_HEAD,4,3,{0},S_HEAD_PAIN2,0,0}, // S_HEAD_PAIN + {SPR_HEAD,4,3,{(actionf_p1)A_Pain},S_HEAD_PAIN3,0,0}, // S_HEAD_PAIN2 + {SPR_HEAD,5,6,{0},S_HEAD_RUN1,0,0}, // S_HEAD_PAIN3 + {SPR_HEAD,6,8,{0},S_HEAD_DIE2,0,0}, // S_HEAD_DIE1 + {SPR_HEAD,7,8,{(actionf_p1)A_Scream},S_HEAD_DIE3,0,0}, // S_HEAD_DIE2 + {SPR_HEAD,8,8,{0},S_HEAD_DIE4,0,0}, // S_HEAD_DIE3 + {SPR_HEAD,9,8,{0},S_HEAD_DIE5,0,0}, // S_HEAD_DIE4 + {SPR_HEAD,10,8,{(actionf_p1)A_Fall},S_HEAD_DIE6,0,0}, // S_HEAD_DIE5 + {SPR_HEAD,11,-1,{0},S_NULL,0,0}, // S_HEAD_DIE6 + {SPR_HEAD,11,8,{0},S_HEAD_RAISE2,0,0}, // S_HEAD_RAISE1 + {SPR_HEAD,10,8,{0},S_HEAD_RAISE3,0,0}, // S_HEAD_RAISE2 + {SPR_HEAD,9,8,{0},S_HEAD_RAISE4,0,0}, // S_HEAD_RAISE3 + {SPR_HEAD,8,8,{0},S_HEAD_RAISE5,0,0}, // S_HEAD_RAISE4 + {SPR_HEAD,7,8,{0},S_HEAD_RAISE6,0,0}, // S_HEAD_RAISE5 + {SPR_HEAD,6,8,{0},S_HEAD_RUN1,0,0}, // S_HEAD_RAISE6 + {SPR_BAL7,32768,4,{0},S_BRBALL2,0,0}, // S_BRBALL1 + {SPR_BAL7,32769,4,{0},S_BRBALL1,0,0}, // S_BRBALL2 + {SPR_BAL7,32770,6,{0},S_BRBALLX2,0,0}, // S_BRBALLX1 + {SPR_BAL7,32771,6,{0},S_BRBALLX3,0,0}, // S_BRBALLX2 + {SPR_BAL7,32772,6,{0},S_NULL,0,0}, // S_BRBALLX3 + {SPR_BOSS,0,10,{(actionf_p1)A_Look},S_BOSS_STND2,0,0}, // S_BOSS_STND + {SPR_BOSS,1,10,{(actionf_p1)A_Look},S_BOSS_STND,0,0}, // S_BOSS_STND2 + {SPR_BOSS,0,3,{(actionf_p1)A_Chase},S_BOSS_RUN2,0,0}, // S_BOSS_RUN1 + {SPR_BOSS,0,3,{(actionf_p1)A_Chase},S_BOSS_RUN3,0,0}, // S_BOSS_RUN2 + {SPR_BOSS,1,3,{(actionf_p1)A_Chase},S_BOSS_RUN4,0,0}, // S_BOSS_RUN3 + {SPR_BOSS,1,3,{(actionf_p1)A_Chase},S_BOSS_RUN5,0,0}, // S_BOSS_RUN4 + {SPR_BOSS,2,3,{(actionf_p1)A_Chase},S_BOSS_RUN6,0,0}, // S_BOSS_RUN5 + {SPR_BOSS,2,3,{(actionf_p1)A_Chase},S_BOSS_RUN7,0,0}, // S_BOSS_RUN6 + {SPR_BOSS,3,3,{(actionf_p1)A_Chase},S_BOSS_RUN8,0,0}, // S_BOSS_RUN7 + {SPR_BOSS,3,3,{(actionf_p1)A_Chase},S_BOSS_RUN1,0,0}, // S_BOSS_RUN8 + {SPR_BOSS,4,8,{(actionf_p1)A_FaceTarget},S_BOSS_ATK2,0,0}, // S_BOSS_ATK1 + {SPR_BOSS,5,8,{(actionf_p1)A_FaceTarget},S_BOSS_ATK3,0,0}, // S_BOSS_ATK2 + {SPR_BOSS,6,8,{(actionf_p1)A_BruisAttack},S_BOSS_RUN1,0,0}, // S_BOSS_ATK3 + {SPR_BOSS,7,2,{0},S_BOSS_PAIN2,0,0}, // S_BOSS_PAIN + {SPR_BOSS,7,2,{(actionf_p1)A_Pain},S_BOSS_RUN1,0,0}, // S_BOSS_PAIN2 + {SPR_BOSS,8,8,{0},S_BOSS_DIE2,0,0}, // S_BOSS_DIE1 + {SPR_BOSS,9,8,{(actionf_p1)A_Scream},S_BOSS_DIE3,0,0}, // S_BOSS_DIE2 + {SPR_BOSS,10,8,{0},S_BOSS_DIE4,0,0}, // S_BOSS_DIE3 + {SPR_BOSS,11,8,{(actionf_p1)A_Fall},S_BOSS_DIE5,0,0}, // S_BOSS_DIE4 + {SPR_BOSS,12,8,{0},S_BOSS_DIE6,0,0}, // S_BOSS_DIE5 + {SPR_BOSS,13,8,{0},S_BOSS_DIE7,0,0}, // S_BOSS_DIE6 + {SPR_BOSS,14,-1,{(actionf_p1)A_BossDeath},S_NULL,0,0}, // S_BOSS_DIE7 + {SPR_BOSS,14,8,{0},S_BOSS_RAISE2,0,0}, // S_BOSS_RAISE1 + {SPR_BOSS,13,8,{0},S_BOSS_RAISE3,0,0}, // S_BOSS_RAISE2 + {SPR_BOSS,12,8,{0},S_BOSS_RAISE4,0,0}, // S_BOSS_RAISE3 + {SPR_BOSS,11,8,{0},S_BOSS_RAISE5,0,0}, // S_BOSS_RAISE4 + {SPR_BOSS,10,8,{0},S_BOSS_RAISE6,0,0}, // S_BOSS_RAISE5 + {SPR_BOSS,9,8,{0},S_BOSS_RAISE7,0,0}, // S_BOSS_RAISE6 + {SPR_BOSS,8,8,{0},S_BOSS_RUN1,0,0}, // S_BOSS_RAISE7 + {SPR_BOS2,0,10,{(actionf_p1)A_Look},S_BOS2_STND2,0,0}, // S_BOS2_STND + {SPR_BOS2,1,10,{(actionf_p1)A_Look},S_BOS2_STND,0,0}, // S_BOS2_STND2 + {SPR_BOS2,0,3,{(actionf_p1)A_Chase},S_BOS2_RUN2,0,0}, // S_BOS2_RUN1 + {SPR_BOS2,0,3,{(actionf_p1)A_Chase},S_BOS2_RUN3,0,0}, // S_BOS2_RUN2 + {SPR_BOS2,1,3,{(actionf_p1)A_Chase},S_BOS2_RUN4,0,0}, // S_BOS2_RUN3 + {SPR_BOS2,1,3,{(actionf_p1)A_Chase},S_BOS2_RUN5,0,0}, // S_BOS2_RUN4 + {SPR_BOS2,2,3,{(actionf_p1)A_Chase},S_BOS2_RUN6,0,0}, // S_BOS2_RUN5 + {SPR_BOS2,2,3,{(actionf_p1)A_Chase},S_BOS2_RUN7,0,0}, // S_BOS2_RUN6 + {SPR_BOS2,3,3,{(actionf_p1)A_Chase},S_BOS2_RUN8,0,0}, // S_BOS2_RUN7 + {SPR_BOS2,3,3,{(actionf_p1)A_Chase},S_BOS2_RUN1,0,0}, // S_BOS2_RUN8 + {SPR_BOS2,4,8,{(actionf_p1)A_FaceTarget},S_BOS2_ATK2,0,0}, // S_BOS2_ATK1 + {SPR_BOS2,5,8,{(actionf_p1)A_FaceTarget},S_BOS2_ATK3,0,0}, // S_BOS2_ATK2 + {SPR_BOS2,6,8,{(actionf_p1)A_BruisAttack},S_BOS2_RUN1,0,0}, // S_BOS2_ATK3 + {SPR_BOS2,7,2,{0},S_BOS2_PAIN2,0,0}, // S_BOS2_PAIN + {SPR_BOS2,7,2,{(actionf_p1)A_Pain},S_BOS2_RUN1,0,0}, // S_BOS2_PAIN2 + {SPR_BOS2,8,8,{0},S_BOS2_DIE2,0,0}, // S_BOS2_DIE1 + {SPR_BOS2,9,8,{(actionf_p1)A_Scream},S_BOS2_DIE3,0,0}, // S_BOS2_DIE2 + {SPR_BOS2,10,8,{0},S_BOS2_DIE4,0,0}, // S_BOS2_DIE3 + {SPR_BOS2,11,8,{(actionf_p1)A_Fall},S_BOS2_DIE5,0,0}, // S_BOS2_DIE4 + {SPR_BOS2,12,8,{0},S_BOS2_DIE6,0,0}, // S_BOS2_DIE5 + {SPR_BOS2,13,8,{0},S_BOS2_DIE7,0,0}, // S_BOS2_DIE6 + {SPR_BOS2,14,-1,{0},S_NULL,0,0}, // S_BOS2_DIE7 + {SPR_BOS2,14,8,{0},S_BOS2_RAISE2,0,0}, // S_BOS2_RAISE1 + {SPR_BOS2,13,8,{0},S_BOS2_RAISE3,0,0}, // S_BOS2_RAISE2 + {SPR_BOS2,12,8,{0},S_BOS2_RAISE4,0,0}, // S_BOS2_RAISE3 + {SPR_BOS2,11,8,{0},S_BOS2_RAISE5,0,0}, // S_BOS2_RAISE4 + {SPR_BOS2,10,8,{0},S_BOS2_RAISE6,0,0}, // S_BOS2_RAISE5 + {SPR_BOS2,9,8,{0},S_BOS2_RAISE7,0,0}, // S_BOS2_RAISE6 + {SPR_BOS2,8,8,{0},S_BOS2_RUN1,0,0}, // S_BOS2_RAISE7 + {SPR_SKUL,32768,10,{(actionf_p1)A_Look},S_SKULL_STND2,0,0}, // S_SKULL_STND + {SPR_SKUL,32769,10,{(actionf_p1)A_Look},S_SKULL_STND,0,0}, // S_SKULL_STND2 + {SPR_SKUL,32768,6,{(actionf_p1)A_Chase},S_SKULL_RUN2,0,0}, // S_SKULL_RUN1 + {SPR_SKUL,32769,6,{(actionf_p1)A_Chase},S_SKULL_RUN1,0,0}, // S_SKULL_RUN2 + {SPR_SKUL,32770,10,{(actionf_p1)A_FaceTarget},S_SKULL_ATK2,0,0}, // S_SKULL_ATK1 + {SPR_SKUL,32771,4,{(actionf_p1)A_SkullAttack},S_SKULL_ATK3,0,0}, // S_SKULL_ATK2 + {SPR_SKUL,32770,4,{0},S_SKULL_ATK4,0,0}, // S_SKULL_ATK3 + {SPR_SKUL,32771,4,{0},S_SKULL_ATK3,0,0}, // S_SKULL_ATK4 + {SPR_SKUL,32772,3,{0},S_SKULL_PAIN2,0,0}, // S_SKULL_PAIN + {SPR_SKUL,32772,3,{(actionf_p1)A_Pain},S_SKULL_RUN1,0,0}, // S_SKULL_PAIN2 + {SPR_SKUL,32773,6,{0},S_SKULL_DIE2,0,0}, // S_SKULL_DIE1 + {SPR_SKUL,32774,6,{(actionf_p1)A_Scream},S_SKULL_DIE3,0,0}, // S_SKULL_DIE2 + {SPR_SKUL,32775,6,{0},S_SKULL_DIE4,0,0}, // S_SKULL_DIE3 + {SPR_SKUL,32776,6,{(actionf_p1)A_Fall},S_SKULL_DIE5,0,0}, // S_SKULL_DIE4 + {SPR_SKUL,9,6,{0},S_SKULL_DIE6,0,0}, // S_SKULL_DIE5 + {SPR_SKUL,10,6,{0},S_NULL,0,0}, // S_SKULL_DIE6 + {SPR_SPID,0,10,{(actionf_p1)A_Look},S_SPID_STND2,0,0}, // S_SPID_STND + {SPR_SPID,1,10,{(actionf_p1)A_Look},S_SPID_STND,0,0}, // S_SPID_STND2 + {SPR_SPID,0,3,{(actionf_p1)A_Metal},S_SPID_RUN2,0,0}, // S_SPID_RUN1 + {SPR_SPID,0,3,{(actionf_p1)A_Chase},S_SPID_RUN3,0,0}, // S_SPID_RUN2 + {SPR_SPID,1,3,{(actionf_p1)A_Chase},S_SPID_RUN4,0,0}, // S_SPID_RUN3 + {SPR_SPID,1,3,{(actionf_p1)A_Chase},S_SPID_RUN5,0,0}, // S_SPID_RUN4 + {SPR_SPID,2,3,{(actionf_p1)A_Metal},S_SPID_RUN6,0,0}, // S_SPID_RUN5 + {SPR_SPID,2,3,{(actionf_p1)A_Chase},S_SPID_RUN7,0,0}, // S_SPID_RUN6 + {SPR_SPID,3,3,{(actionf_p1)A_Chase},S_SPID_RUN8,0,0}, // S_SPID_RUN7 + {SPR_SPID,3,3,{(actionf_p1)A_Chase},S_SPID_RUN9,0,0}, // S_SPID_RUN8 + {SPR_SPID,4,3,{(actionf_p1)A_Metal},S_SPID_RUN10,0,0}, // S_SPID_RUN9 + {SPR_SPID,4,3,{(actionf_p1)A_Chase},S_SPID_RUN11,0,0}, // S_SPID_RUN10 + {SPR_SPID,5,3,{(actionf_p1)A_Chase},S_SPID_RUN12,0,0}, // S_SPID_RUN11 + {SPR_SPID,5,3,{(actionf_p1)A_Chase},S_SPID_RUN1,0,0}, // S_SPID_RUN12 + {SPR_SPID,32768,20,{(actionf_p1)A_FaceTarget},S_SPID_ATK2,0,0}, // S_SPID_ATK1 + {SPR_SPID,32774,4,{(actionf_p1)A_SPosAttack},S_SPID_ATK3,0,0}, // S_SPID_ATK2 + {SPR_SPID,32775,4,{(actionf_p1)A_SPosAttack},S_SPID_ATK4,0,0}, // S_SPID_ATK3 + {SPR_SPID,32775,1,{(actionf_p1)A_SpidRefire},S_SPID_ATK2,0,0}, // S_SPID_ATK4 + {SPR_SPID,8,3,{0},S_SPID_PAIN2,0,0}, // S_SPID_PAIN + {SPR_SPID,8,3,{(actionf_p1)A_Pain},S_SPID_RUN1,0,0}, // S_SPID_PAIN2 + {SPR_SPID,9,20,{(actionf_p1)A_Scream},S_SPID_DIE2,0,0}, // S_SPID_DIE1 + {SPR_SPID,10,10,{(actionf_p1)A_Fall},S_SPID_DIE3,0,0}, // S_SPID_DIE2 + {SPR_SPID,11,10,{0},S_SPID_DIE4,0,0}, // S_SPID_DIE3 + {SPR_SPID,12,10,{0},S_SPID_DIE5,0,0}, // S_SPID_DIE4 + {SPR_SPID,13,10,{0},S_SPID_DIE6,0,0}, // S_SPID_DIE5 + {SPR_SPID,14,10,{0},S_SPID_DIE7,0,0}, // S_SPID_DIE6 + {SPR_SPID,15,10,{0},S_SPID_DIE8,0,0}, // S_SPID_DIE7 + {SPR_SPID,16,10,{0},S_SPID_DIE9,0,0}, // S_SPID_DIE8 + {SPR_SPID,17,10,{0},S_SPID_DIE10,0,0}, // S_SPID_DIE9 + {SPR_SPID,18,30,{0},S_SPID_DIE11,0,0}, // S_SPID_DIE10 + {SPR_SPID,18,-1,{(actionf_p1)A_BossDeath},S_NULL,0,0}, // S_SPID_DIE11 + {SPR_BSPI,0,10,{(actionf_p1)A_Look},S_BSPI_STND2,0,0}, // S_BSPI_STND + {SPR_BSPI,1,10,{(actionf_p1)A_Look},S_BSPI_STND,0,0}, // S_BSPI_STND2 + {SPR_BSPI,0,20,{0},S_BSPI_RUN1,0,0}, // S_BSPI_SIGHT + {SPR_BSPI,0,3,{(actionf_p1)A_BabyMetal},S_BSPI_RUN2,0,0}, // S_BSPI_RUN1 + {SPR_BSPI,0,3,{(actionf_p1)A_Chase},S_BSPI_RUN3,0,0}, // S_BSPI_RUN2 + {SPR_BSPI,1,3,{(actionf_p1)A_Chase},S_BSPI_RUN4,0,0}, // S_BSPI_RUN3 + {SPR_BSPI,1,3,{(actionf_p1)A_Chase},S_BSPI_RUN5,0,0}, // S_BSPI_RUN4 + {SPR_BSPI,2,3,{(actionf_p1)A_Chase},S_BSPI_RUN6,0,0}, // S_BSPI_RUN5 + {SPR_BSPI,2,3,{(actionf_p1)A_Chase},S_BSPI_RUN7,0,0}, // S_BSPI_RUN6 + {SPR_BSPI,3,3,{(actionf_p1)A_BabyMetal},S_BSPI_RUN8,0,0}, // S_BSPI_RUN7 + {SPR_BSPI,3,3,{(actionf_p1)A_Chase},S_BSPI_RUN9,0,0}, // S_BSPI_RUN8 + {SPR_BSPI,4,3,{(actionf_p1)A_Chase},S_BSPI_RUN10,0,0}, // S_BSPI_RUN9 + {SPR_BSPI,4,3,{(actionf_p1)A_Chase},S_BSPI_RUN11,0,0}, // S_BSPI_RUN10 + {SPR_BSPI,5,3,{(actionf_p1)A_Chase},S_BSPI_RUN12,0,0}, // S_BSPI_RUN11 + {SPR_BSPI,5,3,{(actionf_p1)A_Chase},S_BSPI_RUN1,0,0}, // S_BSPI_RUN12 + {SPR_BSPI,32768,20,{(actionf_p1)A_FaceTarget},S_BSPI_ATK2,0,0}, // S_BSPI_ATK1 + {SPR_BSPI,32774,4,{(actionf_p1)A_BspiAttack},S_BSPI_ATK3,0,0}, // S_BSPI_ATK2 + {SPR_BSPI,32775,4,{0},S_BSPI_ATK4,0,0}, // S_BSPI_ATK3 + {SPR_BSPI,32775,1,{(actionf_p1)A_SpidRefire},S_BSPI_ATK2,0,0}, // S_BSPI_ATK4 + {SPR_BSPI,8,3,{0},S_BSPI_PAIN2,0,0}, // S_BSPI_PAIN + {SPR_BSPI,8,3,{(actionf_p1)A_Pain},S_BSPI_RUN1,0,0}, // S_BSPI_PAIN2 + {SPR_BSPI,9,20,{(actionf_p1)A_Scream},S_BSPI_DIE2,0,0}, // S_BSPI_DIE1 + {SPR_BSPI,10,7,{(actionf_p1)A_Fall},S_BSPI_DIE3,0,0}, // S_BSPI_DIE2 + {SPR_BSPI,11,7,{0},S_BSPI_DIE4,0,0}, // S_BSPI_DIE3 + {SPR_BSPI,12,7,{0},S_BSPI_DIE5,0,0}, // S_BSPI_DIE4 + {SPR_BSPI,13,7,{0},S_BSPI_DIE6,0,0}, // S_BSPI_DIE5 + {SPR_BSPI,14,7,{0},S_BSPI_DIE7,0,0}, // S_BSPI_DIE6 + {SPR_BSPI,15,-1,{(actionf_p1)A_BossDeath},S_NULL,0,0}, // S_BSPI_DIE7 + {SPR_BSPI,15,5,{0},S_BSPI_RAISE2,0,0}, // S_BSPI_RAISE1 + {SPR_BSPI,14,5,{0},S_BSPI_RAISE3,0,0}, // S_BSPI_RAISE2 + {SPR_BSPI,13,5,{0},S_BSPI_RAISE4,0,0}, // S_BSPI_RAISE3 + {SPR_BSPI,12,5,{0},S_BSPI_RAISE5,0,0}, // S_BSPI_RAISE4 + {SPR_BSPI,11,5,{0},S_BSPI_RAISE6,0,0}, // S_BSPI_RAISE5 + {SPR_BSPI,10,5,{0},S_BSPI_RAISE7,0,0}, // S_BSPI_RAISE6 + {SPR_BSPI,9,5,{0},S_BSPI_RUN1,0,0}, // S_BSPI_RAISE7 + {SPR_APLS,32768,5,{0},S_ARACH_PLAZ2,0,0}, // S_ARACH_PLAZ + {SPR_APLS,32769,5,{0},S_ARACH_PLAZ,0,0}, // S_ARACH_PLAZ2 + {SPR_APBX,32768,5,{0},S_ARACH_PLEX2,0,0}, // S_ARACH_PLEX + {SPR_APBX,32769,5,{0},S_ARACH_PLEX3,0,0}, // S_ARACH_PLEX2 + {SPR_APBX,32770,5,{0},S_ARACH_PLEX4,0,0}, // S_ARACH_PLEX3 + {SPR_APBX,32771,5,{0},S_ARACH_PLEX5,0,0}, // S_ARACH_PLEX4 + {SPR_APBX,32772,5,{0},S_NULL,0,0}, // S_ARACH_PLEX5 + {SPR_CYBR,0,10,{(actionf_p1)A_Look},S_CYBER_STND2,0,0}, // S_CYBER_STND + {SPR_CYBR,1,10,{(actionf_p1)A_Look},S_CYBER_STND,0,0}, // S_CYBER_STND2 + {SPR_CYBR,0,3,{(actionf_p1)A_Hoof},S_CYBER_RUN2,0,0}, // S_CYBER_RUN1 + {SPR_CYBR,0,3,{(actionf_p1)A_Chase},S_CYBER_RUN3,0,0}, // S_CYBER_RUN2 + {SPR_CYBR,1,3,{(actionf_p1)A_Chase},S_CYBER_RUN4,0,0}, // S_CYBER_RUN3 + {SPR_CYBR,1,3,{(actionf_p1)A_Chase},S_CYBER_RUN5,0,0}, // S_CYBER_RUN4 + {SPR_CYBR,2,3,{(actionf_p1)A_Chase},S_CYBER_RUN6,0,0}, // S_CYBER_RUN5 + {SPR_CYBR,2,3,{(actionf_p1)A_Chase},S_CYBER_RUN7,0,0}, // S_CYBER_RUN6 + {SPR_CYBR,3,3,{(actionf_p1)A_Metal},S_CYBER_RUN8,0,0}, // S_CYBER_RUN7 + {SPR_CYBR,3,3,{(actionf_p1)A_Chase},S_CYBER_RUN1,0,0}, // S_CYBER_RUN8 + {SPR_CYBR,4,6,{(actionf_p1)A_FaceTarget},S_CYBER_ATK2,0,0}, // S_CYBER_ATK1 + {SPR_CYBR,5,12,{(actionf_p1)A_CyberAttack},S_CYBER_ATK3,0,0}, // S_CYBER_ATK2 + {SPR_CYBR,4,12,{(actionf_p1)A_FaceTarget},S_CYBER_ATK4,0,0}, // S_CYBER_ATK3 + {SPR_CYBR,5,12,{(actionf_p1)A_CyberAttack},S_CYBER_ATK5,0,0}, // S_CYBER_ATK4 + {SPR_CYBR,4,12,{(actionf_p1)A_FaceTarget},S_CYBER_ATK6,0,0}, // S_CYBER_ATK5 + {SPR_CYBR,5,12,{(actionf_p1)A_CyberAttack},S_CYBER_RUN1,0,0}, // S_CYBER_ATK6 + {SPR_CYBR,6,10,{(actionf_p1)A_Pain},S_CYBER_RUN1,0,0}, // S_CYBER_PAIN + {SPR_CYBR,7,10,{0},S_CYBER_DIE2,0,0}, // S_CYBER_DIE1 + {SPR_CYBR,8,10,{(actionf_p1)A_Scream},S_CYBER_DIE3,0,0}, // S_CYBER_DIE2 + {SPR_CYBR,9,10,{0},S_CYBER_DIE4,0,0}, // S_CYBER_DIE3 + {SPR_CYBR,10,10,{0},S_CYBER_DIE5,0,0}, // S_CYBER_DIE4 + {SPR_CYBR,11,10,{0},S_CYBER_DIE6,0,0}, // S_CYBER_DIE5 + {SPR_CYBR,12,10,{(actionf_p1)A_Fall},S_CYBER_DIE7,0,0}, // S_CYBER_DIE6 + {SPR_CYBR,13,10,{0},S_CYBER_DIE8,0,0}, // S_CYBER_DIE7 + {SPR_CYBR,14,10,{0},S_CYBER_DIE9,0,0}, // S_CYBER_DIE8 + {SPR_CYBR,15,30,{0},S_CYBER_DIE10,0,0}, // S_CYBER_DIE9 + {SPR_CYBR,15,-1,{(actionf_p1)A_BossDeath},S_NULL,0,0}, // S_CYBER_DIE10 + {SPR_PAIN,0,10,{(actionf_p1)A_Look},S_PAIN_STND,0,0}, // S_PAIN_STND + {SPR_PAIN,0,3,{(actionf_p1)A_Chase},S_PAIN_RUN2,0,0}, // S_PAIN_RUN1 + {SPR_PAIN,0,3,{(actionf_p1)A_Chase},S_PAIN_RUN3,0,0}, // S_PAIN_RUN2 + {SPR_PAIN,1,3,{(actionf_p1)A_Chase},S_PAIN_RUN4,0,0}, // S_PAIN_RUN3 + {SPR_PAIN,1,3,{(actionf_p1)A_Chase},S_PAIN_RUN5,0,0}, // S_PAIN_RUN4 + {SPR_PAIN,2,3,{(actionf_p1)A_Chase},S_PAIN_RUN6,0,0}, // S_PAIN_RUN5 + {SPR_PAIN,2,3,{(actionf_p1)A_Chase},S_PAIN_RUN1,0,0}, // S_PAIN_RUN6 + {SPR_PAIN,3,5,{(actionf_p1)A_FaceTarget},S_PAIN_ATK2,0,0}, // S_PAIN_ATK1 + {SPR_PAIN,4,5,{(actionf_p1)A_FaceTarget},S_PAIN_ATK3,0,0}, // S_PAIN_ATK2 + {SPR_PAIN,32773,5,{(actionf_p1)A_FaceTarget},S_PAIN_ATK4,0,0}, // S_PAIN_ATK3 + {SPR_PAIN,32773,0,{(actionf_p1)A_PainAttack},S_PAIN_RUN1,0,0}, // S_PAIN_ATK4 + {SPR_PAIN,6,6,{0},S_PAIN_PAIN2,0,0}, // S_PAIN_PAIN + {SPR_PAIN,6,6,{(actionf_p1)A_Pain},S_PAIN_RUN1,0,0}, // S_PAIN_PAIN2 + {SPR_PAIN,32775,8,{0},S_PAIN_DIE2,0,0}, // S_PAIN_DIE1 + {SPR_PAIN,32776,8,{(actionf_p1)A_Scream},S_PAIN_DIE3,0,0}, // S_PAIN_DIE2 + {SPR_PAIN,32777,8,{0},S_PAIN_DIE4,0,0}, // S_PAIN_DIE3 + {SPR_PAIN,32778,8,{0},S_PAIN_DIE5,0,0}, // S_PAIN_DIE4 + {SPR_PAIN,32779,8,{(actionf_p1)A_PainDie},S_PAIN_DIE6,0,0}, // S_PAIN_DIE5 + {SPR_PAIN,32780,8,{0},S_NULL,0,0}, // S_PAIN_DIE6 + {SPR_PAIN,12,8,{0},S_PAIN_RAISE2,0,0}, // S_PAIN_RAISE1 + {SPR_PAIN,11,8,{0},S_PAIN_RAISE3,0,0}, // S_PAIN_RAISE2 + {SPR_PAIN,10,8,{0},S_PAIN_RAISE4,0,0}, // S_PAIN_RAISE3 + {SPR_PAIN,9,8,{0},S_PAIN_RAISE5,0,0}, // S_PAIN_RAISE4 + {SPR_PAIN,8,8,{0},S_PAIN_RAISE6,0,0}, // S_PAIN_RAISE5 + {SPR_PAIN,7,8,{0},S_PAIN_RUN1,0,0}, // S_PAIN_RAISE6 + {SPR_SSWV,0,10,{(actionf_p1)A_Look},S_SSWV_STND2,0,0}, // S_SSWV_STND + {SPR_SSWV,1,10,{(actionf_p1)A_Look},S_SSWV_STND,0,0}, // S_SSWV_STND2 + {SPR_SSWV,0,3,{(actionf_p1)A_Chase},S_SSWV_RUN2,0,0}, // S_SSWV_RUN1 + {SPR_SSWV,0,3,{(actionf_p1)A_Chase},S_SSWV_RUN3,0,0}, // S_SSWV_RUN2 + {SPR_SSWV,1,3,{(actionf_p1)A_Chase},S_SSWV_RUN4,0,0}, // S_SSWV_RUN3 + {SPR_SSWV,1,3,{(actionf_p1)A_Chase},S_SSWV_RUN5,0,0}, // S_SSWV_RUN4 + {SPR_SSWV,2,3,{(actionf_p1)A_Chase},S_SSWV_RUN6,0,0}, // S_SSWV_RUN5 + {SPR_SSWV,2,3,{(actionf_p1)A_Chase},S_SSWV_RUN7,0,0}, // S_SSWV_RUN6 + {SPR_SSWV,3,3,{(actionf_p1)A_Chase},S_SSWV_RUN8,0,0}, // S_SSWV_RUN7 + {SPR_SSWV,3,3,{(actionf_p1)A_Chase},S_SSWV_RUN1,0,0}, // S_SSWV_RUN8 + {SPR_SSWV,4,10,{(actionf_p1)A_FaceTarget},S_SSWV_ATK2,0,0}, // S_SSWV_ATK1 + {SPR_SSWV,5,10,{(actionf_p1)A_FaceTarget},S_SSWV_ATK3,0,0}, // S_SSWV_ATK2 + {SPR_SSWV,32774,4,{(actionf_p1)A_CPosAttack},S_SSWV_ATK4,0,0}, // S_SSWV_ATK3 + {SPR_SSWV,5,6,{(actionf_p1)A_FaceTarget},S_SSWV_ATK5,0,0}, // S_SSWV_ATK4 + {SPR_SSWV,32774,4,{(actionf_p1)A_CPosAttack},S_SSWV_ATK6,0,0}, // S_SSWV_ATK5 + {SPR_SSWV,5,1,{(actionf_p1)A_CPosRefire},S_SSWV_ATK2,0,0}, // S_SSWV_ATK6 + {SPR_SSWV,7,3,{0},S_SSWV_PAIN2,0,0}, // S_SSWV_PAIN + {SPR_SSWV,7,3,{(actionf_p1)A_Pain},S_SSWV_RUN1,0,0}, // S_SSWV_PAIN2 + {SPR_SSWV,8,5,{0},S_SSWV_DIE2,0,0}, // S_SSWV_DIE1 + {SPR_SSWV,9,5,{(actionf_p1)A_Scream},S_SSWV_DIE3,0,0}, // S_SSWV_DIE2 + {SPR_SSWV,10,5,{(actionf_p1)A_Fall},S_SSWV_DIE4,0,0}, // S_SSWV_DIE3 + {SPR_SSWV,11,5,{0},S_SSWV_DIE5,0,0}, // S_SSWV_DIE4 + {SPR_SSWV,12,-1,{0},S_NULL,0,0}, // S_SSWV_DIE5 + {SPR_SSWV,13,5,{0},S_SSWV_XDIE2,0,0}, // S_SSWV_XDIE1 + {SPR_SSWV,14,5,{(actionf_p1)A_XScream},S_SSWV_XDIE3,0,0}, // S_SSWV_XDIE2 + {SPR_SSWV,15,5,{(actionf_p1)A_Fall},S_SSWV_XDIE4,0,0}, // S_SSWV_XDIE3 + {SPR_SSWV,16,5,{0},S_SSWV_XDIE5,0,0}, // S_SSWV_XDIE4 + {SPR_SSWV,17,5,{0},S_SSWV_XDIE6,0,0}, // S_SSWV_XDIE5 + {SPR_SSWV,18,5,{0},S_SSWV_XDIE7,0,0}, // S_SSWV_XDIE6 + {SPR_SSWV,19,5,{0},S_SSWV_XDIE8,0,0}, // S_SSWV_XDIE7 + {SPR_SSWV,20,5,{0},S_SSWV_XDIE9,0,0}, // S_SSWV_XDIE8 + {SPR_SSWV,21,-1,{0},S_NULL,0,0}, // S_SSWV_XDIE9 + {SPR_SSWV,12,5,{0},S_SSWV_RAISE2,0,0}, // S_SSWV_RAISE1 + {SPR_SSWV,11,5,{0},S_SSWV_RAISE3,0,0}, // S_SSWV_RAISE2 + {SPR_SSWV,10,5,{0},S_SSWV_RAISE4,0,0}, // S_SSWV_RAISE3 + {SPR_SSWV,9,5,{0},S_SSWV_RAISE5,0,0}, // S_SSWV_RAISE4 + {SPR_SSWV,8,5,{0},S_SSWV_RUN1,0,0}, // S_SSWV_RAISE5 + {SPR_KEEN,0,-1,{0},S_KEENSTND,0,0}, // S_KEENSTND + {SPR_KEEN,0,6,{0},S_COMMKEEN2,0,0}, // S_COMMKEEN + {SPR_KEEN,1,6,{0},S_COMMKEEN3,0,0}, // S_COMMKEEN2 + {SPR_KEEN,2,6,{(actionf_p1)A_Scream},S_COMMKEEN4,0,0}, // S_COMMKEEN3 + {SPR_KEEN,3,6,{0},S_COMMKEEN5,0,0}, // S_COMMKEEN4 + {SPR_KEEN,4,6,{0},S_COMMKEEN6,0,0}, // S_COMMKEEN5 + {SPR_KEEN,5,6,{0},S_COMMKEEN7,0,0}, // S_COMMKEEN6 + {SPR_KEEN,6,6,{0},S_COMMKEEN8,0,0}, // S_COMMKEEN7 + {SPR_KEEN,7,6,{0},S_COMMKEEN9,0,0}, // S_COMMKEEN8 + {SPR_KEEN,8,6,{0},S_COMMKEEN10,0,0}, // S_COMMKEEN9 + {SPR_KEEN,9,6,{0},S_COMMKEEN11,0,0}, // S_COMMKEEN10 + {SPR_KEEN,10,6,{(actionf_p1)A_KeenDie},S_COMMKEEN12,0,0},// S_COMMKEEN11 + {SPR_KEEN,11,-1,{0},S_NULL,0,0}, // S_COMMKEEN12 + {SPR_KEEN,12,4,{0},S_KEENPAIN2,0,0}, // S_KEENPAIN + {SPR_KEEN,12,8,{(actionf_p1)A_Pain},S_KEENSTND,0,0}, // S_KEENPAIN2 + {SPR_BBRN,0,-1,{0},S_NULL,0,0}, // S_BRAIN + {SPR_BBRN,1,36,{(actionf_p1)A_BrainPain},S_BRAIN,0,0}, // S_BRAIN_PAIN + {SPR_BBRN,0,100,{(actionf_p1)A_BrainScream},S_BRAIN_DIE2,0,0}, // S_BRAIN_DIE1 + {SPR_BBRN,0,10,{0},S_BRAIN_DIE3,0,0}, // S_BRAIN_DIE2 + {SPR_BBRN,0,10,{0},S_BRAIN_DIE4,0,0}, // S_BRAIN_DIE3 + {SPR_BBRN,0,-1,{(actionf_p1)A_BrainDie},S_NULL,0,0}, // S_BRAIN_DIE4 + {SPR_SSWV,0,10,{(actionf_p1)A_Look},S_BRAINEYE,0,0}, // S_BRAINEYE + {SPR_SSWV,0,181,{(actionf_p1)A_BrainAwake},S_BRAINEYE1,0,0}, // S_BRAINEYESEE + {SPR_SSWV,0,150,{(actionf_p1)A_BrainSpit},S_BRAINEYE1,0,0}, // S_BRAINEYE1 + {SPR_BOSF,32768,3,{(actionf_p1)A_SpawnSound},S_SPAWN2,0,0}, // S_SPAWN1 + {SPR_BOSF,32769,3,{(actionf_p1)A_SpawnFly},S_SPAWN3,0,0}, // S_SPAWN2 + {SPR_BOSF,32770,3,{(actionf_p1)A_SpawnFly},S_SPAWN4,0,0}, // S_SPAWN3 + {SPR_BOSF,32771,3,{(actionf_p1)A_SpawnFly},S_SPAWN1,0,0}, // S_SPAWN4 + {SPR_FIRE,32768,4,{(actionf_p1)A_Fire},S_SPAWNFIRE2,0,0}, // S_SPAWNFIRE1 + {SPR_FIRE,32769,4,{(actionf_p1)A_Fire},S_SPAWNFIRE3,0,0}, // S_SPAWNFIRE2 + {SPR_FIRE,32770,4,{(actionf_p1)A_Fire},S_SPAWNFIRE4,0,0}, // S_SPAWNFIRE3 + {SPR_FIRE,32771,4,{(actionf_p1)A_Fire},S_SPAWNFIRE5,0,0}, // S_SPAWNFIRE4 + {SPR_FIRE,32772,4,{(actionf_p1)A_Fire},S_SPAWNFIRE6,0,0}, // S_SPAWNFIRE5 + {SPR_FIRE,32773,4,{(actionf_p1)A_Fire},S_SPAWNFIRE7,0,0}, // S_SPAWNFIRE6 + {SPR_FIRE,32774,4,{(actionf_p1)A_Fire},S_SPAWNFIRE8,0,0}, // S_SPAWNFIRE7 + {SPR_FIRE,32775,4,{(actionf_p1)A_Fire},S_NULL,0,0}, // S_SPAWNFIRE8 + {SPR_MISL,32769,10,{0},S_BRAINEXPLODE2,0,0}, // S_BRAINEXPLODE1 + {SPR_MISL,32770,10,{0},S_BRAINEXPLODE3,0,0}, // S_BRAINEXPLODE2 + {SPR_MISL,32771,10,{(actionf_p1)A_BrainExplode},S_NULL,0,0}, // S_BRAINEXPLODE3 + {SPR_ARM1,0,6,{0},S_ARM1A,0,0}, // S_ARM1 + {SPR_ARM1,32769,7,{0},S_ARM1,0,0}, // S_ARM1A + {SPR_ARM2,0,6,{0},S_ARM2A,0,0}, // S_ARM2 + {SPR_ARM2,32769,6,{0},S_ARM2,0,0}, // S_ARM2A + {SPR_BAR1,0,6,{0},S_BAR2,0,0}, // S_BAR1 + {SPR_BAR1,1,6,{0},S_BAR1,0,0}, // S_BAR2 + {SPR_BEXP,32768,5,{0},S_BEXP2,0,0}, // S_BEXP + {SPR_BEXP,32769,5,{(actionf_p1)A_Scream},S_BEXP3,0,0}, // S_BEXP2 + {SPR_BEXP,32770,5,{0},S_BEXP4,0,0}, // S_BEXP3 + {SPR_BEXP,32771,10,{(actionf_p1)A_Explode},S_BEXP5,0,0}, // S_BEXP4 + {SPR_BEXP,32772,10,{0},S_NULL,0,0}, // S_BEXP5 + {SPR_FCAN,32768,4,{0},S_BBAR2,0,0}, // S_BBAR1 + {SPR_FCAN,32769,4,{0},S_BBAR3,0,0}, // S_BBAR2 + {SPR_FCAN,32770,4,{0},S_BBAR1,0,0}, // S_BBAR3 + {SPR_BON1,0,6,{0},S_BON1A,0,0}, // S_BON1 + {SPR_BON1,1,6,{0},S_BON1B,0,0}, // S_BON1A + {SPR_BON1,2,6,{0},S_BON1C,0,0}, // S_BON1B + {SPR_BON1,3,6,{0},S_BON1D,0,0}, // S_BON1C + {SPR_BON1,2,6,{0},S_BON1E,0,0}, // S_BON1D + {SPR_BON1,1,6,{0},S_BON1,0,0}, // S_BON1E + {SPR_BON2,0,6,{0},S_BON2A,0,0}, // S_BON2 + {SPR_BON2,1,6,{0},S_BON2B,0,0}, // S_BON2A + {SPR_BON2,2,6,{0},S_BON2C,0,0}, // S_BON2B + {SPR_BON2,3,6,{0},S_BON2D,0,0}, // S_BON2C + {SPR_BON2,2,6,{0},S_BON2E,0,0}, // S_BON2D + {SPR_BON2,1,6,{0},S_BON2,0,0}, // S_BON2E + {SPR_BKEY,0,10,{0},S_BKEY2,0,0}, // S_BKEY + {SPR_BKEY,32769,10,{0},S_BKEY,0,0}, // S_BKEY2 + {SPR_RKEY,0,10,{0},S_RKEY2,0,0}, // S_RKEY + {SPR_RKEY,32769,10,{0},S_RKEY,0,0}, // S_RKEY2 + {SPR_YKEY,0,10,{0},S_YKEY2,0,0}, // S_YKEY + {SPR_YKEY,32769,10,{0},S_YKEY,0,0}, // S_YKEY2 + {SPR_BSKU,0,10,{0},S_BSKULL2,0,0}, // S_BSKULL + {SPR_BSKU,32769,10,{0},S_BSKULL,0,0}, // S_BSKULL2 + {SPR_RSKU,0,10,{0},S_RSKULL2,0,0}, // S_RSKULL + {SPR_RSKU,32769,10,{0},S_RSKULL,0,0}, // S_RSKULL2 + {SPR_YSKU,0,10,{0},S_YSKULL2,0,0}, // S_YSKULL + {SPR_YSKU,32769,10,{0},S_YSKULL,0,0}, // S_YSKULL2 + {SPR_STIM,0,-1,{0},S_NULL,0,0}, // S_STIM + {SPR_MEDI,0,-1,{0},S_NULL,0,0}, // S_MEDI + {SPR_SOUL,32768,6,{0},S_SOUL2,0,0}, // S_SOUL + {SPR_SOUL,32769,6,{0},S_SOUL3,0,0}, // S_SOUL2 + {SPR_SOUL,32770,6,{0},S_SOUL4,0,0}, // S_SOUL3 + {SPR_SOUL,32771,6,{0},S_SOUL5,0,0}, // S_SOUL4 + {SPR_SOUL,32770,6,{0},S_SOUL6,0,0}, // S_SOUL5 + {SPR_SOUL,32769,6,{0},S_SOUL,0,0}, // S_SOUL6 + {SPR_PINV,32768,6,{0},S_PINV2,0,0}, // S_PINV + {SPR_PINV,32769,6,{0},S_PINV3,0,0}, // S_PINV2 + {SPR_PINV,32770,6,{0},S_PINV4,0,0}, // S_PINV3 + {SPR_PINV,32771,6,{0},S_PINV,0,0}, // S_PINV4 + {SPR_PSTR,32768,-1,{0},S_NULL,0,0}, // S_PSTR + {SPR_PINS,32768,6,{0},S_PINS2,0,0}, // S_PINS + {SPR_PINS,32769,6,{0},S_PINS3,0,0}, // S_PINS2 + {SPR_PINS,32770,6,{0},S_PINS4,0,0}, // S_PINS3 + {SPR_PINS,32771,6,{0},S_PINS,0,0}, // S_PINS4 + {SPR_MEGA,32768,6,{0},S_MEGA2,0,0}, // S_MEGA + {SPR_MEGA,32769,6,{0},S_MEGA3,0,0}, // S_MEGA2 + {SPR_MEGA,32770,6,{0},S_MEGA4,0,0}, // S_MEGA3 + {SPR_MEGA,32771,6,{0},S_MEGA,0,0}, // S_MEGA4 + {SPR_SUIT,32768,-1,{0},S_NULL,0,0}, // S_SUIT + {SPR_PMAP,32768,6,{0},S_PMAP2,0,0}, // S_PMAP + {SPR_PMAP,32769,6,{0},S_PMAP3,0,0}, // S_PMAP2 + {SPR_PMAP,32770,6,{0},S_PMAP4,0,0}, // S_PMAP3 + {SPR_PMAP,32771,6,{0},S_PMAP5,0,0}, // S_PMAP4 + {SPR_PMAP,32770,6,{0},S_PMAP6,0,0}, // S_PMAP5 + {SPR_PMAP,32769,6,{0},S_PMAP,0,0}, // S_PMAP6 + {SPR_PVIS,32768,6,{0},S_PVIS2,0,0}, // S_PVIS + {SPR_PVIS,1,6,{0},S_PVIS,0,0}, // S_PVIS2 + {SPR_CLIP,0,-1,{0},S_NULL,0,0}, // S_CLIP + {SPR_AMMO,0,-1,{0},S_NULL,0,0}, // S_AMMO + {SPR_ROCK,0,-1,{0},S_NULL,0,0}, // S_ROCK + {SPR_BROK,0,-1,{0},S_NULL,0,0}, // S_BROK + {SPR_CELL,0,-1,{0},S_NULL,0,0}, // S_CELL + {SPR_CELP,0,-1,{0},S_NULL,0,0}, // S_CELP + {SPR_SHEL,0,-1,{0},S_NULL,0,0}, // S_SHEL + {SPR_SBOX,0,-1,{0},S_NULL,0,0}, // S_SBOX + {SPR_BPAK,0,-1,{0},S_NULL,0,0}, // S_BPAK + {SPR_BFUG,0,-1,{0},S_NULL,0,0}, // S_BFUG + {SPR_MGUN,0,-1,{0},S_NULL,0,0}, // S_MGUN + {SPR_CSAW,0,-1,{0},S_NULL,0,0}, // S_CSAW + {SPR_LAUN,0,-1,{0},S_NULL,0,0}, // S_LAUN + {SPR_PLAS,0,-1,{0},S_NULL,0,0}, // S_PLAS + {SPR_SHOT,0,-1,{0},S_NULL,0,0}, // S_SHOT + {SPR_SGN2,0,-1,{0},S_NULL,0,0}, // S_SHOT2 + {SPR_COLU,32768,-1,{0},S_NULL,0,0}, // S_COLU + {SPR_SMT2,0,-1,{0},S_NULL,0,0}, // S_STALAG + {SPR_GOR1,0,10,{0},S_BLOODYTWITCH2,0,0}, // S_BLOODYTWITCH + {SPR_GOR1,1,15,{0},S_BLOODYTWITCH3,0,0}, // S_BLOODYTWITCH2 + {SPR_GOR1,2,8,{0},S_BLOODYTWITCH4,0,0}, // S_BLOODYTWITCH3 + {SPR_GOR1,1,6,{0},S_BLOODYTWITCH,0,0}, // S_BLOODYTWITCH4 + {SPR_PLAY,13,-1,{0},S_NULL,0,0}, // S_DEADTORSO + {SPR_PLAY,18,-1,{0},S_NULL,0,0}, // S_DEADBOTTOM + {SPR_POL2,0,-1,{0},S_NULL,0,0}, // S_HEADSONSTICK + {SPR_POL5,0,-1,{0},S_NULL,0,0}, // S_GIBS + {SPR_POL4,0,-1,{0},S_NULL,0,0}, // S_HEADONASTICK + {SPR_POL3,32768,6,{0},S_HEADCANDLES2,0,0}, // S_HEADCANDLES + {SPR_POL3,32769,6,{0},S_HEADCANDLES,0,0}, // S_HEADCANDLES2 + {SPR_POL1,0,-1,{0},S_NULL,0,0}, // S_DEADSTICK + {SPR_POL6,0,6,{0},S_LIVESTICK2,0,0}, // S_LIVESTICK + {SPR_POL6,1,8,{0},S_LIVESTICK,0,0}, // S_LIVESTICK2 + {SPR_GOR2,0,-1,{0},S_NULL,0,0}, // S_MEAT2 + {SPR_GOR3,0,-1,{0},S_NULL,0,0}, // S_MEAT3 + {SPR_GOR4,0,-1,{0},S_NULL,0,0}, // S_MEAT4 + {SPR_GOR5,0,-1,{0},S_NULL,0,0}, // S_MEAT5 + {SPR_SMIT,0,-1,{0},S_NULL,0,0}, // S_STALAGTITE + {SPR_COL1,0,-1,{0},S_NULL,0,0}, // S_TALLGRNCOL + {SPR_COL2,0,-1,{0},S_NULL,0,0}, // S_SHRTGRNCOL + {SPR_COL3,0,-1,{0},S_NULL,0,0}, // S_TALLREDCOL + {SPR_COL4,0,-1,{0},S_NULL,0,0}, // S_SHRTREDCOL + {SPR_CAND,32768,-1,{0},S_NULL,0,0}, // S_CANDLESTIK + {SPR_CBRA,32768,-1,{0},S_NULL,0,0}, // S_CANDELABRA + {SPR_COL6,0,-1,{0},S_NULL,0,0}, // S_SKULLCOL + {SPR_TRE1,0,-1,{0},S_NULL,0,0}, // S_TORCHTREE + {SPR_TRE2,0,-1,{0},S_NULL,0,0}, // S_BIGTREE + {SPR_ELEC,0,-1,{0},S_NULL,0,0}, // S_TECHPILLAR + {SPR_CEYE,32768,6,{0},S_EVILEYE2,0,0}, // S_EVILEYE + {SPR_CEYE,32769,6,{0},S_EVILEYE3,0,0}, // S_EVILEYE2 + {SPR_CEYE,32770,6,{0},S_EVILEYE4,0,0}, // S_EVILEYE3 + {SPR_CEYE,32769,6,{0},S_EVILEYE,0,0}, // S_EVILEYE4 + {SPR_FSKU,32768,6,{0},S_FLOATSKULL2,0,0}, // S_FLOATSKULL + {SPR_FSKU,32769,6,{0},S_FLOATSKULL3,0,0}, // S_FLOATSKULL2 + {SPR_FSKU,32770,6,{0},S_FLOATSKULL,0,0}, // S_FLOATSKULL3 + {SPR_COL5,0,14,{0},S_HEARTCOL2,0,0}, // S_HEARTCOL + {SPR_COL5,1,14,{0},S_HEARTCOL,0,0}, // S_HEARTCOL2 + {SPR_TBLU,32768,4,{0},S_BLUETORCH2,0,0}, // S_BLUETORCH + {SPR_TBLU,32769,4,{0},S_BLUETORCH3,0,0}, // S_BLUETORCH2 + {SPR_TBLU,32770,4,{0},S_BLUETORCH4,0,0}, // S_BLUETORCH3 + {SPR_TBLU,32771,4,{0},S_BLUETORCH,0,0}, // S_BLUETORCH4 + {SPR_TGRN,32768,4,{0},S_GREENTORCH2,0,0}, // S_GREENTORCH + {SPR_TGRN,32769,4,{0},S_GREENTORCH3,0,0}, // S_GREENTORCH2 + {SPR_TGRN,32770,4,{0},S_GREENTORCH4,0,0}, // S_GREENTORCH3 + {SPR_TGRN,32771,4,{0},S_GREENTORCH,0,0}, // S_GREENTORCH4 + {SPR_TRED,32768,4,{0},S_REDTORCH2,0,0}, // S_REDTORCH + {SPR_TRED,32769,4,{0},S_REDTORCH3,0,0}, // S_REDTORCH2 + {SPR_TRED,32770,4,{0},S_REDTORCH4,0,0}, // S_REDTORCH3 + {SPR_TRED,32771,4,{0},S_REDTORCH,0,0}, // S_REDTORCH4 + {SPR_SMBT,32768,4,{0},S_BTORCHSHRT2,0,0}, // S_BTORCHSHRT + {SPR_SMBT,32769,4,{0},S_BTORCHSHRT3,0,0}, // S_BTORCHSHRT2 + {SPR_SMBT,32770,4,{0},S_BTORCHSHRT4,0,0}, // S_BTORCHSHRT3 + {SPR_SMBT,32771,4,{0},S_BTORCHSHRT,0,0}, // S_BTORCHSHRT4 + {SPR_SMGT,32768,4,{0},S_GTORCHSHRT2,0,0}, // S_GTORCHSHRT + {SPR_SMGT,32769,4,{0},S_GTORCHSHRT3,0,0}, // S_GTORCHSHRT2 + {SPR_SMGT,32770,4,{0},S_GTORCHSHRT4,0,0}, // S_GTORCHSHRT3 + {SPR_SMGT,32771,4,{0},S_GTORCHSHRT,0,0}, // S_GTORCHSHRT4 + {SPR_SMRT,32768,4,{0},S_RTORCHSHRT2,0,0}, // S_RTORCHSHRT + {SPR_SMRT,32769,4,{0},S_RTORCHSHRT3,0,0}, // S_RTORCHSHRT2 + {SPR_SMRT,32770,4,{0},S_RTORCHSHRT4,0,0}, // S_RTORCHSHRT3 + {SPR_SMRT,32771,4,{0},S_RTORCHSHRT,0,0}, // S_RTORCHSHRT4 + {SPR_HDB1,0,-1,{0},S_NULL,0,0}, // S_HANGNOGUTS + {SPR_HDB2,0,-1,{0},S_NULL,0,0}, // S_HANGBNOBRAIN + {SPR_HDB3,0,-1,{0},S_NULL,0,0}, // S_HANGTLOOKDN + {SPR_HDB4,0,-1,{0},S_NULL,0,0}, // S_HANGTSKULL + {SPR_HDB5,0,-1,{0},S_NULL,0,0}, // S_HANGTLOOKUP + {SPR_HDB6,0,-1,{0},S_NULL,0,0}, // S_HANGTNOBRAIN + {SPR_POB1,0,-1,{0},S_NULL,0,0}, // S_COLONGIBS + {SPR_POB2,0,-1,{0},S_NULL,0,0}, // S_SMALLPOOL + {SPR_BRS1,0,-1,{0},S_NULL,0,0}, // S_BRAINSTEM + {SPR_TLMP,32768,4,{0},S_TECHLAMP2,0,0}, // S_TECHLAMP + {SPR_TLMP,32769,4,{0},S_TECHLAMP3,0,0}, // S_TECHLAMP2 + {SPR_TLMP,32770,4,{0},S_TECHLAMP4,0,0}, // S_TECHLAMP3 + {SPR_TLMP,32771,4,{0},S_TECHLAMP,0,0}, // S_TECHLAMP4 + {SPR_TLP2,32768,4,{0},S_TECH2LAMP2,0,0}, // S_TECH2LAMP + {SPR_TLP2,32769,4,{0},S_TECH2LAMP3,0,0}, // S_TECH2LAMP2 + {SPR_TLP2,32770,4,{0},S_TECH2LAMP4,0,0}, // S_TECH2LAMP3 + {SPR_TLP2,32771,4,{0},S_TECH2LAMP,0,0} // S_TECH2LAMP4 +}; + + +mobjinfo_t mobjinfo[NUMMOBJTYPES] = { + + { // MT_PLAYER + -1, // doomednum + S_PLAY, // spawnstate + 100, // spawnhealth + S_PLAY_RUN1, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_PLAY_PAIN, // painstate + 255, // painchance + sfx_plpain, // painsound + S_NULL, // meleestate + S_PLAY_ATK1, // missilestate + S_PLAY_DIE1, // deathstate + S_PLAY_XDIE1, // xdeathstate + sfx_pldeth, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SHOOTABLE | MF_DROPOFF | MF_PICKUP | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_POSSESSED + 3004, // doomednum + S_POSS_STND, // spawnstate + 20, // spawnhealth + S_POSS_RUN1, // seestate + sfx_posit1, // seesound + 8, // reactiontime + sfx_pistol, // attacksound + S_POSS_PAIN, // painstate + 200, // painchance + sfx_popain, // painsound + 0, // meleestate + S_POSS_ATK1, // missilestate + S_POSS_DIE1, // deathstate + S_POSS_XDIE1, // xdeathstate + sfx_podth1, // deathsound + 8, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_POSS_RAISE1 // raisestate + }, + + { // MT_SHOTGUY + 9, // doomednum + S_SPOS_STND, // spawnstate + 30, // spawnhealth + S_SPOS_RUN1, // seestate + sfx_posit2, // seesound + 8, // reactiontime + 0, // attacksound + S_SPOS_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_SPOS_ATK1, // missilestate + S_SPOS_DIE1, // deathstate + S_SPOS_XDIE1, // xdeathstate + sfx_podth2, // deathsound + 8, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_SPOS_RAISE1 // raisestate + }, + + { // MT_VILE + 64, // doomednum + S_VILE_STND, // spawnstate + 700, // spawnhealth + S_VILE_RUN1, // seestate + sfx_vilsit, // seesound + 8, // reactiontime + 0, // attacksound + S_VILE_PAIN, // painstate + 10, // painchance + sfx_vipain, // painsound + 0, // meleestate + S_VILE_ATK1, // missilestate + S_VILE_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_vildth, // deathsound + 15, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 500, // mass + 0, // damage + sfx_vilact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_FIRE + -1, // doomednum + S_FIRE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_UNDEAD + 66, // doomednum + S_SKEL_STND, // spawnstate + 300, // spawnhealth + S_SKEL_RUN1, // seestate + sfx_skesit, // seesound + 8, // reactiontime + 0, // attacksound + S_SKEL_PAIN, // painstate + 100, // painchance + sfx_popain, // painsound + S_SKEL_FIST1, // meleestate + S_SKEL_MISS1, // missilestate + S_SKEL_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_skedth, // deathsound + 10, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 500, // mass + 0, // damage + sfx_skeact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_SKEL_RAISE1 // raisestate + }, + + { // MT_TRACER + -1, // doomednum + S_TRACER, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_skeatk, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_TRACEEXP1, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 10 * FRACUNIT, // speed + 11 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 10, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SMOKE + -1, // doomednum + S_SMOKE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_FATSO + 67, // doomednum + S_FATT_STND, // spawnstate + 600, // spawnhealth + S_FATT_RUN1, // seestate + sfx_mansit, // seesound + 8, // reactiontime + 0, // attacksound + S_FATT_PAIN, // painstate + 80, // painchance + sfx_mnpain, // painsound + 0, // meleestate + S_FATT_ATK1, // missilestate + S_FATT_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_mandth, // deathsound + 8, // speed + 48 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_FATT_RAISE1 // raisestate + }, + + { // MT_FATSHOT + -1, // doomednum + S_FATSHOT1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_FATSHOTX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 20 * FRACUNIT, // speed + 6 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_CHAINGUY + 65, // doomednum + S_CPOS_STND, // spawnstate + 70, // spawnhealth + S_CPOS_RUN1, // seestate + sfx_posit2, // seesound + 8, // reactiontime + 0, // attacksound + S_CPOS_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_CPOS_ATK1, // missilestate + S_CPOS_DIE1, // deathstate + S_CPOS_XDIE1, // xdeathstate + sfx_podth2, // deathsound + 8, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_CPOS_RAISE1 // raisestate + }, + + { // MT_TROOP + 3001, // doomednum + S_TROO_STND, // spawnstate + 60, // spawnhealth + S_TROO_RUN1, // seestate + sfx_bgsit1, // seesound + 8, // reactiontime + 0, // attacksound + S_TROO_PAIN, // painstate + 200, // painchance + sfx_popain, // painsound + S_TROO_ATK1, // meleestate + S_TROO_ATK1, // missilestate + S_TROO_DIE1, // deathstate + S_TROO_XDIE1, // xdeathstate + sfx_bgdth1, // deathsound + 8, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_bgact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_TROO_RAISE1 // raisestate + }, + + { // MT_SERGEANT + 3002, // doomednum + S_SARG_STND, // spawnstate + 150, // spawnhealth + S_SARG_RUN1, // seestate + sfx_sgtsit, // seesound + 8, // reactiontime + sfx_sgtatk, // attacksound + S_SARG_PAIN, // painstate + 180, // painchance + sfx_dmpain, // painsound + S_SARG_ATK1, // meleestate + 0, // missilestate + S_SARG_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_sgtdth, // deathsound + 10, // speed + 30 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_SARG_RAISE1 // raisestate + }, + + { // MT_SHADOWS + 58, // doomednum + S_SARG_STND, // spawnstate + 150, // spawnhealth + S_SARG_RUN1, // seestate + sfx_sgtsit, // seesound + 8, // reactiontime + sfx_sgtatk, // attacksound + S_SARG_PAIN, // painstate + 180, // painchance + sfx_dmpain, // painsound + S_SARG_ATK1, // meleestate + 0, // missilestate + S_SARG_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_sgtdth, // deathsound + 10, // speed + 30 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_SHADOW | MF_COUNTKILL, // flags + S_SARG_RAISE1 // raisestate + }, + + { // MT_HEAD + 3005, // doomednum + S_HEAD_STND, // spawnstate + 400, // spawnhealth + S_HEAD_RUN1, // seestate + sfx_cacsit, // seesound + 8, // reactiontime + 0, // attacksound + S_HEAD_PAIN, // painstate + 128, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_HEAD_ATK1, // missilestate + S_HEAD_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_cacdth, // deathsound + 8, // speed + 31 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY | MF_COUNTKILL, // flags + S_HEAD_RAISE1 // raisestate + }, + + { // MT_BRUISER + 3003, // doomednum + S_BOSS_STND, // spawnstate + 1000, // spawnhealth + S_BOSS_RUN1, // seestate + sfx_brssit, // seesound + 8, // reactiontime + 0, // attacksound + S_BOSS_PAIN, // painstate + 50, // painchance + sfx_dmpain, // painsound + S_BOSS_ATK1, // meleestate + S_BOSS_ATK1, // missilestate + S_BOSS_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_brsdth, // deathsound + 8, // speed + 24 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_BOSS_RAISE1 // raisestate + }, + + { // MT_BRUISERSHOT + -1, // doomednum + S_BRBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BRBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 15 * FRACUNIT, // speed + 6 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_KNIGHT + 69, // doomednum + S_BOS2_STND, // spawnstate + 500, // spawnhealth + S_BOS2_RUN1, // seestate + sfx_kntsit, // seesound + 8, // reactiontime + 0, // attacksound + S_BOS2_PAIN, // painstate + 50, // painchance + sfx_dmpain, // painsound + S_BOS2_ATK1, // meleestate + S_BOS2_ATK1, // missilestate + S_BOS2_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_kntdth, // deathsound + 8, // speed + 24 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_BOS2_RAISE1 // raisestate + }, + + { // MT_SKULL + 3006, // doomednum + S_SKULL_STND, // spawnstate + 100, // spawnhealth + S_SKULL_RUN1, // seestate + 0, // seesound + 8, // reactiontime + sfx_sklatk, // attacksound + S_SKULL_PAIN, // painstate + 256, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_SKULL_ATK1, // missilestate + S_SKULL_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 8, // speed + 16 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 50, // mass + 3, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SPIDER + 7, // doomednum + S_SPID_STND, // spawnstate + 3000, // spawnhealth + S_SPID_RUN1, // seestate + sfx_spisit, // seesound + 8, // reactiontime + sfx_shotgn, // attacksound + S_SPID_PAIN, // painstate + 40, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_SPID_ATK1, // missilestate + S_SPID_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_spidth, // deathsound + 12, // speed + 128 * FRACUNIT, // radius + 100 * FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_BABY + 68, // doomednum + S_BSPI_STND, // spawnstate + 500, // spawnhealth + S_BSPI_SIGHT, // seestate + sfx_bspsit, // seesound + 8, // reactiontime + 0, // attacksound + S_BSPI_PAIN, // painstate + 128, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_BSPI_ATK1, // missilestate + S_BSPI_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_bspdth, // deathsound + 12, // speed + 64 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 600, // mass + 0, // damage + sfx_bspact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_BSPI_RAISE1 // raisestate + }, + + { // MT_CYBORG + 16, // doomednum + S_CYBER_STND, // spawnstate + 4000, // spawnhealth + S_CYBER_RUN1, // seestate + sfx_cybsit, // seesound + 8, // reactiontime + 0, // attacksound + S_CYBER_PAIN, // painstate + 20, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_CYBER_ATK1, // missilestate + S_CYBER_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_cybdth, // deathsound + 16, // speed + 40 * FRACUNIT, // radius + 110 * FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_PAIN + 71, // doomednum + S_PAIN_STND, // spawnstate + 400, // spawnhealth + S_PAIN_RUN1, // seestate + sfx_pesit, // seesound + 8, // reactiontime + 0, // attacksound + S_PAIN_PAIN, // painstate + 128, // painchance + sfx_pepain, // painsound + 0, // meleestate + S_PAIN_ATK1, // missilestate + S_PAIN_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_pedth, // deathsound + 8, // speed + 31 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY | MF_COUNTKILL, // flags + S_PAIN_RAISE1 // raisestate + }, + + { // MT_WOLFSS + 84, // doomednum + S_SSWV_STND, // spawnstate + 50, // spawnhealth + S_SSWV_RUN1, // seestate + sfx_sssit, // seesound + 8, // reactiontime + 0, // attacksound + S_SSWV_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_SSWV_ATK1, // missilestate + S_SSWV_DIE1, // deathstate + S_SSWV_XDIE1, // xdeathstate + sfx_ssdth, // deathsound + 8, // speed + 20 * FRACUNIT, // radius + 56 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_SSWV_RAISE1 // raisestate + }, + + { // MT_KEEN + 72, // doomednum + S_KEENSTND, // spawnstate + 100, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_KEENPAIN, // painstate + 256, // painchance + sfx_keenpn, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_COMMKEEN, // deathstate + S_NULL, // xdeathstate + sfx_keendt, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 72 * FRACUNIT, // height + 10000000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY | MF_SHOOTABLE | MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_BOSSBRAIN + 88, // doomednum + S_BRAIN, // spawnstate + 250, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_BRAIN_PAIN, // painstate + 255, // painchance + sfx_bospn, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BRAIN_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_bosdth, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 10000000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SHOOTABLE, // flags + S_NULL // raisestate + }, + + { // MT_BOSSSPIT + 89, // doomednum + S_BRAINEYE, // spawnstate + 1000, // spawnhealth + S_BRAINEYESEE, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 32 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_BOSSTARGET + 87, // doomednum + S_NULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 32 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_SPAWNSHOT + -1, // doomednum + S_SPAWN1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_bospit, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10 * FRACUNIT, // speed + 6 * FRACUNIT, // radius + 32 * FRACUNIT, // height + 100, // mass + 3, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY | MF_NOCLIP, // flags + S_NULL // raisestate + }, + + { // MT_SPAWNFIRE + -1, // doomednum + S_SPAWNFIRE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_BARREL + 2035, // doomednum + S_BAR1, // spawnstate + 20, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BEXP, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 0, // speed + 10 * FRACUNIT, // radius + 42 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD, // flags + S_NULL // raisestate + }, + + { // MT_TROOPSHOT + -1, // doomednum + S_TBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_TBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10 * FRACUNIT, // speed + 6 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 3, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_HEADSHOT + -1, // doomednum + S_RBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_RBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10 * FRACUNIT, // speed + 6 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_ROCKET + -1, // doomednum + S_ROCKET, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_rlaunc, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_EXPLODE1, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 20 * FRACUNIT, // speed + 11 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 20, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_PLASMA + -1, // doomednum + S_PLASBALL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_PLASEXP, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25 * FRACUNIT, // speed + 13 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_BFG + -1, // doomednum + S_BFGSHOT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + 0, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BFGLAND, // deathstate + S_NULL, // xdeathstate + sfx_rxplod, // deathsound + 25 * FRACUNIT, // speed + 13 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 100, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_ARACHPLAZ + -1, // doomednum + S_ARACH_PLAZ, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_ARACH_PLEX, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25 * FRACUNIT, // speed + 13 * FRACUNIT, // radius + 8 * FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_PUFF + -1, // doomednum + S_PUFF1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_BLOOD + -1, // doomednum + S_BLOOD1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_TFOG + -1, // doomednum + S_TFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_IFOG + -1, // doomednum + S_IFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_TELEPORTMAN + 14, // doomednum + S_NULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_EXTRABFG + -1, // doomednum + S_BFGEXP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC0 + 2018, // doomednum + S_ARM1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC1 + 2019, // doomednum + S_ARM2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC2 + 2014, // doomednum + S_BON1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC3 + 2015, // doomednum + S_BON2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC4 + 5, // doomednum + S_BKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC5 + 13, // doomednum + S_RKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC6 + 6, // doomednum + S_YKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC7 + 39, // doomednum + S_YSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC8 + 38, // doomednum + S_RSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC9 + 40, // doomednum + S_BSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC10 + 2011, // doomednum + S_STIM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC11 + 2012, // doomednum + S_MEDI, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC12 + 2013, // doomednum + S_SOUL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_INV + 2022, // doomednum + S_PINV, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC13 + 2023, // doomednum + S_PSTR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_INS + 2024, // doomednum + S_PINS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC14 + 2025, // doomednum + S_SUIT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC15 + 2026, // doomednum + S_PMAP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC16 + 2045, // doomednum + S_PVIS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MEGA + 83, // doomednum + S_MEGA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL | MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_CLIP + 2007, // doomednum + S_CLIP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC17 + 2048, // doomednum + S_AMMO, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC18 + 2010, // doomednum + S_ROCK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC19 + 2046, // doomednum + S_BROK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC20 + 2047, // doomednum + S_CELL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC21 + 17, // doomednum + S_CELP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC22 + 2008, // doomednum + S_SHEL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC23 + 2049, // doomednum + S_SBOX, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC24 + 8, // doomednum + S_BPAK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC25 + 2006, // doomednum + S_BFUG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_CHAINGUN + 2002, // doomednum + S_MGUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC26 + 2005, // doomednum + S_CSAW, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC27 + 2003, // doomednum + S_LAUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC28 + 2004, // doomednum + S_PLAS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_SHOTGUN + 2001, // doomednum + S_SHOT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_SUPERSHOTGUN + 82, // doomednum + S_SHOT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC29 + 85, // doomednum + S_TECHLAMP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC30 + 86, // doomednum + S_TECH2LAMP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC31 + 2028, // doomednum + S_COLU, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC32 + 30, // doomednum + S_TALLGRNCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC33 + 31, // doomednum + S_SHRTGRNCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC34 + 32, // doomednum + S_TALLREDCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC35 + 33, // doomednum + S_SHRTREDCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC36 + 37, // doomednum + S_SKULLCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC37 + 36, // doomednum + S_HEARTCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC38 + 41, // doomednum + S_EVILEYE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC39 + 42, // doomednum + S_FLOATSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC40 + 43, // doomednum + S_TORCHTREE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC41 + 44, // doomednum + S_BLUETORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC42 + 45, // doomednum + S_GREENTORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC43 + 46, // doomednum + S_REDTORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC44 + 55, // doomednum + S_BTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC45 + 56, // doomednum + S_GTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC46 + 57, // doomednum + S_RTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC47 + 47, // doomednum + S_STALAGTITE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC48 + 48, // doomednum + S_TECHPILLAR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC49 + 34, // doomednum + S_CANDLESTIK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC50 + 35, // doomednum + S_CANDELABRA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC51 + 49, // doomednum + S_BLOODYTWITCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 68 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC52 + 50, // doomednum + S_MEAT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 84 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC53 + 51, // doomednum + S_MEAT3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 84 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC54 + 52, // doomednum + S_MEAT4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 68 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC55 + 53, // doomednum + S_MEAT5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 52 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC56 + 59, // doomednum + S_MEAT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 84 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC57 + 60, // doomednum + S_MEAT4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 68 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC58 + 61, // doomednum + S_MEAT3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 52 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC59 + 62, // doomednum + S_MEAT5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 52 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC60 + 63, // doomednum + S_BLOODYTWITCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 68 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC61 + 22, // doomednum + S_HEAD_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC62 + 15, // doomednum + S_PLAY_DIE7, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC63 + 18, // doomednum + S_POSS_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC64 + 21, // doomednum + S_SARG_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC65 + 23, // doomednum + S_SKULL_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC66 + 20, // doomednum + S_TROO_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC67 + 19, // doomednum + S_SPOS_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC68 + 10, // doomednum + S_PLAY_XDIE9, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC69 + 12, // doomednum + S_PLAY_XDIE9, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC70 + 28, // doomednum + S_HEADSONSTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC71 + 24, // doomednum + S_GIBS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC72 + 27, // doomednum + S_HEADONASTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC73 + 29, // doomednum + S_HEADCANDLES, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC74 + 25, // doomednum + S_DEADSTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC75 + 26, // doomednum + S_LIVESTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC76 + 54, // doomednum + S_BIGTREE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 32 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC77 + 70, // doomednum + S_BBAR1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC78 + 73, // doomednum + S_HANGNOGUTS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 88 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC79 + 74, // doomednum + S_HANGBNOBRAIN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 88 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC80 + 75, // doomednum + S_HANGTLOOKDN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC81 + 76, // doomednum + S_HANGTSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC82 + 77, // doomednum + S_HANGTLOOKUP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC83 + 78, // doomednum + S_HANGTNOBRAIN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16 * FRACUNIT, // radius + 64 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC84 + 79, // doomednum + S_COLONGIBS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_MISC85 + 80, // doomednum + S_SMALLPOOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_MISC86 + 81, // doomednum + S_BRAINSTEM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20 * FRACUNIT, // radius + 16 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + } +}; diff --git a/src/DOOM/info.h b/src/DOOM/info.h new file mode 100644 index 0000000..929b57f --- /dev/null +++ b/src/DOOM/info.h @@ -0,0 +1,1344 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Thing frame/state LUT, +// generated by multigen utilitiy. +// This one is the original DOOM version, preserved. +// +//----------------------------------------------------------------------------- + +#ifndef __INFO__ +#define __INFO__ + + +// Needed for action function pointer handling. +#include "d_think.h" + + +typedef enum +{ + SPR_TROO, + SPR_SHTG, + SPR_PUNG, + SPR_PISG, + SPR_PISF, + SPR_SHTF, + SPR_SHT2, + SPR_CHGG, + SPR_CHGF, + SPR_MISG, + SPR_MISF, + SPR_SAWG, + SPR_PLSG, + SPR_PLSF, + SPR_BFGG, + SPR_BFGF, + SPR_BLUD, + SPR_PUFF, + SPR_BAL1, + SPR_BAL2, + SPR_PLSS, + SPR_PLSE, + SPR_MISL, + SPR_BFS1, + SPR_BFE1, + SPR_BFE2, + SPR_TFOG, + SPR_IFOG, + SPR_PLAY, + SPR_POSS, + SPR_SPOS, + SPR_VILE, + SPR_FIRE, + SPR_FATB, + SPR_FBXP, + SPR_SKEL, + SPR_MANF, + SPR_FATT, + SPR_CPOS, + SPR_SARG, + SPR_HEAD, + SPR_BAL7, + SPR_BOSS, + SPR_BOS2, + SPR_SKUL, + SPR_SPID, + SPR_BSPI, + SPR_APLS, + SPR_APBX, + SPR_CYBR, + SPR_PAIN, + SPR_SSWV, + SPR_KEEN, + SPR_BBRN, + SPR_BOSF, + SPR_ARM1, + SPR_ARM2, + SPR_BAR1, + SPR_BEXP, + SPR_FCAN, + SPR_BON1, + SPR_BON2, + SPR_BKEY, + SPR_RKEY, + SPR_YKEY, + SPR_BSKU, + SPR_RSKU, + SPR_YSKU, + SPR_STIM, + SPR_MEDI, + SPR_SOUL, + SPR_PINV, + SPR_PSTR, + SPR_PINS, + SPR_MEGA, + SPR_SUIT, + SPR_PMAP, + SPR_PVIS, + SPR_CLIP, + SPR_AMMO, + SPR_ROCK, + SPR_BROK, + SPR_CELL, + SPR_CELP, + SPR_SHEL, + SPR_SBOX, + SPR_BPAK, + SPR_BFUG, + SPR_MGUN, + SPR_CSAW, + SPR_LAUN, + SPR_PLAS, + SPR_SHOT, + SPR_SGN2, + SPR_COLU, + SPR_SMT2, + SPR_GOR1, + SPR_POL2, + SPR_POL5, + SPR_POL4, + SPR_POL3, + SPR_POL1, + SPR_POL6, + SPR_GOR2, + SPR_GOR3, + SPR_GOR4, + SPR_GOR5, + SPR_SMIT, + SPR_COL1, + SPR_COL2, + SPR_COL3, + SPR_COL4, + SPR_CAND, + SPR_CBRA, + SPR_COL6, + SPR_TRE1, + SPR_TRE2, + SPR_ELEC, + SPR_CEYE, + SPR_FSKU, + SPR_COL5, + SPR_TBLU, + SPR_TGRN, + SPR_TRED, + SPR_SMBT, + SPR_SMGT, + SPR_SMRT, + SPR_HDB1, + SPR_HDB2, + SPR_HDB3, + SPR_HDB4, + SPR_HDB5, + SPR_HDB6, + SPR_POB1, + SPR_POB2, + SPR_BRS1, + SPR_TLMP, + SPR_TLP2, + NUMSPRITES +} spritenum_t; + + +typedef enum +{ + S_NULL, + S_LIGHTDONE, + S_PUNCH, + S_PUNCHDOWN, + S_PUNCHUP, + S_PUNCH1, + S_PUNCH2, + S_PUNCH3, + S_PUNCH4, + S_PUNCH5, + S_PISTOL, + S_PISTOLDOWN, + S_PISTOLUP, + S_PISTOL1, + S_PISTOL2, + S_PISTOL3, + S_PISTOL4, + S_PISTOLFLASH, + S_SGUN, + S_SGUNDOWN, + S_SGUNUP, + S_SGUN1, + S_SGUN2, + S_SGUN3, + S_SGUN4, + S_SGUN5, + S_SGUN6, + S_SGUN7, + S_SGUN8, + S_SGUN9, + S_SGUNFLASH1, + S_SGUNFLASH2, + S_DSGUN, + S_DSGUNDOWN, + S_DSGUNUP, + S_DSGUN1, + S_DSGUN2, + S_DSGUN3, + S_DSGUN4, + S_DSGUN5, + S_DSGUN6, + S_DSGUN7, + S_DSGUN8, + S_DSGUN9, + S_DSGUN10, + S_DSNR1, + S_DSNR2, + S_DSGUNFLASH1, + S_DSGUNFLASH2, + S_CHAIN, + S_CHAINDOWN, + S_CHAINUP, + S_CHAIN1, + S_CHAIN2, + S_CHAIN3, + S_CHAINFLASH1, + S_CHAINFLASH2, + S_MISSILE, + S_MISSILEDOWN, + S_MISSILEUP, + S_MISSILE1, + S_MISSILE2, + S_MISSILE3, + S_MISSILEFLASH1, + S_MISSILEFLASH2, + S_MISSILEFLASH3, + S_MISSILEFLASH4, + S_SAW, + S_SAWB, + S_SAWDOWN, + S_SAWUP, + S_SAW1, + S_SAW2, + S_SAW3, + S_PLASMA, + S_PLASMADOWN, + S_PLASMAUP, + S_PLASMA1, + S_PLASMA2, + S_PLASMAFLASH1, + S_PLASMAFLASH2, + S_BFG, + S_BFGDOWN, + S_BFGUP, + S_BFG1, + S_BFG2, + S_BFG3, + S_BFG4, + S_BFGFLASH1, + S_BFGFLASH2, + S_BLOOD1, + S_BLOOD2, + S_BLOOD3, + S_PUFF1, + S_PUFF2, + S_PUFF3, + S_PUFF4, + S_TBALL1, + S_TBALL2, + S_TBALLX1, + S_TBALLX2, + S_TBALLX3, + S_RBALL1, + S_RBALL2, + S_RBALLX1, + S_RBALLX2, + S_RBALLX3, + S_PLASBALL, + S_PLASBALL2, + S_PLASEXP, + S_PLASEXP2, + S_PLASEXP3, + S_PLASEXP4, + S_PLASEXP5, + S_ROCKET, + S_BFGSHOT, + S_BFGSHOT2, + S_BFGLAND, + S_BFGLAND2, + S_BFGLAND3, + S_BFGLAND4, + S_BFGLAND5, + S_BFGLAND6, + S_BFGEXP, + S_BFGEXP2, + S_BFGEXP3, + S_BFGEXP4, + S_EXPLODE1, + S_EXPLODE2, + S_EXPLODE3, + S_TFOG, + S_TFOG01, + S_TFOG02, + S_TFOG2, + S_TFOG3, + S_TFOG4, + S_TFOG5, + S_TFOG6, + S_TFOG7, + S_TFOG8, + S_TFOG9, + S_TFOG10, + S_IFOG, + S_IFOG01, + S_IFOG02, + S_IFOG2, + S_IFOG3, + S_IFOG4, + S_IFOG5, + S_PLAY, + S_PLAY_RUN1, + S_PLAY_RUN2, + S_PLAY_RUN3, + S_PLAY_RUN4, + S_PLAY_ATK1, + S_PLAY_ATK2, + S_PLAY_PAIN, + S_PLAY_PAIN2, + S_PLAY_DIE1, + S_PLAY_DIE2, + S_PLAY_DIE3, + S_PLAY_DIE4, + S_PLAY_DIE5, + S_PLAY_DIE6, + S_PLAY_DIE7, + S_PLAY_XDIE1, + S_PLAY_XDIE2, + S_PLAY_XDIE3, + S_PLAY_XDIE4, + S_PLAY_XDIE5, + S_PLAY_XDIE6, + S_PLAY_XDIE7, + S_PLAY_XDIE8, + S_PLAY_XDIE9, + S_POSS_STND, + S_POSS_STND2, + S_POSS_RUN1, + S_POSS_RUN2, + S_POSS_RUN3, + S_POSS_RUN4, + S_POSS_RUN5, + S_POSS_RUN6, + S_POSS_RUN7, + S_POSS_RUN8, + S_POSS_ATK1, + S_POSS_ATK2, + S_POSS_ATK3, + S_POSS_PAIN, + S_POSS_PAIN2, + S_POSS_DIE1, + S_POSS_DIE2, + S_POSS_DIE3, + S_POSS_DIE4, + S_POSS_DIE5, + S_POSS_XDIE1, + S_POSS_XDIE2, + S_POSS_XDIE3, + S_POSS_XDIE4, + S_POSS_XDIE5, + S_POSS_XDIE6, + S_POSS_XDIE7, + S_POSS_XDIE8, + S_POSS_XDIE9, + S_POSS_RAISE1, + S_POSS_RAISE2, + S_POSS_RAISE3, + S_POSS_RAISE4, + S_SPOS_STND, + S_SPOS_STND2, + S_SPOS_RUN1, + S_SPOS_RUN2, + S_SPOS_RUN3, + S_SPOS_RUN4, + S_SPOS_RUN5, + S_SPOS_RUN6, + S_SPOS_RUN7, + S_SPOS_RUN8, + S_SPOS_ATK1, + S_SPOS_ATK2, + S_SPOS_ATK3, + S_SPOS_PAIN, + S_SPOS_PAIN2, + S_SPOS_DIE1, + S_SPOS_DIE2, + S_SPOS_DIE3, + S_SPOS_DIE4, + S_SPOS_DIE5, + S_SPOS_XDIE1, + S_SPOS_XDIE2, + S_SPOS_XDIE3, + S_SPOS_XDIE4, + S_SPOS_XDIE5, + S_SPOS_XDIE6, + S_SPOS_XDIE7, + S_SPOS_XDIE8, + S_SPOS_XDIE9, + S_SPOS_RAISE1, + S_SPOS_RAISE2, + S_SPOS_RAISE3, + S_SPOS_RAISE4, + S_SPOS_RAISE5, + S_VILE_STND, + S_VILE_STND2, + S_VILE_RUN1, + S_VILE_RUN2, + S_VILE_RUN3, + S_VILE_RUN4, + S_VILE_RUN5, + S_VILE_RUN6, + S_VILE_RUN7, + S_VILE_RUN8, + S_VILE_RUN9, + S_VILE_RUN10, + S_VILE_RUN11, + S_VILE_RUN12, + S_VILE_ATK1, + S_VILE_ATK2, + S_VILE_ATK3, + S_VILE_ATK4, + S_VILE_ATK5, + S_VILE_ATK6, + S_VILE_ATK7, + S_VILE_ATK8, + S_VILE_ATK9, + S_VILE_ATK10, + S_VILE_ATK11, + S_VILE_HEAL1, + S_VILE_HEAL2, + S_VILE_HEAL3, + S_VILE_PAIN, + S_VILE_PAIN2, + S_VILE_DIE1, + S_VILE_DIE2, + S_VILE_DIE3, + S_VILE_DIE4, + S_VILE_DIE5, + S_VILE_DIE6, + S_VILE_DIE7, + S_VILE_DIE8, + S_VILE_DIE9, + S_VILE_DIE10, + S_FIRE1, + S_FIRE2, + S_FIRE3, + S_FIRE4, + S_FIRE5, + S_FIRE6, + S_FIRE7, + S_FIRE8, + S_FIRE9, + S_FIRE10, + S_FIRE11, + S_FIRE12, + S_FIRE13, + S_FIRE14, + S_FIRE15, + S_FIRE16, + S_FIRE17, + S_FIRE18, + S_FIRE19, + S_FIRE20, + S_FIRE21, + S_FIRE22, + S_FIRE23, + S_FIRE24, + S_FIRE25, + S_FIRE26, + S_FIRE27, + S_FIRE28, + S_FIRE29, + S_FIRE30, + S_SMOKE1, + S_SMOKE2, + S_SMOKE3, + S_SMOKE4, + S_SMOKE5, + S_TRACER, + S_TRACER2, + S_TRACEEXP1, + S_TRACEEXP2, + S_TRACEEXP3, + S_SKEL_STND, + S_SKEL_STND2, + S_SKEL_RUN1, + S_SKEL_RUN2, + S_SKEL_RUN3, + S_SKEL_RUN4, + S_SKEL_RUN5, + S_SKEL_RUN6, + S_SKEL_RUN7, + S_SKEL_RUN8, + S_SKEL_RUN9, + S_SKEL_RUN10, + S_SKEL_RUN11, + S_SKEL_RUN12, + S_SKEL_FIST1, + S_SKEL_FIST2, + S_SKEL_FIST3, + S_SKEL_FIST4, + S_SKEL_MISS1, + S_SKEL_MISS2, + S_SKEL_MISS3, + S_SKEL_MISS4, + S_SKEL_PAIN, + S_SKEL_PAIN2, + S_SKEL_DIE1, + S_SKEL_DIE2, + S_SKEL_DIE3, + S_SKEL_DIE4, + S_SKEL_DIE5, + S_SKEL_DIE6, + S_SKEL_RAISE1, + S_SKEL_RAISE2, + S_SKEL_RAISE3, + S_SKEL_RAISE4, + S_SKEL_RAISE5, + S_SKEL_RAISE6, + S_FATSHOT1, + S_FATSHOT2, + S_FATSHOTX1, + S_FATSHOTX2, + S_FATSHOTX3, + S_FATT_STND, + S_FATT_STND2, + S_FATT_RUN1, + S_FATT_RUN2, + S_FATT_RUN3, + S_FATT_RUN4, + S_FATT_RUN5, + S_FATT_RUN6, + S_FATT_RUN7, + S_FATT_RUN8, + S_FATT_RUN9, + S_FATT_RUN10, + S_FATT_RUN11, + S_FATT_RUN12, + S_FATT_ATK1, + S_FATT_ATK2, + S_FATT_ATK3, + S_FATT_ATK4, + S_FATT_ATK5, + S_FATT_ATK6, + S_FATT_ATK7, + S_FATT_ATK8, + S_FATT_ATK9, + S_FATT_ATK10, + S_FATT_PAIN, + S_FATT_PAIN2, + S_FATT_DIE1, + S_FATT_DIE2, + S_FATT_DIE3, + S_FATT_DIE4, + S_FATT_DIE5, + S_FATT_DIE6, + S_FATT_DIE7, + S_FATT_DIE8, + S_FATT_DIE9, + S_FATT_DIE10, + S_FATT_RAISE1, + S_FATT_RAISE2, + S_FATT_RAISE3, + S_FATT_RAISE4, + S_FATT_RAISE5, + S_FATT_RAISE6, + S_FATT_RAISE7, + S_FATT_RAISE8, + S_CPOS_STND, + S_CPOS_STND2, + S_CPOS_RUN1, + S_CPOS_RUN2, + S_CPOS_RUN3, + S_CPOS_RUN4, + S_CPOS_RUN5, + S_CPOS_RUN6, + S_CPOS_RUN7, + S_CPOS_RUN8, + S_CPOS_ATK1, + S_CPOS_ATK2, + S_CPOS_ATK3, + S_CPOS_ATK4, + S_CPOS_PAIN, + S_CPOS_PAIN2, + S_CPOS_DIE1, + S_CPOS_DIE2, + S_CPOS_DIE3, + S_CPOS_DIE4, + S_CPOS_DIE5, + S_CPOS_DIE6, + S_CPOS_DIE7, + S_CPOS_XDIE1, + S_CPOS_XDIE2, + S_CPOS_XDIE3, + S_CPOS_XDIE4, + S_CPOS_XDIE5, + S_CPOS_XDIE6, + S_CPOS_RAISE1, + S_CPOS_RAISE2, + S_CPOS_RAISE3, + S_CPOS_RAISE4, + S_CPOS_RAISE5, + S_CPOS_RAISE6, + S_CPOS_RAISE7, + S_TROO_STND, + S_TROO_STND2, + S_TROO_RUN1, + S_TROO_RUN2, + S_TROO_RUN3, + S_TROO_RUN4, + S_TROO_RUN5, + S_TROO_RUN6, + S_TROO_RUN7, + S_TROO_RUN8, + S_TROO_ATK1, + S_TROO_ATK2, + S_TROO_ATK3, + S_TROO_PAIN, + S_TROO_PAIN2, + S_TROO_DIE1, + S_TROO_DIE2, + S_TROO_DIE3, + S_TROO_DIE4, + S_TROO_DIE5, + S_TROO_XDIE1, + S_TROO_XDIE2, + S_TROO_XDIE3, + S_TROO_XDIE4, + S_TROO_XDIE5, + S_TROO_XDIE6, + S_TROO_XDIE7, + S_TROO_XDIE8, + S_TROO_RAISE1, + S_TROO_RAISE2, + S_TROO_RAISE3, + S_TROO_RAISE4, + S_TROO_RAISE5, + S_SARG_STND, + S_SARG_STND2, + S_SARG_RUN1, + S_SARG_RUN2, + S_SARG_RUN3, + S_SARG_RUN4, + S_SARG_RUN5, + S_SARG_RUN6, + S_SARG_RUN7, + S_SARG_RUN8, + S_SARG_ATK1, + S_SARG_ATK2, + S_SARG_ATK3, + S_SARG_PAIN, + S_SARG_PAIN2, + S_SARG_DIE1, + S_SARG_DIE2, + S_SARG_DIE3, + S_SARG_DIE4, + S_SARG_DIE5, + S_SARG_DIE6, + S_SARG_RAISE1, + S_SARG_RAISE2, + S_SARG_RAISE3, + S_SARG_RAISE4, + S_SARG_RAISE5, + S_SARG_RAISE6, + S_HEAD_STND, + S_HEAD_RUN1, + S_HEAD_ATK1, + S_HEAD_ATK2, + S_HEAD_ATK3, + S_HEAD_PAIN, + S_HEAD_PAIN2, + S_HEAD_PAIN3, + S_HEAD_DIE1, + S_HEAD_DIE2, + S_HEAD_DIE3, + S_HEAD_DIE4, + S_HEAD_DIE5, + S_HEAD_DIE6, + S_HEAD_RAISE1, + S_HEAD_RAISE2, + S_HEAD_RAISE3, + S_HEAD_RAISE4, + S_HEAD_RAISE5, + S_HEAD_RAISE6, + S_BRBALL1, + S_BRBALL2, + S_BRBALLX1, + S_BRBALLX2, + S_BRBALLX3, + S_BOSS_STND, + S_BOSS_STND2, + S_BOSS_RUN1, + S_BOSS_RUN2, + S_BOSS_RUN3, + S_BOSS_RUN4, + S_BOSS_RUN5, + S_BOSS_RUN6, + S_BOSS_RUN7, + S_BOSS_RUN8, + S_BOSS_ATK1, + S_BOSS_ATK2, + S_BOSS_ATK3, + S_BOSS_PAIN, + S_BOSS_PAIN2, + S_BOSS_DIE1, + S_BOSS_DIE2, + S_BOSS_DIE3, + S_BOSS_DIE4, + S_BOSS_DIE5, + S_BOSS_DIE6, + S_BOSS_DIE7, + S_BOSS_RAISE1, + S_BOSS_RAISE2, + S_BOSS_RAISE3, + S_BOSS_RAISE4, + S_BOSS_RAISE5, + S_BOSS_RAISE6, + S_BOSS_RAISE7, + S_BOS2_STND, + S_BOS2_STND2, + S_BOS2_RUN1, + S_BOS2_RUN2, + S_BOS2_RUN3, + S_BOS2_RUN4, + S_BOS2_RUN5, + S_BOS2_RUN6, + S_BOS2_RUN7, + S_BOS2_RUN8, + S_BOS2_ATK1, + S_BOS2_ATK2, + S_BOS2_ATK3, + S_BOS2_PAIN, + S_BOS2_PAIN2, + S_BOS2_DIE1, + S_BOS2_DIE2, + S_BOS2_DIE3, + S_BOS2_DIE4, + S_BOS2_DIE5, + S_BOS2_DIE6, + S_BOS2_DIE7, + S_BOS2_RAISE1, + S_BOS2_RAISE2, + S_BOS2_RAISE3, + S_BOS2_RAISE4, + S_BOS2_RAISE5, + S_BOS2_RAISE6, + S_BOS2_RAISE7, + S_SKULL_STND, + S_SKULL_STND2, + S_SKULL_RUN1, + S_SKULL_RUN2, + S_SKULL_ATK1, + S_SKULL_ATK2, + S_SKULL_ATK3, + S_SKULL_ATK4, + S_SKULL_PAIN, + S_SKULL_PAIN2, + S_SKULL_DIE1, + S_SKULL_DIE2, + S_SKULL_DIE3, + S_SKULL_DIE4, + S_SKULL_DIE5, + S_SKULL_DIE6, + S_SPID_STND, + S_SPID_STND2, + S_SPID_RUN1, + S_SPID_RUN2, + S_SPID_RUN3, + S_SPID_RUN4, + S_SPID_RUN5, + S_SPID_RUN6, + S_SPID_RUN7, + S_SPID_RUN8, + S_SPID_RUN9, + S_SPID_RUN10, + S_SPID_RUN11, + S_SPID_RUN12, + S_SPID_ATK1, + S_SPID_ATK2, + S_SPID_ATK3, + S_SPID_ATK4, + S_SPID_PAIN, + S_SPID_PAIN2, + S_SPID_DIE1, + S_SPID_DIE2, + S_SPID_DIE3, + S_SPID_DIE4, + S_SPID_DIE5, + S_SPID_DIE6, + S_SPID_DIE7, + S_SPID_DIE8, + S_SPID_DIE9, + S_SPID_DIE10, + S_SPID_DIE11, + S_BSPI_STND, + S_BSPI_STND2, + S_BSPI_SIGHT, + S_BSPI_RUN1, + S_BSPI_RUN2, + S_BSPI_RUN3, + S_BSPI_RUN4, + S_BSPI_RUN5, + S_BSPI_RUN6, + S_BSPI_RUN7, + S_BSPI_RUN8, + S_BSPI_RUN9, + S_BSPI_RUN10, + S_BSPI_RUN11, + S_BSPI_RUN12, + S_BSPI_ATK1, + S_BSPI_ATK2, + S_BSPI_ATK3, + S_BSPI_ATK4, + S_BSPI_PAIN, + S_BSPI_PAIN2, + S_BSPI_DIE1, + S_BSPI_DIE2, + S_BSPI_DIE3, + S_BSPI_DIE4, + S_BSPI_DIE5, + S_BSPI_DIE6, + S_BSPI_DIE7, + S_BSPI_RAISE1, + S_BSPI_RAISE2, + S_BSPI_RAISE3, + S_BSPI_RAISE4, + S_BSPI_RAISE5, + S_BSPI_RAISE6, + S_BSPI_RAISE7, + S_ARACH_PLAZ, + S_ARACH_PLAZ2, + S_ARACH_PLEX, + S_ARACH_PLEX2, + S_ARACH_PLEX3, + S_ARACH_PLEX4, + S_ARACH_PLEX5, + S_CYBER_STND, + S_CYBER_STND2, + S_CYBER_RUN1, + S_CYBER_RUN2, + S_CYBER_RUN3, + S_CYBER_RUN4, + S_CYBER_RUN5, + S_CYBER_RUN6, + S_CYBER_RUN7, + S_CYBER_RUN8, + S_CYBER_ATK1, + S_CYBER_ATK2, + S_CYBER_ATK3, + S_CYBER_ATK4, + S_CYBER_ATK5, + S_CYBER_ATK6, + S_CYBER_PAIN, + S_CYBER_DIE1, + S_CYBER_DIE2, + S_CYBER_DIE3, + S_CYBER_DIE4, + S_CYBER_DIE5, + S_CYBER_DIE6, + S_CYBER_DIE7, + S_CYBER_DIE8, + S_CYBER_DIE9, + S_CYBER_DIE10, + S_PAIN_STND, + S_PAIN_RUN1, + S_PAIN_RUN2, + S_PAIN_RUN3, + S_PAIN_RUN4, + S_PAIN_RUN5, + S_PAIN_RUN6, + S_PAIN_ATK1, + S_PAIN_ATK2, + S_PAIN_ATK3, + S_PAIN_ATK4, + S_PAIN_PAIN, + S_PAIN_PAIN2, + S_PAIN_DIE1, + S_PAIN_DIE2, + S_PAIN_DIE3, + S_PAIN_DIE4, + S_PAIN_DIE5, + S_PAIN_DIE6, + S_PAIN_RAISE1, + S_PAIN_RAISE2, + S_PAIN_RAISE3, + S_PAIN_RAISE4, + S_PAIN_RAISE5, + S_PAIN_RAISE6, + S_SSWV_STND, + S_SSWV_STND2, + S_SSWV_RUN1, + S_SSWV_RUN2, + S_SSWV_RUN3, + S_SSWV_RUN4, + S_SSWV_RUN5, + S_SSWV_RUN6, + S_SSWV_RUN7, + S_SSWV_RUN8, + S_SSWV_ATK1, + S_SSWV_ATK2, + S_SSWV_ATK3, + S_SSWV_ATK4, + S_SSWV_ATK5, + S_SSWV_ATK6, + S_SSWV_PAIN, + S_SSWV_PAIN2, + S_SSWV_DIE1, + S_SSWV_DIE2, + S_SSWV_DIE3, + S_SSWV_DIE4, + S_SSWV_DIE5, + S_SSWV_XDIE1, + S_SSWV_XDIE2, + S_SSWV_XDIE3, + S_SSWV_XDIE4, + S_SSWV_XDIE5, + S_SSWV_XDIE6, + S_SSWV_XDIE7, + S_SSWV_XDIE8, + S_SSWV_XDIE9, + S_SSWV_RAISE1, + S_SSWV_RAISE2, + S_SSWV_RAISE3, + S_SSWV_RAISE4, + S_SSWV_RAISE5, + S_KEENSTND, + S_COMMKEEN, + S_COMMKEEN2, + S_COMMKEEN3, + S_COMMKEEN4, + S_COMMKEEN5, + S_COMMKEEN6, + S_COMMKEEN7, + S_COMMKEEN8, + S_COMMKEEN9, + S_COMMKEEN10, + S_COMMKEEN11, + S_COMMKEEN12, + S_KEENPAIN, + S_KEENPAIN2, + S_BRAIN, + S_BRAIN_PAIN, + S_BRAIN_DIE1, + S_BRAIN_DIE2, + S_BRAIN_DIE3, + S_BRAIN_DIE4, + S_BRAINEYE, + S_BRAINEYESEE, + S_BRAINEYE1, + S_SPAWN1, + S_SPAWN2, + S_SPAWN3, + S_SPAWN4, + S_SPAWNFIRE1, + S_SPAWNFIRE2, + S_SPAWNFIRE3, + S_SPAWNFIRE4, + S_SPAWNFIRE5, + S_SPAWNFIRE6, + S_SPAWNFIRE7, + S_SPAWNFIRE8, + S_BRAINEXPLODE1, + S_BRAINEXPLODE2, + S_BRAINEXPLODE3, + S_ARM1, + S_ARM1A, + S_ARM2, + S_ARM2A, + S_BAR1, + S_BAR2, + S_BEXP, + S_BEXP2, + S_BEXP3, + S_BEXP4, + S_BEXP5, + S_BBAR1, + S_BBAR2, + S_BBAR3, + S_BON1, + S_BON1A, + S_BON1B, + S_BON1C, + S_BON1D, + S_BON1E, + S_BON2, + S_BON2A, + S_BON2B, + S_BON2C, + S_BON2D, + S_BON2E, + S_BKEY, + S_BKEY2, + S_RKEY, + S_RKEY2, + S_YKEY, + S_YKEY2, + S_BSKULL, + S_BSKULL2, + S_RSKULL, + S_RSKULL2, + S_YSKULL, + S_YSKULL2, + S_STIM, + S_MEDI, + S_SOUL, + S_SOUL2, + S_SOUL3, + S_SOUL4, + S_SOUL5, + S_SOUL6, + S_PINV, + S_PINV2, + S_PINV3, + S_PINV4, + S_PSTR, + S_PINS, + S_PINS2, + S_PINS3, + S_PINS4, + S_MEGA, + S_MEGA2, + S_MEGA3, + S_MEGA4, + S_SUIT, + S_PMAP, + S_PMAP2, + S_PMAP3, + S_PMAP4, + S_PMAP5, + S_PMAP6, + S_PVIS, + S_PVIS2, + S_CLIP, + S_AMMO, + S_ROCK, + S_BROK, + S_CELL, + S_CELP, + S_SHEL, + S_SBOX, + S_BPAK, + S_BFUG, + S_MGUN, + S_CSAW, + S_LAUN, + S_PLAS, + S_SHOT, + S_SHOT2, + S_COLU, + S_STALAG, + S_BLOODYTWITCH, + S_BLOODYTWITCH2, + S_BLOODYTWITCH3, + S_BLOODYTWITCH4, + S_DEADTORSO, + S_DEADBOTTOM, + S_HEADSONSTICK, + S_GIBS, + S_HEADONASTICK, + S_HEADCANDLES, + S_HEADCANDLES2, + S_DEADSTICK, + S_LIVESTICK, + S_LIVESTICK2, + S_MEAT2, + S_MEAT3, + S_MEAT4, + S_MEAT5, + S_STALAGTITE, + S_TALLGRNCOL, + S_SHRTGRNCOL, + S_TALLREDCOL, + S_SHRTREDCOL, + S_CANDLESTIK, + S_CANDELABRA, + S_SKULLCOL, + S_TORCHTREE, + S_BIGTREE, + S_TECHPILLAR, + S_EVILEYE, + S_EVILEYE2, + S_EVILEYE3, + S_EVILEYE4, + S_FLOATSKULL, + S_FLOATSKULL2, + S_FLOATSKULL3, + S_HEARTCOL, + S_HEARTCOL2, + S_BLUETORCH, + S_BLUETORCH2, + S_BLUETORCH3, + S_BLUETORCH4, + S_GREENTORCH, + S_GREENTORCH2, + S_GREENTORCH3, + S_GREENTORCH4, + S_REDTORCH, + S_REDTORCH2, + S_REDTORCH3, + S_REDTORCH4, + S_BTORCHSHRT, + S_BTORCHSHRT2, + S_BTORCHSHRT3, + S_BTORCHSHRT4, + S_GTORCHSHRT, + S_GTORCHSHRT2, + S_GTORCHSHRT3, + S_GTORCHSHRT4, + S_RTORCHSHRT, + S_RTORCHSHRT2, + S_RTORCHSHRT3, + S_RTORCHSHRT4, + S_HANGNOGUTS, + S_HANGBNOBRAIN, + S_HANGTLOOKDN, + S_HANGTSKULL, + S_HANGTLOOKUP, + S_HANGTNOBRAIN, + S_COLONGIBS, + S_SMALLPOOL, + S_BRAINSTEM, + S_TECHLAMP, + S_TECHLAMP2, + S_TECHLAMP3, + S_TECHLAMP4, + S_TECH2LAMP, + S_TECH2LAMP2, + S_TECH2LAMP3, + S_TECH2LAMP4, + NUMSTATES +} statenum_t; + + +typedef struct +{ + spritenum_t sprite; + long frame; + long tics; + actionf_t action; + statenum_t nextstate; + long misc1, misc2; +} state_t; + + +extern state_t states[NUMSTATES]; +extern char* sprnames[NUMSPRITES + 1]; + + +typedef enum +{ + MT_PLAYER, + MT_POSSESSED, + MT_SHOTGUY, + MT_VILE, + MT_FIRE, + MT_UNDEAD, + MT_TRACER, + MT_SMOKE, + MT_FATSO, + MT_FATSHOT, + MT_CHAINGUY, + MT_TROOP, + MT_SERGEANT, + MT_SHADOWS, + MT_HEAD, + MT_BRUISER, + MT_BRUISERSHOT, + MT_KNIGHT, + MT_SKULL, + MT_SPIDER, + MT_BABY, + MT_CYBORG, + MT_PAIN, + MT_WOLFSS, + MT_KEEN, + MT_BOSSBRAIN, + MT_BOSSSPIT, + MT_BOSSTARGET, + MT_SPAWNSHOT, + MT_SPAWNFIRE, + MT_BARREL, + MT_TROOPSHOT, + MT_HEADSHOT, + MT_ROCKET, + MT_PLASMA, + MT_BFG, + MT_ARACHPLAZ, + MT_PUFF, + MT_BLOOD, + MT_TFOG, + MT_IFOG, + MT_TELEPORTMAN, + MT_EXTRABFG, + MT_MISC0, + MT_MISC1, + MT_MISC2, + MT_MISC3, + MT_MISC4, + MT_MISC5, + MT_MISC6, + MT_MISC7, + MT_MISC8, + MT_MISC9, + MT_MISC10, + MT_MISC11, + MT_MISC12, + MT_INV, + MT_MISC13, + MT_INS, + MT_MISC14, + MT_MISC15, + MT_MISC16, + MT_MEGA, + MT_CLIP, + MT_MISC17, + MT_MISC18, + MT_MISC19, + MT_MISC20, + MT_MISC21, + MT_MISC22, + MT_MISC23, + MT_MISC24, + MT_MISC25, + MT_CHAINGUN, + MT_MISC26, + MT_MISC27, + MT_MISC28, + MT_SHOTGUN, + MT_SUPERSHOTGUN, + MT_MISC29, + MT_MISC30, + MT_MISC31, + MT_MISC32, + MT_MISC33, + MT_MISC34, + MT_MISC35, + MT_MISC36, + MT_MISC37, + MT_MISC38, + MT_MISC39, + MT_MISC40, + MT_MISC41, + MT_MISC42, + MT_MISC43, + MT_MISC44, + MT_MISC45, + MT_MISC46, + MT_MISC47, + MT_MISC48, + MT_MISC49, + MT_MISC50, + MT_MISC51, + MT_MISC52, + MT_MISC53, + MT_MISC54, + MT_MISC55, + MT_MISC56, + MT_MISC57, + MT_MISC58, + MT_MISC59, + MT_MISC60, + MT_MISC61, + MT_MISC62, + MT_MISC63, + MT_MISC64, + MT_MISC65, + MT_MISC66, + MT_MISC67, + MT_MISC68, + MT_MISC69, + MT_MISC70, + MT_MISC71, + MT_MISC72, + MT_MISC73, + MT_MISC74, + MT_MISC75, + MT_MISC76, + MT_MISC77, + MT_MISC78, + MT_MISC79, + MT_MISC80, + MT_MISC81, + MT_MISC82, + MT_MISC83, + MT_MISC84, + MT_MISC85, + MT_MISC86, + NUMMOBJTYPES +} mobjtype_t; + + +typedef struct +{ + int doomednum; + int spawnstate; + int spawnhealth; + int seestate; + int seesound; + int reactiontime; + int attacksound; + int painstate; + int painchance; + int painsound; + int meleestate; + int missilestate; + int deathstate; + int xdeathstate; + int deathsound; + int speed; + int radius; + int height; + int mass; + int damage; + int activesound; + int flags; + int raisestate; +} mobjinfo_t; + + +extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/m_argv.c b/src/DOOM/m_argv.c new file mode 100644 index 0000000..deb84a3 --- /dev/null +++ b/src/DOOM/m_argv.c @@ -0,0 +1,53 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + +// #if defined(DOOM_WIN32) +// #define strcasecmp stricmp +// #endif + + +int myargc; +char** myargv; + + +// +// M_CheckParm +// Checks for the given parameter +// in the program's command line arguments. +// Returns the argument number (1 to argc-1) +// or 0 if not present +int M_CheckParm(char* check) +{ + int i; + + for (i = 1; i < myargc; i++) + { + if (!doom_strcasecmp(check, myargv[i])) + return i; + } + + return 0; +} diff --git a/src/DOOM/m_argv.h b/src/DOOM/m_argv.h new file mode 100644 index 0000000..78daecd --- /dev/null +++ b/src/DOOM/m_argv.h @@ -0,0 +1,43 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Nil. +// +//----------------------------------------------------------------------------- + +#ifndef __M_ARGV__ +#define __M_ARGV__ + + +// +// MISC +// +extern int myargc; +extern char** myargv; + +// Returns the position of the given parameter +// in the arg list (0 if not found). +int M_CheckParm(char* check); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/m_bbox.c b/src/DOOM/m_bbox.c new file mode 100644 index 0000000..d1b25b4 --- /dev/null +++ b/src/DOOM/m_bbox.c @@ -0,0 +1,50 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Main loop menu stuff. +// Random number LUT. +// Default Config File. +// PCX Screenshots. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "m_bbox.h" + + +void M_ClearBox(fixed_t* box) +{ + box[BOXTOP] = box[BOXRIGHT] = DOOM_MININT; + box[BOXBOTTOM] = box[BOXLEFT] = DOOM_MAXINT; +} + + +void M_AddToBox(fixed_t* box, fixed_t x, fixed_t y) +{ + if (x < box[BOXLEFT]) + box[BOXLEFT] = x; + else if (x > box[BOXRIGHT]) + box[BOXRIGHT] = x; + if (y < box[BOXBOTTOM]) + box[BOXBOTTOM] = y; + else if (y > box[BOXTOP]) + box[BOXTOP] = y; +} diff --git a/src/DOOM/m_bbox.h b/src/DOOM/m_bbox.h new file mode 100644 index 0000000..8ab5848 --- /dev/null +++ b/src/DOOM/m_bbox.h @@ -0,0 +1,51 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Nil. +// +//----------------------------------------------------------------------------- + +#ifndef __M_BBOX__ +#define __M_BBOX__ + + +#include "doomtype.h" +#include "m_fixed.h" + + +// Bounding box coordinate storage. +enum +{ + BOXTOP, + BOXBOTTOM, + BOXLEFT, + BOXRIGHT +}; // bbox coordinates + + +// Bounding box functions. +void M_ClearBox(fixed_t* box); +void M_AddToBox(fixed_t* box, fixed_t x, fixed_t y); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/m_cheat.c b/src/DOOM/m_cheat.c new file mode 100644 index 0000000..e81071f --- /dev/null +++ b/src/DOOM/m_cheat.c @@ -0,0 +1,91 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Cheat sequence checking. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "m_cheat.h" + + +// +// CHEAT SEQUENCE PACKAGE +// + +static int firsttime = 1; +static unsigned char cheat_xlate_table[256]; + + +// +// Called in st_stuff module, which handles the input. +// Returns a 1 if the cheat was successful, 0 if failed. +// +int cht_CheckCheat(cheatseq_t* cht, char key) +{ + int i; + int rc = 0; + + if (firsttime) + { + firsttime = 0; + for (i = 0; i < 256; i++) cheat_xlate_table[i] = SCRAMBLE(i); + } + + if (!cht->p) + cht->p = cht->sequence; // initialize if first time + + if (*cht->p == 0) + *(cht->p++) = key; + else if + (cheat_xlate_table[(unsigned char)key] == *cht->p) cht->p++; + else + cht->p = cht->sequence; + + if (*cht->p == 1) + cht->p++; + else if (*cht->p == 0xff) // end of sequence character + { + cht->p = cht->sequence; + rc = 1; + } + + return rc; +} + + +void cht_GetParam(cheatseq_t* cht, char* buffer) +{ + unsigned char* p, c; + + p = cht->sequence; + while (*(p++) != 1); + + do + { + c = *p; + *(buffer++) = c; + *(p++) = 0; + } while (c && *p != 0xff); + + if (*p == 0xff) + *buffer = 0; +} diff --git a/src/DOOM/m_cheat.h b/src/DOOM/m_cheat.h new file mode 100644 index 0000000..b128d5b --- /dev/null +++ b/src/DOOM/m_cheat.h @@ -0,0 +1,52 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Cheat code checking. +// +//----------------------------------------------------------------------------- + +#ifndef __M_CHEAT__ +#define __M_CHEAT__ + + +// +// CHEAT SEQUENCE PACKAGE +// + +#define SCRAMBLE(a) \ +((((a)&1)<<7) + (((a)&2)<<5) + ((a)&4) + (((a)&8)<<1) \ + + (((a)&16)>>1) + ((a)&32) + (((a)&64)>>5) + (((a)&128)>>7)) + + +typedef struct +{ + unsigned char* sequence; + unsigned char* p; +} cheatseq_t; + + +int cht_CheckCheat(cheatseq_t* cht, char key); +void cht_GetParam(cheatseq_t* cht, char* buffer); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/m_fixed.c b/src/DOOM/m_fixed.c new file mode 100644 index 0000000..b1d9896 --- /dev/null +++ b/src/DOOM/m_fixed.c @@ -0,0 +1,59 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Fixed point implementation. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "stdlib.h" +#include "doomtype.h" +#include "i_system.h" +#include "m_fixed.h" + + +fixed_t FixedMul(fixed_t a, fixed_t b) +{ + return ((long long)a * (long long)b) >> FRACBITS; +} + + +// +// FixedDiv, C version. +// +fixed_t FixedDiv(fixed_t a, fixed_t b) +{ + if ((doom_abs(a) >> 14) >= doom_abs(b)) + return (a ^ b) < 0 ? DOOM_MININT : DOOM_MAXINT; + return FixedDiv2(a, b); +} + + +fixed_t FixedDiv2(fixed_t a, fixed_t b) +{ + double c; + + c = ((double)a) / ((double)b) * FRACUNIT; + + if (c >= 2147483648.0 || c < -2147483648.0) + I_Error("Error: FixedDiv: divide by zero"); + return (fixed_t)c; +} diff --git a/src/DOOM/m_fixed.h b/src/DOOM/m_fixed.h new file mode 100644 index 0000000..ff45d7e --- /dev/null +++ b/src/DOOM/m_fixed.h @@ -0,0 +1,47 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Fixed point arithemtics, implementation. +// +//----------------------------------------------------------------------------- + +#ifndef __M_FIXED__ +#define __M_FIXED__ + + +// +// Fixed point, 32bit as 16.16. +// +#define FRACBITS 16 +#define FRACUNIT (1< + M_DrawEpisode, // drawing routine -> + 48,63, // x,y + ep1 // lastOn +}; + + +// +// NEW GAME +// +enum +{ + killthings, + toorough, + hurtme, + violence, + nightmare, + newg_end +} newgame_e; + +menuitem_t NewGameMenu[] = +{ + {1,"M_JKILL", M_ChooseSkill, 'i'}, + {1,"M_ROUGH", M_ChooseSkill, 'h'}, + {1,"M_HURT", M_ChooseSkill, 'h'}, + {1,"M_ULTRA", M_ChooseSkill, 'u'}, + {1,"M_NMARE", M_ChooseSkill, 'n'} +}; + +menu_t NewDef = +{ + newg_end, // # of menu items + &EpiDef, // previous menu + NewGameMenu, // menuitem_t -> + M_DrawNewGame, // drawing routine -> + 48,63, // x,y + hurtme // lastOn +}; + + +// +// OPTIONS MENU +// +menuitem_t* OptionsMenu; + +enum +{ + endgame, + messages, + crosshair_opt, + always_run_opt, + //detail, // Details do nothing? + scrnsize, + option_empty1, + mouseoptions, + soundvol, + opt_end +} options_e; + +menuitem_t OptionsMenuFull[] = +{ + {1,"M_ENDGAM", M_EndGame,'e'}, + {1,"M_MESSG", M_ChangeMessages,'m'}, + {1,"TXT_CROS", M_ChangeCrosshair,'c'}, + {1,"TXT_ARUN", M_ChangeAlwaysRun,'r'}, + //{1,"M_DETAIL", M_ChangeDetail,'g'}, // Details do nothing? + {2,"M_SCRNSZ", M_SizeDisplay,'s'}, + {-1,"",0}, + {1,"TXT_MOPT", M_MouseOptions,'f'}, + {1,"M_SVOL", M_Sound,'s'} +}; + +menu_t OptionsDef = +{ + opt_end, + &MainDef, + OptionsMenuFull, + M_DrawOptions, + 60,37, + 0 +}; + + +enum +{ + endgame_no_mouse, + messages_no_mouse, + crosshair_opt_no_mouse, + always_run_opt_no_mouse, + //detail_no_mouse, // Details do nothing? + scrnsize_no_mouse, + option_empty1_no_mouse, + soundvol_no_mouse, + opt_end_no_mouse +} options_e_no_mouse; + +menuitem_t OptionsMenuNoMouse[] = +{ + {1,"M_ENDGAM", M_EndGame,'e'}, + {1,"M_MESSG", M_ChangeMessages,'m'}, + {1,"TXT_CROS", M_ChangeCrosshair,'c'}, + {1,"TXT_ARUN", M_ChangeAlwaysRun,'r'}, + //{1,"M_DETAIL", M_ChangeDetail,'g'}, // Details do nothing? + {2,"M_SCRNSZ", M_SizeDisplay,'s'}, + {-1,"",0}, + {1,"M_SVOL", M_Sound,'s'} +}; + +menu_t OptionsNoMouseDef = +{ + opt_end_no_mouse, + &MainDef, + OptionsMenuNoMouse, + M_DrawOptions, + 60,37, + 0 +}; + + +enum +{ + endgame_no_sound, + messages_no_sound, + crosshair_opt_no_sound, + always_run_opt_no_sound, + //detail_no_sound, // Details do nothing? + scrnsize_no_sound, + option_empty1_no_sound, + mouseoptions_no_sound, + opt_end_no_sound +} options_e_no_sound; + +menuitem_t OptionsMenuNoSound[] = +{ + {1,"M_ENDGAM", M_EndGame,'e'}, + {1,"M_MESSG", M_ChangeMessages,'m'}, + {1,"TXT_CROS", M_ChangeCrosshair,'c'}, + {1,"TXT_ARUN", M_ChangeAlwaysRun,'r'}, + //{1,"M_DETAIL", M_ChangeDetail,'g'}, // Details do nothing? + {2,"M_SCRNSZ", M_SizeDisplay,'s'}, + {-1,"",0}, + {1,"TXT_MOPT", M_MouseOptions,'f'} +}; + +menu_t OptionsNoSoundDef = +{ + opt_end_no_sound, + &MainDef, + OptionsMenuNoSound, + M_DrawOptions, + 60,37, + 0 +}; + + +enum +{ + endgame_no_sound_no_mouse, + messages_no_sound_no_mouse, + crosshair_opt_no_sound_no_mouse, + always_run_top_no_sound_no_mouse, + //detail_no_sound_no_mouse, // Details do nothing? + scrnsize_no_sound_no_mouse, + option_empty1_no_sound_no_mouse, + opt_end_no_sound_no_mouse +} options_e_no_sound_no_mouse; + +menuitem_t OptionsMenuNoSoundNoMouse[] = +{ + {1,"M_ENDGAM", M_EndGame,'e'}, + {1,"M_MESSG", M_ChangeMessages,'m'}, + {1,"TXT_CROS", M_ChangeCrosshair,'c'}, + {1,"TXT_ARUN", M_ChangeAlwaysRun,'r'}, + //{1,"M_DETAIL", M_ChangeDetail,'g'}, // Details do nothing? + {2,"M_SCRNSZ", M_SizeDisplay,'s'}, + {-1,"",0} +}; + +menu_t OptionsNoSoundNoMouseDef = +{ + opt_end_no_sound_no_mouse, + &MainDef, + OptionsMenuNoSoundNoMouse, + M_DrawOptions, + 60,37, + 0 +}; + + +// +// MOUSE OPTIONS +// +enum +{ + mousemov, + mousesens, + mouse_option_empty1, + mouse_opt_end +} mouseoptions_e; + +menuitem_t MouseOptionsMenu[] = +{ + {1,"TXT_MMOV", M_MouseMove,'f'}, + {2,"M_MSENS", M_ChangeSensitivity,'m'}, + {-1,"",0}, +}; + +menu_t MouseOptionsDef = +{ + mouse_opt_end, + &OptionsDef, + MouseOptionsMenu, + M_DrawMouseOptions, + 60,70, + 0 +}; + + +// +// Read This! MENU 1 & 2 +// +enum +{ + rdthsempty1, + read1_end +} read_e; + +menuitem_t ReadMenu1[] = +{ + {1,"",M_ReadThis2,0} +}; + +menu_t ReadDef1 = +{ + read1_end, + &MainDef, + ReadMenu1, + M_DrawReadThis1, + 280,185, + 0 +}; + +enum +{ + rdthsempty2, + read2_end +} read_e2; + +menuitem_t ReadMenu2[] = +{ + {1,"",M_FinishReadThis,0} +}; + +menu_t ReadDef2 = +{ + read2_end, + &ReadDef1, + ReadMenu2, + M_DrawReadThis2, + 330,175, + 0 +}; + +// +// SOUND VOLUME MENU +// +menuitem_t* SoundMenu; + +enum +{ + sfx_vol, + sfx_empty1, + music_vol, + sfx_empty2, + sound_end +} sound_e; + +menuitem_t SoundMenuFull[] = +{ + {2,"M_SFXVOL",M_SfxVol,'s'}, + {-1,"",0}, + {2,"M_MUSVOL",M_MusicVol,'m'}, + {-1,"",0} +}; + +menu_t SoundDef = +{ + sound_end, + &OptionsDef, + SoundMenuFull, + M_DrawSound, + 80,64, + 0 +}; + + +enum +{ + music_vol_no_sfx, + sfx_empty2_no_sfx, + sound_end_no_sfx +} sound_e_no_sfx; + +menuitem_t SoundMenuNoSFX[] = +{ + {2,"M_MUSVOL",M_MusicVol,'m'}, + {-1,"",0} +}; + +menu_t SoundNoSFXDef = +{ + sound_end_no_sfx, + &OptionsDef, + SoundMenuNoSFX, + M_DrawSound, + 80,64, + 0 +}; + + +enum +{ + sfx_vol_no_music, + sfx_empty1_no_music, + sound_end_no_music +} sound_e_no_music; + +menuitem_t SoundMenuNoMusic[] = +{ + {2,"M_SFXVOL",M_SfxVol,'s'}, + {-1,"",0} +}; + +menu_t SoundNoMusicDef = +{ + sound_end_no_music, + &OptionsDef, + SoundMenuNoMusic, + M_DrawSound, + 80,64, + 0 +}; + + +// +// LOAD GAME MENU +// +enum +{ + load1, + load2, + load3, + load4, + load5, + load6, + load_end +} load_e; + +menuitem_t LoadMenu[] = +{ + {1,"", M_LoadSelect,'1'}, + {1,"", M_LoadSelect,'2'}, + {1,"", M_LoadSelect,'3'}, + {1,"", M_LoadSelect,'4'}, + {1,"", M_LoadSelect,'5'}, + {1,"", M_LoadSelect,'6'} +}; + +menu_t LoadDef = +{ + load_end, + &MainDef, + LoadMenu, + M_DrawLoad, + 80,54, + 0 +}; + +// +// SAVE GAME MENU +// +menuitem_t SaveMenu[] = +{ + {1,"", M_SaveSelect,'1'}, + {1,"", M_SaveSelect,'2'}, + {1,"", M_SaveSelect,'3'}, + {1,"", M_SaveSelect,'4'}, + {1,"", M_SaveSelect,'5'}, + {1,"", M_SaveSelect,'6'} +}; + +menu_t SaveDef = +{ + load_end, + &MainDef, + SaveMenu, + M_DrawSave, + 80,54, + 0 +}; + + +// +// M_DrawCustomMenuText +// Draw several segments of patches to make up new text +// +void M_DrawCustomMenuText(char* name, int x, int y) +{ + for (int i = 0; i < custom_texts_count; ++i) + { + menu_custom_text_t* custom_text = menu_custom_texts + i; + if (doom_strcmp(custom_text->name, name) == 0) + { + menu_custom_text_seg_t* seg = custom_text->segs; + while (seg->lump) + { + void* lump = W_CacheLumpName(seg->lump, PU_CACHE); + V_DrawPatchRectDirect(x + seg->offx, y, 0, lump, seg->x, seg->w); + ++seg; + } + break; + } + } +} + + +// +// M_ReadSaveStrings +// read the strings from the savegame files +// +void M_ReadSaveStrings(void) +{ + void* handle; + int count; + int i; + char name[256]; + + for (i = 0; i < load_end; i++) + { +#if 0 + if (M_CheckParm("-cdrom")) + //doom_sprintf(name, "c:\\doomdata\\" SAVEGAMENAME "%d.dsg", i); + else +#endif + { + //doom_sprintf(name, SAVEGAMENAME"%d.dsg", i); + doom_strcpy(name, SAVEGAMENAME); + doom_concat(name, doom_itoa(i, 10)); + doom_concat(name, ".dsg"); + } + + handle = doom_open(name, "r"); + if (handle == 0) + { + doom_strcpy(&savegamestrings[i][0], EMPTYSTRING); + LoadMenu[i].status = 0; + continue; + } + count = doom_read(handle, &savegamestrings[i], SAVESTRINGSIZE); + doom_close(handle); + LoadMenu[i].status = 1; + } +} + + +// +// M_LoadGame & Cie. +// +void M_DrawLoad(void) +{ + int i; + + V_DrawPatchDirect(72, 28, 0, W_CacheLumpName("M_LOADG", PU_CACHE)); + for (i = 0; i < load_end; i++) + { + M_DrawSaveLoadBorder(LoadDef.x, LoadDef.y + LINEHEIGHT * i); + M_WriteText(LoadDef.x, LoadDef.y + LINEHEIGHT * i, savegamestrings[i]); + } +} + + +// +// Draw border for the savegame description +// +void M_DrawSaveLoadBorder(int x, int y) +{ + int i; + + V_DrawPatchDirect(x - 8, y + 7, 0, W_CacheLumpName("M_LSLEFT", PU_CACHE)); + + for (i = 0; i < 24; i++) + { + V_DrawPatchDirect(x, y + 7, 0, W_CacheLumpName("M_LSCNTR", PU_CACHE)); + x += 8; + } + + V_DrawPatchDirect(x, y + 7, 0, W_CacheLumpName("M_LSRGHT", PU_CACHE)); +} + + +// +// User wants to load this game +// +void M_LoadSelect(int choice) +{ + char name[256]; + +#if 0 + if (M_CheckParm("-cdrom")) + //doom_sprintf(name, "c:\\doomdata\\"SAVEGAMENAME"%d.dsg", choice); + else +#endif + { + //doom_sprintf(name, SAVEGAMENAME"%d.dsg", choice); + doom_strcpy(name, SAVEGAMENAME); + doom_concat(name, doom_itoa(choice, 10)); + doom_concat(name, ".dsg"); + } + G_LoadGame(name); + M_ClearMenus(); +} + + +// +// Selected from DOOM menu +// +void M_LoadGame(int choice) +{ + if (netgame) + { + M_StartMessage(LOADNET, 0, false); + return; + } + + M_SetupNextMenu(&LoadDef); + M_ReadSaveStrings(); +} + + +// +// M_SaveGame & Cie. +// +void M_DrawSave(void) +{ + int i; + + V_DrawPatchDirect(72, 28, 0, W_CacheLumpName("M_SAVEG", PU_CACHE)); + for (i = 0; i < load_end; i++) + { + M_DrawSaveLoadBorder(LoadDef.x, LoadDef.y + LINEHEIGHT * i); + M_WriteText(LoadDef.x, LoadDef.y + LINEHEIGHT * i, savegamestrings[i]); + } + + if (saveStringEnter) + { + i = M_StringWidth(savegamestrings[saveSlot]); + M_WriteText(LoadDef.x + i, LoadDef.y + LINEHEIGHT * saveSlot, "_"); + } +} + + +// +// M_Responder calls this when user is finished +// +void M_DoSave(int slot) +{ + G_SaveGame(slot, savegamestrings[slot]); + M_ClearMenus(); + + // PICK QUICKSAVE SLOT YET? + if (quickSaveSlot == -2) + quickSaveSlot = slot; +} + + +// +// User wants to save. Start string input for M_Responder +// +void M_SaveSelect(int choice) +{ + // we are going to be intercepting all chars + saveStringEnter = 1; + + saveSlot = choice; + doom_strcpy(saveOldString, savegamestrings[choice]); + if (!doom_strcmp(savegamestrings[choice], EMPTYSTRING)) + savegamestrings[choice][0] = 0; + saveCharIndex = (int)doom_strlen(savegamestrings[choice]); +} + + +// +// Selected from DOOM menu +// +void M_SaveGame(int choice) +{ + if (!usergame) + { + M_StartMessage(SAVEDEAD, 0, false); + return; + } + + if (gamestate != GS_LEVEL) + return; + + M_SetupNextMenu(&SaveDef); + M_ReadSaveStrings(); +} + + +// +// M_QuickSave +// +void M_QuickSaveResponse(int ch) +{ + if (ch == 'y') + { + M_DoSave(quickSaveSlot); + S_StartSound(0, sfx_swtchx); + } +} + + +void M_QuickSave(void) +{ + if (!usergame) + { + S_StartSound(0, sfx_oof); + return; + } + + if (gamestate != GS_LEVEL) + return; + + if (quickSaveSlot < 0) + { + M_StartControlPanel(); + M_ReadSaveStrings(); + M_SetupNextMenu(&SaveDef); + quickSaveSlot = -2; // means to pick a slot now + return; + } + //doom_sprintf(tempstring, QSPROMPT, savegamestrings[quickSaveSlot]); + doom_strcpy(tempstring, QSPROMPT_1); + doom_concat(tempstring, savegamestrings[quickSaveSlot]); + doom_strcpy(tempstring, QSPROMPT_2); + M_StartMessage(tempstring, M_QuickSaveResponse, true); +} + + +// +// M_QuickLoad +// +void M_QuickLoadResponse(int ch) +{ + if (ch == 'y') + { + M_LoadSelect(quickSaveSlot); + S_StartSound(0, sfx_swtchx); + } +} + + +void M_QuickLoad(void) +{ + if (netgame) + { + M_StartMessage(QLOADNET, 0, false); + return; + } + + if (quickSaveSlot < 0) + { + M_StartMessage(QSAVESPOT, 0, false); + return; + } + //doom_sprintf(tempstring, QLPROMPT, savegamestrings[quickSaveSlot]); + doom_strcpy(tempstring, QLPROMPT_1); + doom_concat(tempstring, savegamestrings[quickSaveSlot]); + doom_strcpy(tempstring, QLPROMPT_2); + M_StartMessage(tempstring, M_QuickLoadResponse, true); +} + + +// +// Read This Menus +// Had a "quick hack to fix romero bug" +// +void M_DrawReadThis1(void) +{ + inhelpscreens = true; + switch (gamemode) + { + case commercial: + V_DrawPatchDirect(0, 0, 0, W_CacheLumpName("HELP", PU_CACHE)); + break; + case shareware: + case registered: + case retail: + V_DrawPatchDirect(0, 0, 0, W_CacheLumpName("HELP1", PU_CACHE)); + break; + default: + break; + } + return; +} + + +// +// Read This Menus - optional second page. +// +void M_DrawReadThis2(void) +{ + inhelpscreens = true; + switch (gamemode) + { + case retail: + case commercial: + // This hack keeps us from having to change menus. + V_DrawPatchDirect(0, 0, 0, W_CacheLumpName("CREDIT", PU_CACHE)); + break; + case shareware: + case registered: + V_DrawPatchDirect(0, 0, 0, W_CacheLumpName("HELP2", PU_CACHE)); + break; + default: + break; + } + return; +} + + +// +// Change Sfx & Music volumes +// +void M_DrawSound(void) +{ + V_DrawPatchDirect(60, 38, 0, W_CacheLumpName("M_SVOL", PU_CACHE)); + + if (!(doom_flags & DOOM_FLAG_HIDE_SOUND_OPTIONS)) + { + int offset = (doom_flags & DOOM_FLAG_HIDE_MUSIC_OPTIONS) ? sfx_vol_no_music : sfx_vol; + M_DrawThermo(SoundDef.x, SoundDef.y + LINEHEIGHT * (offset + 1), + 16, snd_SfxVolume); + } + + if (!(doom_flags & DOOM_FLAG_HIDE_MUSIC_OPTIONS)) + { + int offset = (doom_flags & DOOM_FLAG_HIDE_SOUND_OPTIONS) ? music_vol_no_sfx : music_vol; + M_DrawThermo(SoundDef.x, SoundDef.y + LINEHEIGHT * (offset + 1), + 16, snd_MusicVolume); + } +} + + +void M_Sound(int choice) +{ + M_SetupNextMenu(&SoundDef); +} + + +void M_MouseOptions(int choice) +{ + M_SetupNextMenu(&MouseOptionsDef); +} + + +void M_SfxVol(int choice) +{ + switch (choice) + { + case 0: + if (snd_SfxVolume) + snd_SfxVolume--; + break; + case 1: + if (snd_SfxVolume < 15) + snd_SfxVolume++; + break; + } + + S_SetSfxVolume(snd_SfxVolume /* *8 */); +} + + +void M_MusicVol(int choice) +{ + switch (choice) + { + case 0: + if (snd_MusicVolume) + snd_MusicVolume--; + break; + case 1: + if (snd_MusicVolume < 15) + snd_MusicVolume++; + break; + } + + S_SetMusicVolume(snd_MusicVolume /* *8 */); +} + + +// +// M_DrawMainMenu +// +void M_DrawMainMenu(void) +{ + V_DrawPatchDirect(94, 2, 0, W_CacheLumpName("M_DOOM", PU_CACHE)); +} + + +// +// M_NewGame +// +void M_DrawNewGame(void) +{ + V_DrawPatchDirect(96, 14, 0, W_CacheLumpName("M_NEWG", PU_CACHE)); + V_DrawPatchDirect(54, 38, 0, W_CacheLumpName("M_SKILL", PU_CACHE)); +} + + +void M_NewGame(int choice) +{ + if (netgame && !demoplayback) + { + M_StartMessage(NEWGAME, 0, false); + return; + } + + if (gamemode == commercial) + M_SetupNextMenu(&NewDef); + else + M_SetupNextMenu(&EpiDef); +} + + +// +// M_Episode +// +void M_DrawEpisode(void) +{ + V_DrawPatchDirect(54, 38, 0, W_CacheLumpName("M_EPISOD", PU_CACHE)); +} + +void M_VerifyNightmare(int ch) +{ + if (ch != 'y') + return; + + G_DeferedInitNew(nightmare, epi + 1, 1); + M_ClearMenus(); +} + +void M_ChooseSkill(int choice) +{ + if (choice == nightmare) + { + M_StartMessage(NIGHTMARE, M_VerifyNightmare, true); + return; + } + + G_DeferedInitNew(choice, epi + 1, 1); + M_ClearMenus(); +} + +void M_Episode(int choice) +{ + if ((gamemode == shareware) + && choice) + { + M_StartMessage(SWSTRING, 0, false); + M_SetupNextMenu(&ReadDef1); + return; + } + + // Yet another hack... + if ((gamemode == registered) + && (choice > 2)) + { + doom_print( + "M_Episode: 4th episode requires UltimateDOOM\n"); + choice = 0; + } + + epi = choice; + M_SetupNextMenu(&NewDef); +} + + +// +// M_Options +// +void M_DrawOptions(void) +{ + V_DrawPatchDirect(108, 15, 0, W_CacheLumpName("M_OPTTTL", PU_CACHE)); + + //V_DrawPatchDirect (OptionsDef.x + 175,OptionsDef.y+LINEHEIGHT*detail,0, + // W_CacheLumpName(detailNames[detailLevel],PU_CACHE)); // Details do nothing? + + V_DrawPatchDirect(OptionsDef.x + 120, OptionsDef.y + LINEHEIGHT * messages, 0, + W_CacheLumpName(msgNames[showMessages], PU_CACHE)); + + extern int crosshair; + V_DrawPatchDirect(OptionsDef.x + 131, OptionsDef.y + LINEHEIGHT * crosshair_opt, 0, + W_CacheLumpName(msgNames[crosshair], PU_CACHE)); + + extern int always_run; + V_DrawPatchDirect(OptionsDef.x + 147, OptionsDef.y + LINEHEIGHT * always_run_opt, 0, + W_CacheLumpName(msgNames[always_run], PU_CACHE)); + + M_DrawThermo(OptionsDef.x, OptionsDef.y + LINEHEIGHT * (scrnsize + 1), + 9, screenSize); +} + + +void M_DrawMouseOptions(void) +{ + M_DrawCustomMenuText("TXT_MOPT", 74, 45); + + V_DrawPatchDirect(MouseOptionsDef.x + 149, MouseOptionsDef.y + LINEHEIGHT * mousemov, 0, + W_CacheLumpName(msgNames[mousemove], PU_CACHE)); + + M_DrawThermo(MouseOptionsDef.x, MouseOptionsDef.y + LINEHEIGHT * (mousesens + 1), + 10, mouseSensitivity); +} + + +void M_Options(int choice) +{ + M_SetupNextMenu(&OptionsDef); +} + + +// +// Toggle messages on/off +// +void M_ChangeMessages(int choice) +{ + // warning: unused parameter `int choice' + choice = 0; + showMessages = 1 - showMessages; + + if (!showMessages) + players[consoleplayer].message = MSGOFF; + else + players[consoleplayer].message = MSGON; + + message_dontfuckwithme = true; +} + + +// +// Toggle crosshair on/off +// +void M_ChangeCrosshair(int choice) +{ + extern int crosshair; + + // warning: unused parameter `int choice' + choice = 0; + crosshair = 1 - crosshair; + + if (!crosshair) + players[consoleplayer].message = CROSSOFF; + else + players[consoleplayer].message = CROSSON; + + message_dontfuckwithme = true; +} + + +// +// Toggle always-run on/off +// +void M_ChangeAlwaysRun(int choice) +{ + extern int always_run; + + // warning: unused parameter `int choice' + choice = 0; + always_run = 1 - always_run; + + if (!always_run) + players[consoleplayer].message = ALWAYSRUNOFF; + else + players[consoleplayer].message = ALWAYSRUNON; + + message_dontfuckwithme = true; +} + + +// +// M_EndGame +// +void M_EndGameResponse(int ch) +{ + if (ch != 'y') + return; + + currentMenu->lastOn = itemOn; + M_ClearMenus(); + D_StartTitle(); +} + + +void M_EndGame(int choice) +{ + choice = 0; + if (!usergame) + { + S_StartSound(0, sfx_oof); + return; + } + + if (netgame) + { + M_StartMessage(NETEND, 0, false); + return; + } + + M_StartMessage(ENDGAME, M_EndGameResponse, true); +} + + +// +// M_ReadThis +// +void M_ReadThis(int choice) +{ + choice = 0; + M_SetupNextMenu(&ReadDef1); +} + + +void M_ReadThis2(int choice) +{ + choice = 0; + M_SetupNextMenu(&ReadDef2); +} + + +void M_FinishReadThis(int choice) +{ + choice = 0; + M_SetupNextMenu(&MainDef); +} + + +// +// M_QuitDOOM +// +void M_QuitResponse(int ch) +{ + if (ch != 'y') + return; + if (!netgame) + { + if (gamemode == commercial) + S_StartSound(0, quitsounds2[(gametic >> 2) & 7]); + else + S_StartSound(0, quitsounds[(gametic >> 2) & 7]); + I_WaitVBL(105); + } + I_Quit(); +} + + +void M_QuitDOOM(int choice) +{ + // We pick index 0 which is language sensitive, + // or one at random, between 1 and maximum number. + if (language != english) + { + //doom_sprintf(endstring, "%s\n\n"DOSY, endmsg[0]); + doom_strcpy(endstring, endmsg[0]); + doom_concat(endstring, "\n\n" DOSY); + } + else + { + //doom_sprintf(endstring, "%s\n\n" DOSY, endmsg[gametic % (NUM_QUITMESSAGES - 2) + 1]); + doom_strcpy(endstring, endmsg[gametic % (NUM_QUITMESSAGES - 2) + 1]); + doom_concat(endstring, "\n\n" DOSY); + } + + M_StartMessage(endstring, M_QuitResponse, true); +} + + +void M_ChangeSensitivity(int choice) +{ + switch (choice) + { + case 0: + if (mouseSensitivity) + mouseSensitivity--; + break; + case 1: + if (mouseSensitivity < 9) + mouseSensitivity++; + break; + } +} + + +void M_MouseMove(int choice) +{ + choice = 0; + mousemove = 1 - mousemove; + + return; +} + + +void M_ChangeDetail(int choice) +{ + choice = 0; + detailLevel = 1 - detailLevel; + + // FIXME - does not work. Remove anyway? + doom_print("M_ChangeDetail: low detail mode n.a.\n"); +} + + +void M_SizeDisplay(int choice) +{ + switch (choice) + { + case 0: + if (screenSize > 0) + { + screenblocks--; + screenSize--; + } + break; + case 1: + if (screenSize < 8) + { + screenblocks++; + screenSize++; + } + break; + } + + R_SetViewSize(screenblocks, detailLevel); +} + + +// +// Menu Functions +// +void M_DrawThermo(int x, int y, int thermWidth, int thermDot) +{ + int xx; + int i; + + xx = x; + V_DrawPatchDirect(xx, y, 0, W_CacheLumpName("M_THERML", PU_CACHE)); + xx += 8; + for (i = 0; i < thermWidth; i++) + { + V_DrawPatchDirect(xx, y, 0, W_CacheLumpName("M_THERMM", PU_CACHE)); + xx += 8; + } + V_DrawPatchDirect(xx, y, 0, W_CacheLumpName("M_THERMR", PU_CACHE)); + + V_DrawPatchDirect((x + 8) + thermDot * 8, y, + 0, W_CacheLumpName("M_THERMO", PU_CACHE)); +} + + +void M_DrawEmptyCell(menu_t* menu, int item) +{ + V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1, 0, + W_CacheLumpName("M_CELL1", PU_CACHE)); +} + + +void M_DrawSelCell(menu_t* menu, int item) +{ + V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1, 0, + W_CacheLumpName("M_CELL2", PU_CACHE)); +} + + +void M_StartMessage(char* string, void* routine, doom_boolean input) +{ + messageLastMenuActive = menuactive; + messageToPrint = 1; + messageString = string; + messageRoutine = routine; + messageNeedsInput = input; + menuactive = true; + return; +} + + +void M_StopMessage(void) +{ + menuactive = messageLastMenuActive; + messageToPrint = 0; +} + + +// +// Find string width from hu_font chars +// +int M_StringWidth(char* string) +{ + int i; + int w = 0; + int c; + + for (i = 0; i < doom_strlen(string); i++) + { + c = doom_toupper(string[i]) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + w += 4; + else + w += SHORT(hu_font[c]->width); + } + + return w; +} + + +// +// Find string height from hu_font chars +// +int M_StringHeight(char* string) +{ + int i; + int h; + int height = SHORT(hu_font[0]->height); + + h = height; + for (i = 0; i < doom_strlen(string); i++) + if (string[i] == '\n') + h += height; + + return h; +} + + +// +// Write a string using the hu_font +// +void M_WriteText(int x, int y, char* string) +{ + int w; + char* ch; + int c; + int cx; + int cy; + + ch = string; + cx = x; + cy = y; + + while (1) + { + c = *ch++; + if (!c) + break; + if (c == '\n') + { + cx = x; + cy += 12; + continue; + } + + c = doom_toupper(c) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + if (cx + w > SCREENWIDTH) + break; + V_DrawPatchDirect(cx, cy, 0, hu_font[c]); + cx += w; + } +} + + +// +// CONTROL PANEL +// + +// +// M_Responder +// +doom_boolean M_Responder(event_t* ev) +{ + int ch; + int i; + static int joywait = 0; + static int mousewait = 0; + static int mousey = 0; + static int lasty = 0; + static int mousex = 0; + static int lastx = 0; + + ch = -1; + + if (ev->type == ev_joystick && joywait < I_GetTime()) + { + if (ev->data3 == -1) + { + ch = KEY_UPARROW; + joywait = I_GetTime() + 5; + } + else if (ev->data3 == 1) + { + ch = KEY_DOWNARROW; + joywait = I_GetTime() + 5; + } + + if (ev->data2 == -1) + { + ch = KEY_LEFTARROW; + joywait = I_GetTime() + 2; + } + else if (ev->data2 == 1) + { + ch = KEY_RIGHTARROW; + joywait = I_GetTime() + 2; + } + + if (ev->data1 & 1) + { + ch = KEY_ENTER; + joywait = I_GetTime() + 5; + } + if (ev->data1 & 2) + { + ch = KEY_BACKSPACE; + joywait = I_GetTime() + 5; + } + } + else + { + if (ev->type == ev_mouse && mousewait < I_GetTime()) + { + mousey += ev->data3; + if (mousey < lasty - 30) + { + ch = KEY_DOWNARROW; + mousewait = I_GetTime() + 5; + mousey = lasty -= 30; + } + else if (mousey > lasty + 30) + { + ch = KEY_UPARROW; + mousewait = I_GetTime() + 5; + mousey = lasty += 30; + } + + mousex += ev->data2; + if (mousex < lastx - 30) + { + ch = KEY_LEFTARROW; + mousewait = I_GetTime() + 5; + mousex = lastx -= 30; + } + else if (mousex > lastx + 30) + { + ch = KEY_RIGHTARROW; + mousewait = I_GetTime() + 5; + mousex = lastx += 30; + } + + if (ev->data1 & 1) + { + ch = KEY_ENTER; + mousewait = I_GetTime() + 15; + } + + if (ev->data1 & 2) + { + ch = KEY_BACKSPACE; + mousewait = I_GetTime() + 15; + } + } + else + if (ev->type == ev_keydown) + { + ch = ev->data1; + } + } + + if (ch == -1) + return false; + + + // Save Game string input + if (saveStringEnter) + { + switch (ch) + { + case KEY_BACKSPACE: + if (saveCharIndex > 0) + { + saveCharIndex--; + savegamestrings[saveSlot][saveCharIndex] = 0; + } + break; + + case KEY_ESCAPE: + saveStringEnter = 0; + doom_strcpy(&savegamestrings[saveSlot][0], saveOldString); + break; + + case KEY_ENTER: + saveStringEnter = 0; + if (savegamestrings[saveSlot][0]) + M_DoSave(saveSlot); + break; + + default: + ch = doom_toupper(ch); + if (ch != 32) + if (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE) + break; + if (ch >= 32 && ch <= 127 && + saveCharIndex < SAVESTRINGSIZE - 1 && + M_StringWidth(savegamestrings[saveSlot]) < + (SAVESTRINGSIZE - 2) * 8) + { + savegamestrings[saveSlot][saveCharIndex++] = ch; + savegamestrings[saveSlot][saveCharIndex] = 0; + } + break; + } + return true; + } + + // Take care of any messages that need input + if (messageToPrint) + { + if (messageNeedsInput == true && + !(ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE)) + return false; + + menuactive = messageLastMenuActive; + messageToPrint = 0; + if (messageRoutine) + messageRoutine(ch); + + menuactive = false; + S_StartSound(0, sfx_swtchx); + return true; + } + + if (devparm && ch == KEY_F1) + { + G_ScreenShot(); + return true; + } + + + // F-Keys + if (!menuactive) + switch (ch) + { + case KEY_MINUS: // Screen size down + if (automapactive || chat_on) + return false; + M_SizeDisplay(0); + S_StartSound(0, sfx_stnmov); + return true; + + case KEY_EQUALS: // Screen size up + if (automapactive || chat_on) + return false; + M_SizeDisplay(1); + S_StartSound(0, sfx_stnmov); + return true; + + case KEY_F1: // Help key + M_StartControlPanel(); + + if (gamemode == retail) + currentMenu = &ReadDef2; + else + currentMenu = &ReadDef1; + + itemOn = 0; + S_StartSound(0, sfx_swtchn); + return true; + + case KEY_F2: // Save + M_StartControlPanel(); + S_StartSound(0, sfx_swtchn); + M_SaveGame(0); + return true; + + case KEY_F3: // Load + M_StartControlPanel(); + S_StartSound(0, sfx_swtchn); + M_LoadGame(0); + return true; + + case KEY_F4: // Sound Volume + M_StartControlPanel(); + currentMenu = &SoundDef; + itemOn = sfx_vol; + S_StartSound(0, sfx_swtchn); + return true; + + // case KEY_F5: // Detail toggle + // M_ChangeDetail(0); + // S_StartSound(0, sfx_swtchn); + // return true; + + case KEY_F5: // Crosshair toggle + M_ChangeCrosshair(0); + S_StartSound(0, sfx_swtchn); + return true; + + case KEY_F6: // Quicksave + S_StartSound(0, sfx_swtchn); + M_QuickSave(); + return true; + + case KEY_F7: // End game + S_StartSound(0, sfx_swtchn); + M_EndGame(0); + return true; + + case KEY_F8: // Toggle messages + M_ChangeMessages(0); + S_StartSound(0, sfx_swtchn); + return true; + + case KEY_F9: // Quickload + S_StartSound(0, sfx_swtchn); + M_QuickLoad(); + return true; + + case KEY_F10: // Quit DOOM + S_StartSound(0, sfx_swtchn); + M_QuitDOOM(0); + return true; + + case KEY_F11: // gamma toggle + usegamma++; + if (usegamma > 4) + usegamma = 0; + players[consoleplayer].message = gammamsg[usegamma]; + I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); + return true; + + } + + + // Pop-up menu? + if (!menuactive) + { + if (ch == KEY_ESCAPE) + { + M_StartControlPanel(); + S_StartSound(0, sfx_swtchn); + return true; + } + return false; + } + + + // Keys usable within menu + switch (ch) + { + case KEY_DOWNARROW: + do + { + if (itemOn + 1 > currentMenu->numitems - 1) + itemOn = 0; + else itemOn++; + S_StartSound(0, sfx_pstop); + } while (currentMenu->menuitems[itemOn].status == -1); + return true; + + case KEY_UPARROW: + do + { + if (!itemOn) + itemOn = currentMenu->numitems - 1; + else itemOn--; + S_StartSound(0, sfx_pstop); + } while (currentMenu->menuitems[itemOn].status == -1); + return true; + + case KEY_LEFTARROW: + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status == 2) + { + S_StartSound(0, sfx_stnmov); + currentMenu->menuitems[itemOn].routine(0); + } + return true; + + case KEY_RIGHTARROW: + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status == 2) + { + S_StartSound(0, sfx_stnmov); + currentMenu->menuitems[itemOn].routine(1); + } + return true; + + case KEY_ENTER: + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status) + { + currentMenu->lastOn = itemOn; + if (currentMenu->menuitems[itemOn].status == 2) + { + currentMenu->menuitems[itemOn].routine(1); // right arrow + S_StartSound(0, sfx_stnmov); + } + else + { + currentMenu->menuitems[itemOn].routine(itemOn); + S_StartSound(0, sfx_pistol); + } + } + return true; + + case KEY_ESCAPE: + currentMenu->lastOn = itemOn; + M_ClearMenus(); + S_StartSound(0, sfx_swtchx); + return true; + + case KEY_BACKSPACE: + currentMenu->lastOn = itemOn; + if (currentMenu->prevMenu) + { + currentMenu = currentMenu->prevMenu; + itemOn = currentMenu->lastOn; + S_StartSound(0, sfx_swtchn); + } + return true; + + default: + for (i = itemOn + 1; i < currentMenu->numitems; i++) + if (currentMenu->menuitems[i].alphaKey == ch) + { + itemOn = i; + S_StartSound(0, sfx_pstop); + return true; + } + for (i = 0; i <= itemOn; i++) + if (currentMenu->menuitems[i].alphaKey == ch) + { + itemOn = i; + S_StartSound(0, sfx_pstop); + return true; + } + break; + + } + + return false; +} + + +// +// M_StartControlPanel +// +void M_StartControlPanel(void) +{ + // intro might call this repeatedly + if (menuactive) + return; + + menuactive = 1; + currentMenu = &MainDef; // JDC + itemOn = currentMenu->lastOn; // JDC +} + + +// +// M_Drawer +// Called after the view has been rendered, +// but before it has been blitted. +// +void M_Drawer(void) +{ + static short x; + static short y; + short i; + short max; + char string[40]; + int start; + + inhelpscreens = false; + + + // Horiz. & Vertically center string and print it. + if (messageToPrint) + { + start = 0; + y = 100 - M_StringHeight(messageString) / 2; + while (*(messageString + start)) + { + for (i = 0; i < doom_strlen(messageString + start); i++) + if (*(messageString + start + i) == '\n') + { + doom_memset(string, 0, 40); + doom_strncpy(string, messageString + start, i); + start += i + 1; + break; + } + + if (i == doom_strlen(messageString + start)) + { + doom_strcpy(string, messageString + start); + start += i; + } + + x = 160 - M_StringWidth(string) / 2; + M_WriteText(x, y, string); + y += SHORT(hu_font[0]->height); + } + return; + } + + if (!menuactive) + return; + + // Darken background so the menu is more readable. + if (doom_flags & DOOM_FLAG_MENU_DARKEN_BG) + { + extern byte* screens[5]; + extern unsigned char screen_palette[256 * 3]; + extern lighttable_t* colormaps; + for (int j = 0, len = SCREENWIDTH * SCREENHEIGHT; j < len; ++j) + { + byte color = screens[0][j]; + color = colormaps[color + (20 * 256)]; + screens[0][j] = color; + } + } + + if (currentMenu->routine) + currentMenu->routine(); // call Draw routine + + // DRAW MENU + x = currentMenu->x; + y = currentMenu->y; + max = currentMenu->numitems; + + for (i = 0; i < max; i++) + { + menuitem_t* menuitem = currentMenu->menuitems + i; + if (menuitem->name[0]) + { + if (doom_strncmp(menuitem->name, "TXT_", 4) == 0) + { + M_DrawCustomMenuText(menuitem->name, x, y); + } + else + { + V_DrawPatchDirect(x, y, 0, W_CacheLumpName(menuitem->name, PU_CACHE)); + } + } + y += LINEHEIGHT; + } + + + // DRAW SKULL + V_DrawPatchDirect(x + SKULLXOFF, currentMenu->y - 5 + itemOn * LINEHEIGHT, 0, + W_CacheLumpName(skullName[whichSkull], PU_CACHE)); + +} + + +// +// M_ClearMenus +// +void M_ClearMenus(void) +{ + menuactive = 0; +} + + +// +// M_SetupNextMenu +// +void M_SetupNextMenu(menu_t* menudef) +{ + currentMenu = menudef; + itemOn = currentMenu->lastOn; +} + + +// +// M_Ticker +// +void M_Ticker(void) +{ + if (--skullAnimCounter <= 0) + { + whichSkull ^= 1; + skullAnimCounter = 8; + } +} + + +// +// M_Init +// +void M_Init(void) +{ + doom_boolean hide_mouse = (doom_flags & DOOM_FLAG_HIDE_MOUSE_OPTIONS) ? true : false; + doom_boolean hide_sound = ((doom_flags & DOOM_FLAG_HIDE_MUSIC_OPTIONS) && (doom_flags & DOOM_FLAG_HIDE_SOUND_OPTIONS)) ? true : false; + + OptionsMenu = OptionsMenuFull; + if (hide_mouse && !hide_sound) + { + OptionsMenu = OptionsMenuNoMouse; + doom_memcpy(&OptionsDef, &OptionsNoMouseDef, sizeof(OptionsDef)); + } + else if (!hide_mouse && hide_sound) + { + OptionsMenu = OptionsMenuNoSound; + doom_memcpy(&OptionsDef, &OptionsNoSoundDef, sizeof(OptionsDef)); + } + else if (hide_mouse && hide_sound) + { + OptionsMenu = OptionsMenuNoSoundNoMouse; + doom_memcpy(&OptionsDef, &OptionsNoSoundNoMouseDef, sizeof(OptionsDef)); + } + + SoundMenu = SoundMenuFull; + if (doom_flags & DOOM_FLAG_HIDE_MUSIC_OPTIONS) + { + SoundMenu = SoundMenuNoMusic; + doom_memcpy(&SoundDef, &SoundNoMusicDef, sizeof(SoundDef)); + } + else if (doom_flags & DOOM_FLAG_HIDE_SOUND_OPTIONS) + { + SoundMenu = SoundMenuNoSFX; + doom_memcpy(&SoundDef, &SoundNoSFXDef, sizeof(SoundDef)); + } + + currentMenu = &MainDef; + menuactive = 0; + itemOn = currentMenu->lastOn; + whichSkull = 0; + skullAnimCounter = 10; + screenSize = screenblocks - 3; + messageToPrint = 0; + messageString = 0; + messageLastMenuActive = menuactive; + quickSaveSlot = -1; + + // Here we could catch other version dependencies, + // like HELP1/2, and four episodes. + + switch (gamemode) + { + case commercial: + // This is used because DOOM 2 had only one HELP + // page. I use CREDIT as second page now, but + // kept this hack for educational purposes. + MainMenu[readthis] = MainMenu[quitdoom]; + MainDef.numitems--; + MainDef.y += 8; + NewDef.prevMenu = &MainDef; + ReadDef1.routine = M_DrawReadThis1; + ReadDef1.x = 330; + ReadDef1.y = 165; + ReadMenu1[0].routine = M_FinishReadThis; + break; + case shareware: + // Episode 2 and 3 are handled, + // branching to an ad screen. + case registered: + // We need to remove the fourth episode. + EpiDef.numitems--; + break; + case retail: + // We are fine. + default: + break; + } +} diff --git a/src/DOOM/m_menu.h b/src/DOOM/m_menu.h new file mode 100644 index 0000000..beb1433 --- /dev/null +++ b/src/DOOM/m_menu.h @@ -0,0 +1,63 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Menu widget stuff, episode selection and such. +// +//----------------------------------------------------------------------------- + +#ifndef __M_MENU__ +#define __M_MENU__ + + +#include "d_event.h" + + +// +// MENUS +// + +// Called by main loop, +// saves config file and calls I_Quit when user exits. +// Even when the menu is not displayed, +// this can resize the view and change game parameters. +// Does all the real work of the menu interaction. +doom_boolean M_Responder(event_t* ev); + +// Called by main loop, +// only used for menu (skull cursor) animation. +void M_Ticker(void); + +// Called by main loop, +// draws the menus directly into the screen buffer. +void M_Drawer(void); + +// Called by D_DoomMain, +// loads the config file. +void M_Init(void); + +// Called by intro code to force menu up upon a keypress, +// does nothing if menu is already up. +void M_StartControlPanel(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/m_misc.c b/src/DOOM/m_misc.c new file mode 100644 index 0000000..fcee6a3 --- /dev/null +++ b/src/DOOM/m_misc.c @@ -0,0 +1,542 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// +// $Log:$ +// +// DESCRIPTION: +// Main loop menu stuff. +// Default Config File. +// PCX Screenshots. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "doomdef.h" +#include "z_zone.h" +#include "m_swap.h" +#include "m_argv.h" +#include "w_wad.h" +#include "i_system.h" +#include "i_video.h" +#include "v_video.h" +#include "hu_stuff.h" +#include "doomstat.h" // State. +#include "dstrings.h" // Data. +#include "m_misc.h" + + +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#define STRING_VALUE 0xFFFF + + +// +// SCREEN SHOTS +// +typedef struct +{ + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + + unsigned short xmin; + unsigned short ymin; + unsigned short xmax; + unsigned short ymax; + + unsigned short hres; + unsigned short vres; + + unsigned char palette[48]; + + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + + char filler[58]; + unsigned char data; // unbounded +} pcx_t; + + +// +// M_DrawText +// Returns the final X coordinate +// HU_Init must have been called to init the font +// +extern patch_t* hu_font[HU_FONTSIZE]; + + +// +// DEFAULTS +// +extern int key_right; +extern int key_left; +extern int key_up; +extern int key_down; + +extern int key_strafeleft; +extern int key_straferight; + +extern int key_fire; +extern int key_use; +extern int key_strafe; +extern int key_speed; + +extern int mousebfire; +extern int mousebstrafe; +extern int mousebforward; +extern int mousemove; + +extern int joybfire; +extern int joybstrafe; +extern int joybuse; +extern int joybspeed; + +extern int viewwidth; +extern int viewheight; + +extern int mouseSensitivity; +extern int showMessages; + +extern int detailLevel; + +extern int screenblocks; + +extern int showMessages; + +// machine-independent sound params +extern int numChannels; + +extern char* chat_macros[]; + +extern byte scantokey[128]; + + +int usemouse; +int usejoystick; +int crosshair; +int always_run; + + +default_t defaults[] = +{ + {"mouse_sensitivity",&mouseSensitivity, 5}, + {"sfx_volume",&snd_SfxVolume, 8}, + {"music_volume",&snd_MusicVolume, 8}, + {"show_messages",&showMessages, 1}, + + {"key_right",&key_right, KEY_RIGHTARROW}, + {"key_left",&key_left, KEY_LEFTARROW}, + {"key_up",&key_up, KEY_UPARROW}, + {"key_down",&key_down, KEY_DOWNARROW}, + {"key_strafeleft",&key_strafeleft, ','}, + {"key_straferight",&key_straferight, '.'}, + + {"key_fire",&key_fire, KEY_RCTRL}, + {"key_use",&key_use, ' '}, + {"key_strafe",&key_strafe, KEY_RALT}, + {"key_speed",&key_speed, KEY_RSHIFT}, + + {"use_mouse",&usemouse, 1}, + {"mouseb_fire",&mousebfire,0}, + {"mouseb_strafe",&mousebstrafe,1}, + {"mouseb_forward",&mousebforward,2}, + {"mouse_move",&mousemove,0}, + + {"use_joystick",&usejoystick, 0}, + {"joyb_fire",&joybfire,0}, + {"joyb_strafe",&joybstrafe,1}, + {"joyb_use",&joybuse,3}, + {"joyb_speed",&joybspeed,2}, + + {"screenblocks",&screenblocks, 9}, + {"detaillevel",&detailLevel, 0}, + {"crosshair",&crosshair, 0}, + {"always_run",&always_run, 0}, + + {"snd_channels",&numChannels, 3}, + + {"usegamma",&usegamma, 0}, + + {"chatmacro0", 0, STRING_VALUE, 0, 0, &chat_macros[0], HUSTR_CHATMACRO0 }, + {"chatmacro1", 0, STRING_VALUE, 0, 0, &chat_macros[1], HUSTR_CHATMACRO1 }, + {"chatmacro2", 0, STRING_VALUE, 0, 0, &chat_macros[2], HUSTR_CHATMACRO2 }, + {"chatmacro3", 0, STRING_VALUE, 0, 0, &chat_macros[3], HUSTR_CHATMACRO3 }, + {"chatmacro4", 0, STRING_VALUE, 0, 0, &chat_macros[4], HUSTR_CHATMACRO4 }, + {"chatmacro5", 0, STRING_VALUE, 0, 0, &chat_macros[5], HUSTR_CHATMACRO5 }, + {"chatmacro6", 0, STRING_VALUE, 0, 0, &chat_macros[6], HUSTR_CHATMACRO6 }, + {"chatmacro7", 0, STRING_VALUE, 0, 0, &chat_macros[7], HUSTR_CHATMACRO7 }, + {"chatmacro8", 0, STRING_VALUE, 0, 0, &chat_macros[8], HUSTR_CHATMACRO8 }, + {"chatmacro9", 0, STRING_VALUE, 0, 0, &chat_macros[9], HUSTR_CHATMACRO9 } +}; + + +int numdefaults = sizeof(defaults) / sizeof(default_t);; +char* defaultfile; + + +int M_DrawText(int x, int y, doom_boolean direct, char* string) +{ + int c; + int w; + + while (*string) + { + c = doom_toupper(*string) - HU_FONTSTART; + string++; + if (c < 0 || c> HU_FONTSIZE) + { + x += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + if (x + w > SCREENWIDTH) + break; + if (direct) + V_DrawPatchDirect(x, y, 0, hu_font[c]); + else + V_DrawPatch(x, y, 0, hu_font[c]); + x += w; + } + + return x; +} + + +// +// M_WriteFile +// +doom_boolean M_WriteFile(char const* name, void* source, int length) +{ + void* handle; + int count; + + handle = doom_open(name, "wb"); + + if (handle == 0) + return false; + + count = doom_write(handle, source, length); + doom_close(handle); + + if (count < length) + return false; + + return true; +} + + +// +// M_ReadFile +// +int M_ReadFile(char const* name, byte** buffer) +{ + void* handle; + int count, length; + byte* buf; + + handle = doom_open(name, "rb"); + if (handle == 0) + { + //I_Error("Error: Couldn't read file %s", name); + + doom_strcpy(error_buf, "Error: Couldn't read file "); + doom_concat(error_buf, name); + I_Error(error_buf); + } + doom_seek(handle, 0, DOOM_SEEK_END); + length = doom_tell(handle); + doom_seek(handle, 0, DOOM_SEEK_SET); + buf = Z_Malloc(length, PU_STATIC, 0); + count = doom_read(handle, buf, length); + doom_close(handle); + + if (count < length) + { + //I_Error("Error: Couldn't read file %s", name); + + doom_strcpy(error_buf, "Error: Couldn't read file "); + doom_concat(error_buf, name); + I_Error(error_buf); + } + + *buffer = buf; + return length; +} + + +// +// M_SaveDefaults +// +void M_SaveDefaults(void) +{ + int i; + int v; + void* f; + + f = doom_open(defaultfile, "w"); + if (!f) + return; // can't write the file, but don't complain + + for (i = 0; i < numdefaults; i++) + { + if (defaults[i].defaultvalue > -0xfff + && defaults[i].defaultvalue < 0xfff) + { + v = *defaults[i].location; + //fprintf(f, "%s\t\t%i\n", defaults[i].name, v); + doom_fprint(f, defaults[i].name); + doom_fprint(f, "\t\t"); + doom_fprint(f, doom_itoa(v, 10)); + doom_fprint(f, "\n"); + } + else + { + //fprintf(f, "%s\t\t\"%s\"\n", defaults[i].name, + // *(char**)(defaults[i].text_location)); + doom_fprint(f, defaults[i].name); + doom_fprint(f, "\t\t\""); + doom_fprint(f, *(char**)(defaults[i].text_location)); + doom_fprint(f, "\"\n"); + } + } + + doom_close(f); +} + + +// +// M_LoadDefaults +// +void M_LoadDefaults(void) +{ + int i; + int len; + void* f; + char def[80]; + char strparm[100]; + char* newstring; + int parm; + doom_boolean isstring; + + // set everything to base values + // numdefaults = sizeof(defaults)/sizeof(defaults[0]); + for (i = 0; i < numdefaults; i++) + { + if (defaults[i].defaultvalue == 0xFFFF) + *defaults[i].text_location = defaults[i].default_text_value; + else + *defaults[i].location = (int)defaults[i].defaultvalue; + } + + // check for a custom default file + i = M_CheckParm("-config"); + if (i && i < myargc - 1) + { + defaultfile = myargv[i + 1]; + //doom_print(" default file: %s\n", defaultfile); + doom_print(" default file: "); + doom_print(defaultfile); + doom_print("\n"); + } + else + defaultfile = basedefault; + + // read the file in, overriding any set defaults + f = doom_open(defaultfile, "r"); + if (f) + { + while (!doom_eof(f)) + { + // def + int arg_read = 0; + char c; + for (i = 0; i < 79; ++i) + { + doom_read(f, &c, 1); + if (c == ' ' || c == '\n' || c == '\t') + { + if (i > 0) arg_read++; + break; + } + def[i] = c; + } + def[i] = '\0'; + + // Ignore spaces + if (c != '\n') + { + while (1) + { + doom_read(f, &c, 1); + if (c != ' ' && c != '\t') break; + } + + // strparam + i = 0; + if (c != '\n') + { + for (; i < 260;) + { + strparm[i++] = c; + doom_read(f, &c, 1); + if (c == '\n') + { + if (i > 0) arg_read++; + break; + } + } + } + strparm[i] = '\0'; + } + + isstring = false; + //if (fscanf(f, "%79s %[^\n]\n", def, strparm) == 2) + if (arg_read == 2) + { + if (strparm[0] == '"') + { + // get a string default + isstring = true; + len = (int)doom_strlen(strparm); + newstring = (char*)doom_malloc(len); + strparm[len - 1] = 0; + doom_strcpy(newstring, strparm + 1); + } + else if (strparm[0] == '0' && strparm[1] == 'x') + { + //sscanf(strparm + 2, "%x", &parm); + parm = doom_atox(strparm + 2); + } + else + { + //sscanf(strparm, "%i", &parm); + parm = doom_atoi(strparm); + } + for (i = 0; i < numdefaults; i++) + if (!doom_strcmp(def, defaults[i].name)) + { + if (!isstring) + *defaults[i].location = parm; + else + *defaults[i].text_location = newstring; + break; + } + } + } + + doom_close(f); + } +} + + +// +// WritePCXfile +// +void WritePCXfile(char* filename, byte* data, int width, int height, byte* palette) +{ + int i; + int length; + pcx_t* pcx; + byte* pack; + + pcx = Z_Malloc(width * height * 2 + 1000, PU_STATIC, 0); + + pcx->manufacturer = 0x0a; // PCX id + pcx->version = 5; // 256 color + pcx->encoding = 1; // uncompressed + pcx->bits_per_pixel = 8; // 256 color + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = SHORT(width - 1); + pcx->ymax = SHORT(height - 1); + pcx->hres = SHORT(width); + pcx->vres = SHORT(height); + doom_memset(pcx->palette, 0, sizeof(pcx->palette)); + pcx->color_planes = 1; // chunky image + pcx->bytes_per_line = SHORT(width); + pcx->palette_type = SHORT(2); // not a grey scale + doom_memset(pcx->filler, 0, sizeof(pcx->filler)); + + // pack the image + pack = &pcx->data; + + for (i = 0; i < width * height; i++) + { + if ((*data & 0xc0) != 0xc0) + *pack++ = *data++; + else + { + *pack++ = 0xc1; + *pack++ = *data++; + } + } + + // write the palette + *pack++ = 0x0c; // palette ID byte + for (i = 0; i < 768; i++) + *pack++ = *palette++; + + // write output file + length = (int)(pack - (byte*)pcx); + M_WriteFile(filename, pcx, length); + + Z_Free(pcx); +} + + +// +// M_ScreenShot +// +void M_ScreenShot(void) +{ + int i; + byte* linear; + char lbmname[12]; + void* f; + + // munge planar buffer to linear + linear = screens[2]; + I_ReadScreen(linear); + + // find a file name to save it to + doom_strcpy(lbmname, "DOOM00.pcx"); + + for (i = 0; i <= 99; i++) + { + lbmname[4] = i / 10 + '0'; + lbmname[5] = i % 10 + '0'; + if ((f = doom_open(lbmname, "wb")) == 0) + break; // file doesn't exist + doom_close(f); + } + if (i == 100) + I_Error("Error: M_ScreenShot: Couldn't create a PCX"); + + // save the pcx file + WritePCXfile(lbmname, linear, + SCREENWIDTH, SCREENHEIGHT, + W_CacheLumpName("PLAYPAL", PU_CACHE)); + + players[consoleplayer].message = "screen shot"; +} diff --git a/src/DOOM/m_misc.h b/src/DOOM/m_misc.h new file mode 100644 index 0000000..41b6491 --- /dev/null +++ b/src/DOOM/m_misc.h @@ -0,0 +1,58 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// +// +//----------------------------------------------------------------------------- + +#ifndef __M_MISC__ +#define __M_MISC__ + + +#include "doomtype.h" + + +// +// MISC +// +typedef struct +{ + char* name; + int* location; + int defaultvalue; + int scantranslate; // PC scan code hack + int untranslated; // lousy hack + char** text_location; // [pd] int* location was used to store text pointer. Can't change to intptr_t unless we change all settings type + char* default_text_value; // [pd] So we don't change defaultvalue behavior for int to intptr_t +} default_t; + + +doom_boolean M_WriteFile(char const* name, void* source, int length); +int M_ReadFile(char const* name, byte** buffer); +void M_ScreenShot(void); +void M_LoadDefaults(void); +void M_SaveDefaults(void); +int M_DrawText(int x, int y, doom_boolean direct, char* string); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/m_random.c b/src/DOOM/m_random.c new file mode 100644 index 0000000..5e5ac8d --- /dev/null +++ b/src/DOOM/m_random.c @@ -0,0 +1,77 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Random number LUT. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + +// +// M_Random +// Returns a 0-255 number +// +unsigned char rndtable[256] = { + 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 , + 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 , + 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 , + 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 , + 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 , + 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 , + 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 , + 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 , + 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 , + 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 , + 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 , + 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 , + 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 , + 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 , + 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 , + 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 , + 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 , + 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 , + 120, 163, 236, 249 +}; + + +int rndindex = 0; +int prndindex = 0; + + +// Which one is deterministic? +int P_Random(void) +{ + prndindex = (prndindex + 1) & 0xff; + return rndtable[prndindex]; +} + + +int M_Random(void) +{ + rndindex = (rndindex + 1) & 0xff; + return rndtable[rndindex]; +} + + +void M_ClearRandom(void) +{ + rndindex = prndindex = 0; +} diff --git a/src/DOOM/m_random.h b/src/DOOM/m_random.h new file mode 100644 index 0000000..6b5c123 --- /dev/null +++ b/src/DOOM/m_random.h @@ -0,0 +1,46 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// +// +//----------------------------------------------------------------------------- + +#ifndef __M_RANDOM__ +#define __M_RANDOM__ + + +#include "doomtype.h" + + +// Returns a number from 0 to 255, +// from a lookup table. +int M_Random(void); + +// As M_Random, but used only by the play simulation. +int P_Random(void); + +// Fix randoms for demos. +void M_ClearRandom(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/m_swap.c b/src/DOOM/m_swap.c new file mode 100644 index 0000000..89fbe00 --- /dev/null +++ b/src/DOOM/m_swap.c @@ -0,0 +1,53 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Endianess handling, swapping 16bit and 32bit. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "m_swap.h" + + +// Not needed with big endian. +#ifdef __BIG_ENDIAN__ + + +// Swap 16bit, that is, MSB and LSB byte. +unsigned short SwapSHORT(unsigned short x) +{ + // No masking with 0xFF should be necessary. + return (x >> 8) | (x << 8); +} + + +// Swapping 32bit. +unsigned long SwapLONG(unsigned long x) +{ + return + (x >> 24) + | ((x >> 8) & 0xff00) + | ((x << 8) & 0xff0000) + | (x << 24); +} + + +#endif diff --git a/src/DOOM/m_swap.h b/src/DOOM/m_swap.h new file mode 100644 index 0000000..7c15320 --- /dev/null +++ b/src/DOOM/m_swap.h @@ -0,0 +1,45 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Endianess handling, swapping 16bit and 32bit. +// +//----------------------------------------------------------------------------- + +#ifndef __M_SWAP__ +#define __M_SWAP__ + + +// Endianess handling. +// WAD files are stored little endian. +#ifdef __BIG_ENDIAN__ +unsigned short SwapSHORT(unsigned short); +unsigned long SwapLONG(unsigned long); +#define SHORT(x) ((short)SwapSHORT((unsigned short) (x))) +#define LONG(x) ((long)SwapLONG((unsigned long) (x))) +#else +#define SHORT(x) (x) +#define LONG(x) (x) +#endif + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/p_ceilng.c b/src/DOOM/p_ceilng.c new file mode 100644 index 0000000..943ec38 --- /dev/null +++ b/src/DOOM/p_ceilng.c @@ -0,0 +1,322 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: Ceiling aninmation (lowering, crushing, raising) +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "z_zone.h" +#include "doomdef.h" +#include "p_local.h" +#include "s_sound.h" +#include "doomstat.h" // State. +#include "r_state.h" // State. +#include "sounds.h" // Data. + + +// +// CEILINGS +// + +ceiling_t* activeceilings[MAXCEILINGS]; + + +// +// T_MoveCeiling +// +void T_MoveCeiling(ceiling_t* ceiling) +{ + result_e res; + + switch (ceiling->direction) + { + case 0: + // IN STASIS + break; + case 1: + // UP + res = T_MovePlane(ceiling->sector, + ceiling->speed, + ceiling->topheight, + false, 1, ceiling->direction); + + if (!(leveltime & 7)) + { + switch (ceiling->type) + { + case silentCrushAndRaise: + break; + default: + S_StartSound((mobj_t*)&ceiling->sector->soundorg, + sfx_stnmov); + // ? + break; + } + } + + if (res == pastdest) + { + switch (ceiling->type) + { + case raiseToHighest: + P_RemoveActiveCeiling(ceiling); + break; + + case silentCrushAndRaise: + S_StartSound((mobj_t*)&ceiling->sector->soundorg, + sfx_pstop); + case fastCrushAndRaise: + case crushAndRaise: + ceiling->direction = -1; + break; + + default: + break; + } + + } + break; + + case -1: + // DOWN + res = T_MovePlane(ceiling->sector, + ceiling->speed, + ceiling->bottomheight, + ceiling->crush, 1, ceiling->direction); + + if (!(leveltime & 7)) + { + switch (ceiling->type) + { + case silentCrushAndRaise: break; + default: + S_StartSound((mobj_t*)&ceiling->sector->soundorg, + sfx_stnmov); + } + } + + if (res == pastdest) + { + switch (ceiling->type) + { + case silentCrushAndRaise: + S_StartSound((mobj_t*)&ceiling->sector->soundorg, + sfx_pstop); + case crushAndRaise: + ceiling->speed = CEILSPEED; + case fastCrushAndRaise: + ceiling->direction = 1; + break; + + case lowerAndCrush: + case lowerToFloor: + P_RemoveActiveCeiling(ceiling); + break; + + default: + break; + } + } + else // ( res != pastdest ) + { + if (res == crushed) + { + switch (ceiling->type) + { + case silentCrushAndRaise: + case crushAndRaise: + case lowerAndCrush: + ceiling->speed = CEILSPEED / 8; + break; + + default: + break; + } + } + } + break; + } +} + + +// +// EV_DoCeiling +// Move a ceiling up/down and all around! +// +int EV_DoCeiling(line_t* line, ceiling_e type) +{ + int secnum; + int rtn; + sector_t* sec; + ceiling_t* ceiling; + + secnum = -1; + rtn = 0; + + // Reactivate in-stasis ceilings...for certain types. + switch (type) + { + case fastCrushAndRaise: + case silentCrushAndRaise: + case crushAndRaise: + P_ActivateInStasisCeiling(line); + default: + break; + } + + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) + continue; + + // new door thinker + rtn = 1; + ceiling = Z_Malloc(sizeof(*ceiling), PU_LEVSPEC, 0); + P_AddThinker(&ceiling->thinker); + sec->specialdata = ceiling; + ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; + ceiling->sector = sec; + ceiling->crush = false; + + switch (type) + { + case fastCrushAndRaise: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8 * FRACUNIT); + ceiling->direction = -1; + ceiling->speed = CEILSPEED * 2; + break; + + case silentCrushAndRaise: + case crushAndRaise: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + case lowerAndCrush: + case lowerToFloor: + ceiling->bottomheight = sec->floorheight; + if (type != lowerToFloor) + ceiling->bottomheight += 8 * FRACUNIT; + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + case raiseToHighest: + ceiling->topheight = P_FindHighestCeilingSurrounding(sec); + ceiling->direction = 1; + ceiling->speed = CEILSPEED; + break; + } + + ceiling->tag = sec->tag; + ceiling->type = type; + P_AddActiveCeiling(ceiling); + } + + return rtn; +} + + +// +// Add an active ceiling +// +void P_AddActiveCeiling(ceiling_t* c) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] == 0) + { + activeceilings[i] = c; + return; + } + } +} + + +// +// Remove a ceiling's thinker +// +void P_RemoveActiveCeiling(ceiling_t* c) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] == c) + { + activeceilings[i]->sector->specialdata = 0; + P_RemoveThinker(&activeceilings[i]->thinker); + activeceilings[i] = 0; + break; + } + } +} + + +// +// Restart a ceiling that's in-stasis +// +void P_ActivateInStasisCeiling(line_t* line) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] + && (activeceilings[i]->tag == line->tag) + && (activeceilings[i]->direction == 0)) + { + activeceilings[i]->direction = activeceilings[i]->olddirection; + activeceilings[i]->thinker.function.acp1 + = (actionf_p1)T_MoveCeiling; + } + } +} + + +// +// EV_CeilingCrushStop +// Stop a ceiling from crushing! +// +int EV_CeilingCrushStop(line_t* line) +{ + int i; + int rtn; + + rtn = 0; + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] + && (activeceilings[i]->tag == line->tag) + && (activeceilings[i]->direction != 0)) + { + activeceilings[i]->olddirection = activeceilings[i]->direction; + activeceilings[i]->thinker.function.acv = (actionf_v)0; + activeceilings[i]->direction = 0; // in-stasis + rtn = 1; + } + } + + return rtn; +} diff --git a/src/DOOM/p_doors.c b/src/DOOM/p_doors.c new file mode 100644 index 0000000..e562102 --- /dev/null +++ b/src/DOOM/p_doors.c @@ -0,0 +1,517 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: Door animation code (opening/closing) +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "z_zone.h" +#include "doomdef.h" +#include "p_local.h" +#include "s_sound.h" +#include "doomstat.h" // State. +#include "r_state.h" // State. +#include "dstrings.h" // Data. +#include "sounds.h" // Data. + + +// +// VERTICAL DOORS +// + +// +// T_VerticalDoor +// +void T_VerticalDoor(vldoor_t* door) +{ + result_e res; + + switch (door->direction) + { + case 0: + // WAITING + if (!--door->topcountdown) + { + switch (door->type) + { + case blazeRaise: + door->direction = -1; // time to go back down + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_bdcls); + break; + + case door_normal: + door->direction = -1; // time to go back down + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_dorcls); + break; + + case close30ThenOpen: + door->direction = 1; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_doropn); + break; + + default: + break; + } + } + break; + + case 2: + // INITIAL WAIT + if (!--door->topcountdown) + { + switch (door->type) + { + case raiseIn5Mins: + door->direction = 1; + door->type = door_normal; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_doropn); + break; + + default: + break; + } + } + break; + + case -1: + // DOWN + res = T_MovePlane(door->sector, + door->speed, + door->sector->floorheight, + false, 1, door->direction); + if (res == pastdest) + { + switch (door->type) + { + case blazeRaise: + case blazeClose: + door->sector->specialdata = 0; + P_RemoveThinker(&door->thinker); // unlink and free + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_bdcls); + break; + + case door_normal: + case door_close: + door->sector->specialdata = 0; + P_RemoveThinker(&door->thinker); // unlink and free + break; + + case close30ThenOpen: + door->direction = 0; + door->topcountdown = 35 * 30; + break; + + default: + break; + } + } + else if (res == crushed) + { + switch (door->type) + { + case blazeClose: + case door_close: // DO NOT GO BACK UP! + break; + + default: + door->direction = 1; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_doropn); + break; + } + } + break; + + case 1: + // UP + res = T_MovePlane(door->sector, + door->speed, + door->topheight, + false, 1, door->direction); + + if (res == pastdest) + { + switch (door->type) + { + case blazeRaise: + case door_normal: + door->direction = 0; // wait at top + door->topcountdown = door->topwait; + break; + + case close30ThenOpen: + case blazeOpen: + case door_open: + door->sector->specialdata = 0; + P_RemoveThinker(&door->thinker); // unlink and free + break; + + default: + break; + } + } + break; + } +} + + +// +// EV_DoLockedDoor +// Move a locked door up/down +// +int EV_DoLockedDoor(line_t* line, vldoor_e type, mobj_t* thing) +{ + player_t* p; + + p = thing->player; + + if (!p) + return 0; + + switch (line->special) + { + case 99: // Blue Lock + case 133: + if (!p) + return 0; + if (!p->cards[it_bluecard] && !p->cards[it_blueskull]) + { + p->message = PD_BLUEO; + S_StartSound(0, sfx_oof); + return 0; + } + break; + + case 134: // Red Lock + case 135: + if (!p) + return 0; + if (!p->cards[it_redcard] && !p->cards[it_redskull]) + { + p->message = PD_REDO; + S_StartSound(0, sfx_oof); + return 0; + } + break; + + case 136: // Yellow Lock + case 137: + if (!p) + return 0; + if (!p->cards[it_yellowcard] && + !p->cards[it_yellowskull]) + { + p->message = PD_YELLOWO; + S_StartSound(0, sfx_oof); + return 0; + } + break; + } + + return EV_DoDoor(line, type); +} + + +int EV_DoDoor(line_t* line, vldoor_e type) +{ + int secnum, rtn; + sector_t* sec; + vldoor_t* door; + + secnum = -1; + rtn = 0; + + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) + continue; + + + // new door thinker + rtn = 1; + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + P_AddThinker(&door->thinker); + sec->specialdata = door; + + door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; + door->sector = sec; + door->type = type; + door->topwait = VDOORWAIT; + door->speed = VDOORSPEED; + + switch (type) + { + case blazeClose: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->direction = -1; + door->speed = VDOORSPEED * 4; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_bdcls); + break; + + case door_close: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->direction = -1; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_dorcls); + break; + + case close30ThenOpen: + door->topheight = sec->ceilingheight; + door->direction = -1; + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_dorcls); + break; + + case blazeRaise: + case blazeOpen: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->speed = VDOORSPEED * 4; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_bdopn); + break; + + case door_normal: + case door_open: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t*)&door->sector->soundorg, + sfx_doropn); + break; + + default: + break; + } + } + + return rtn; +} + + +// +// EV_VerticalDoor : open a door manually, no tag value +// +void EV_VerticalDoor(line_t* line, mobj_t* thing) +{ + player_t* player; + int secnum; + sector_t* sec; + vldoor_t* door; + int side; + + side = 0; // only front sides can be used + + // Check for locks + player = thing->player; + + switch (line->special) + { + case 26: // Blue Lock + case 32: + if (!player) + return; + + if (!player->cards[it_bluecard] && !player->cards[it_blueskull]) + { + player->message = PD_BLUEK; + S_StartSound(0, sfx_oof); + return; + } + break; + + case 27: // Yellow Lock + case 34: + if (!player) + return; + + if (!player->cards[it_yellowcard] && + !player->cards[it_yellowskull]) + { + player->message = PD_YELLOWK; + S_StartSound(0, sfx_oof); + return; + } + break; + + case 28: // Red Lock + case 33: + if (!player) + return; + + if (!player->cards[it_redcard] && !player->cards[it_redskull]) + { + player->message = PD_REDK; + S_StartSound(0, sfx_oof); + return; + } + break; + } + + // if the sector has an active thinker, use it + sec = sides[line->sidenum[side ^ 1]].sector; + secnum = (int)(sec - sectors); + + if (sec->specialdata) + { + door = sec->specialdata; + switch (line->special) + { + case 1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s + case 26: + case 27: + case 28: + case 117: + if (door->direction == -1) + door->direction = 1; // go back up + else + { + if (!thing->player) + return; // JDC: bad guys never close doors + + door->direction = -1; // start going down immediately + } + return; + } + } + + // for proper sound + switch (line->special) + { + case 117: // BLAZING DOOR RAISE + case 118: // BLAZING DOOR OPEN + S_StartSound((mobj_t*)&sec->soundorg, sfx_bdopn); + break; + + case 1: // NORMAL DOOR SOUND + case 31: + S_StartSound((mobj_t*)&sec->soundorg, sfx_doropn); + break; + + default: // LOCKED DOOR SOUND + S_StartSound((mobj_t*)&sec->soundorg, sfx_doropn); + break; + } + + + // new door thinker + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + P_AddThinker(&door->thinker); + sec->specialdata = door; + door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; + door->sector = sec; + door->direction = 1; + door->speed = VDOORSPEED; + door->topwait = VDOORWAIT; + + switch (line->special) + { + case 1: + case 26: + case 27: + case 28: + door->type = door_normal; + break; + + case 31: + case 32: + case 33: + case 34: + door->type = door_open; + line->special = 0; + break; + + case 117: // blazing door raise + door->type = blazeRaise; + door->speed = VDOORSPEED * 4; + break; + case 118: // blazing door open + door->type = blazeOpen; + line->special = 0; + door->speed = VDOORSPEED * 4; + break; + } + + // find the top and bottom of the movement range + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; +} + + +// +// Spawn a door that closes after 30 seconds +// +void P_SpawnDoorCloseIn30(sector_t* sec) +{ + vldoor_t* door; + + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + + P_AddThinker(&door->thinker); + + sec->specialdata = door; + sec->special = 0; + + door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; + door->sector = sec; + door->direction = 0; + door->type = door_normal; + door->speed = VDOORSPEED; + door->topcountdown = 30 * 35; +} + + +// +// Spawn a door that opens after 5 minutes +// +void P_SpawnDoorRaiseIn5Mins(sector_t* sec, int secnum) +{ + vldoor_t* door; + + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + + P_AddThinker(&door->thinker); + + sec->specialdata = door; + sec->special = 0; + + door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; + door->sector = sec; + door->direction = 2; + door->type = raiseIn5Mins; + door->speed = VDOORSPEED; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->topwait = VDOORWAIT; + door->topcountdown = 5 * 60 * 35; +} diff --git a/src/DOOM/p_enemy.c b/src/DOOM/p_enemy.c new file mode 100644 index 0000000..84b7c57 --- /dev/null +++ b/src/DOOM/p_enemy.c @@ -0,0 +1,1957 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Enemy thinking, AI. +// Action Pointer Functions +// that are associated with states/frames. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "m_random.h" +#include "i_system.h" +#include "doomdef.h" +#include "p_local.h" +#include "s_sound.h" +#include "g_game.h" +#include "doomstat.h" // State. +#include "r_state.h" // State. +#include "sounds.h" // Data. + + +#define MAXSPECIALCROSS 8 +#define FATSPREAD (ANG90/8) +#define SKULLSPEED (20*FRACUNIT) + + +typedef enum +{ + DI_EAST, + DI_NORTHEAST, + DI_NORTH, + DI_NORTHWEST, + DI_WEST, + DI_SOUTHWEST, + DI_SOUTH, + DI_SOUTHEAST, + DI_NODIR, + NUMDIRS +} dirtype_t; + + +// +// P_NewChaseDir related LUT. +// +dirtype_t opposite[] = +{ + DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, + DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR +}; + +dirtype_t diags[] = +{ + DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST +}; + +mobj_t* soundtarget; +fixed_t xspeed[8] = { FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000 }; +fixed_t yspeed[8] = { 0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000 }; +int TRACEANGLE = 0xc000000; +mobj_t* corpsehit; +mobj_t* vileobj; +fixed_t viletryx; +fixed_t viletryy; +mobj_t* braintargets[32]; +int numbraintargets; +int braintargeton; + + +extern line_t* spechit[MAXSPECIALCROSS]; +extern int numspechit; + + +void A_Fall(mobj_t* actor); +void A_Fire(mobj_t* actor); +void A_ReFire(player_t* player, pspdef_t* psp); +void A_SpawnFly(mobj_t* mo); + + +// +// ENEMY THINKING +// Enemies are allways spawned +// with targetplayer = -1, threshold = 0 +// Most monsters are spawned unaware of all players, +// but some can be made preaware +// + + +// +// Called by P_NoiseAlert. +// Recursively traverse adjacent sectors, +// sound blocking lines cut off traversal. +// +void P_RecursiveSound(sector_t* sec, int soundblocks) +{ + int i; + line_t* check; + sector_t* other; + + // wake up all monsters in this sector + if (sec->validcount == validcount + && sec->soundtraversed <= soundblocks + 1) + { + return; // already flooded + } + + sec->validcount = validcount; + sec->soundtraversed = soundblocks + 1; + sec->soundtarget = soundtarget; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + if (!(check->flags & ML_TWOSIDED)) + continue; + + P_LineOpening(check); + + if (openrange <= 0) + continue; // closed door + + if (sides[check->sidenum[0]].sector == sec) + other = sides[check->sidenum[1]].sector; + else + other = sides[check->sidenum[0]].sector; + + if (check->flags & ML_SOUNDBLOCK) + { + if (!soundblocks) + P_RecursiveSound(other, 1); + } + else + P_RecursiveSound(other, soundblocks); + } +} + + +// +// P_NoiseAlert +// If a monster yells at a player, +// it will alert other monsters to the player. +// +void P_NoiseAlert(mobj_t* target, mobj_t* emmiter) +{ + soundtarget = target; + validcount++; + P_RecursiveSound(emmiter->subsector->sector, 0); +} + + +// +// P_CheckMeleeRange +// +doom_boolean P_CheckMeleeRange(mobj_t* actor) +{ + mobj_t* pl; + fixed_t dist; + + if (!actor->target) + return false; + + pl = actor->target; + dist = P_AproxDistance(pl->x - actor->x, pl->y - actor->y); + + if (dist >= MELEERANGE - 20 * FRACUNIT + pl->info->radius) + return false; + + if (!P_CheckSight(actor, actor->target)) + return false; + + return true; +} + + +// +// P_CheckMissileRange +// +doom_boolean P_CheckMissileRange(mobj_t* actor) +{ + fixed_t dist; + + if (!P_CheckSight(actor, actor->target)) + return false; + + if (actor->flags & MF_JUSTHIT) + { + // the target just hit the enemy, + // so fight back! + actor->flags &= ~MF_JUSTHIT; + return true; + } + + if (actor->reactiontime) + return false; // do not attack yet + + // OPTIMIZE: get this from a global checksight + dist = P_AproxDistance(actor->x - actor->target->x, + actor->y - actor->target->y) - 64 * FRACUNIT; + + if (!actor->info->meleestate) + dist -= 128 * FRACUNIT; // no melee attack, so fire more + + dist >>= 16; + + if (actor->type == MT_VILE) + { + if (dist > 14 * 64) + return false; // too far away + } + + + if (actor->type == MT_UNDEAD) + { + if (dist < 196) + return false; // close for fist attack + dist >>= 1; + } + + if (actor->type == MT_CYBORG + || actor->type == MT_SPIDER + || actor->type == MT_SKULL) + { + dist >>= 1; + } + + if (dist > 200) + dist = 200; + + if (actor->type == MT_CYBORG && dist > 160) + dist = 160; + + if (P_Random() < dist) + return false; + + return true; +} + + +// +// P_Move +// Move in the current direction, +// returns false if the move is blocked. +// +doom_boolean P_Move(mobj_t* actor) +{ + fixed_t tryx; + fixed_t tryy; + + line_t* ld; + + // warning: 'catch', 'throw', and 'try' + // are all C++ reserved words + doom_boolean try_ok; + doom_boolean good; + + if (actor->movedir == DI_NODIR) + return false; + + if ((unsigned)actor->movedir >= 8) + I_Error("Error: Weird actor->movedir!"); + + tryx = actor->x + actor->info->speed * xspeed[actor->movedir]; + tryy = actor->y + actor->info->speed * yspeed[actor->movedir]; + + try_ok = P_TryMove(actor, tryx, tryy); + + if (!try_ok) + { + // open any specials + if (actor->flags & MF_FLOAT && floatok) + { + // must adjust height + if (actor->z < tmfloorz) + actor->z += FLOATSPEED; + else + actor->z -= FLOATSPEED; + + actor->flags |= MF_INFLOAT; + return true; + } + + if (!numspechit) + return false; + + actor->movedir = DI_NODIR; + good = false; + while (numspechit--) + { + ld = spechit[numspechit]; + // if the special is not a door + // that can be opened, + // return false + if (P_UseSpecialLine(actor, ld, 0)) + good = true; + } + return good; + } + else + { + actor->flags &= ~MF_INFLOAT; + } + + if (!(actor->flags & MF_FLOAT)) + actor->z = actor->floorz; + + return true; +} + + +// +// TryWalk +// Attempts to move actor on +// in its current (ob->moveangle) direction. +// If blocked by either a wall or an actor +// returns FALSE +// If move is either clear or blocked only by a door, +// returns TRUE and sets... +// If a door is in the way, +// an OpenDoor call is made to start it opening. +// +doom_boolean P_TryWalk(mobj_t* actor) +{ + if (!P_Move(actor)) + { + return false; + } + + actor->movecount = P_Random() & 15; + return true; +} + + +void P_NewChaseDir(mobj_t* actor) +{ + fixed_t deltax; + fixed_t deltay; + + dirtype_t d[3]; + + int tdir; + dirtype_t olddir; + + dirtype_t turnaround; + + if (!actor->target) + I_Error("Error: P_NewChaseDir: called with no target"); + + olddir = actor->movedir; + turnaround = opposite[olddir]; + + deltax = actor->target->x - actor->x; + deltay = actor->target->y - actor->y; + + if (deltax > 10 * FRACUNIT) + d[1] = DI_EAST; + else if (deltax < -10 * FRACUNIT) + d[1] = DI_WEST; + else + d[1] = DI_NODIR; + + if (deltay < -10 * FRACUNIT) + d[2] = DI_SOUTH; + else if (deltay > 10 * FRACUNIT) + d[2] = DI_NORTH; + else + d[2] = DI_NODIR; + + // try direct route + if (d[1] != DI_NODIR + && d[2] != DI_NODIR) + { + actor->movedir = diags[((deltay < 0) << 1) + (deltax > 0)]; + if (actor->movedir != turnaround && P_TryWalk(actor)) + return; + } + + // try other directions + if (P_Random() > 200 + || doom_abs(deltay) > doom_abs(deltax)) + { + tdir = d[1]; + d[1] = d[2]; + d[2] = tdir; + } + + if (d[1] == turnaround) + d[1] = DI_NODIR; + if (d[2] == turnaround) + d[2] = DI_NODIR; + + if (d[1] != DI_NODIR) + { + actor->movedir = d[1]; + if (P_TryWalk(actor)) + { + // either moved forward or attacked + return; + } + } + + if (d[2] != DI_NODIR) + { + actor->movedir = d[2]; + + if (P_TryWalk(actor)) + return; + } + + // there is no direct path to the player, + // so pick another direction. + if (olddir != DI_NODIR) + { + actor->movedir = olddir; + + if (P_TryWalk(actor)) + return; + } + + // randomly determine direction of search + if (P_Random() & 1) + { + for (tdir = DI_EAST; + tdir <= DI_SOUTHEAST; + tdir++) + { + if (tdir != turnaround) + { + actor->movedir = tdir; + + if (P_TryWalk(actor)) + return; + } + } + } + else + { + for (tdir = DI_SOUTHEAST; + tdir != (DI_EAST - 1); + tdir--) + { + if (tdir != turnaround) + { + actor->movedir = tdir; + + if (P_TryWalk(actor)) + return; + } + } + } + + if (turnaround != DI_NODIR) + { + actor->movedir = turnaround; + if (P_TryWalk(actor)) + return; + } + + actor->movedir = DI_NODIR; // can not move +} + + +// +// P_LookForPlayers +// If allaround is false, only look 180 degrees in front. +// Returns true if a player is targeted. +// +doom_boolean P_LookForPlayers(mobj_t* actor, doom_boolean allaround) +{ + int c; + int stop; + player_t* player; + sector_t* sector; + angle_t an; + fixed_t dist; + + sector = actor->subsector->sector; + + c = 0; + stop = (actor->lastlook - 1) & 3; + + for (; ; actor->lastlook = (actor->lastlook + 1) & 3) + { + if (!playeringame[actor->lastlook]) + continue; + + if (c++ == 2 + || actor->lastlook == stop) + { + // done looking + return false; + } + + player = &players[actor->lastlook]; + + if (player->health <= 0) + continue; // dead + + if (!P_CheckSight(actor, player->mo)) + continue; // out of sight + + if (!allaround) + { + an = R_PointToAngle2(actor->x, + actor->y, + player->mo->x, + player->mo->y) + - actor->angle; + + if (an > ANG90 && an < ANG270) + { + dist = P_AproxDistance(player->mo->x - actor->x, + player->mo->y - actor->y); + // if real close, react anyway + if (dist > MELEERANGE) + continue; // behind back + } + } + + actor->target = player->mo; + return true; + } + + return false; +} + + +// +// A_KeenDie +// DOOM II special, map 32. +// Uses special tag 666. +// +void A_KeenDie(mobj_t* mo) +{ + thinker_t* th; + mobj_t* mo2; + line_t junk; + + A_Fall(mo); + + // scan the remaining thinkers + // to see if all Keens are dead + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t*)th; + if (mo2 != mo + && mo2->type == mo->type + && mo2->health > 0) + { + // other Keen not dead + return; + } + } + + junk.tag = 666; + EV_DoDoor(&junk, door_open); +} + + +// +// ACTION ROUTINES +// + +// +// A_Look +// Stay in state until a player is sighted. +// +void A_Look(mobj_t* actor) +{ + mobj_t* targ; + + actor->threshold = 0; // any shot will wake up + targ = actor->subsector->sector->soundtarget; + + if (targ + && (targ->flags & MF_SHOOTABLE)) + { + actor->target = targ; + + if (actor->flags & MF_AMBUSH) + { + if (P_CheckSight(actor, actor->target)) + goto seeyou; + } + else + goto seeyou; + } + + + if (!P_LookForPlayers(actor, false)) + return; + + // go into chase state +seeyou: + if (actor->info->seesound) + { + int sound; + + switch (actor->info->seesound) + { + case sfx_posit1: + case sfx_posit2: + case sfx_posit3: + sound = sfx_posit1 + P_Random() % 3; + break; + + case sfx_bgsit1: + case sfx_bgsit2: + sound = sfx_bgsit1 + P_Random() % 2; + break; + + default: + sound = actor->info->seesound; + break; + } + + if (actor->type == MT_SPIDER + || actor->type == MT_CYBORG) + { + // full volume + S_StartSound(0, sound); + } + else + S_StartSound(actor, sound); + } + + P_SetMobjState(actor, actor->info->seestate); +} + + +// +// A_Chase +// Actor has a melee attack, +// so it tries to close as fast as possible +// +void A_Chase(mobj_t* actor) +{ + int delta; + + if (actor->reactiontime) + actor->reactiontime--; + + // modify target threshold + if (actor->threshold) + { + if (!actor->target + || actor->target->health <= 0) + { + actor->threshold = 0; + } + else + actor->threshold--; + } + + // turn towards movement direction if not there yet + if (actor->movedir < 8) + { + actor->angle &= (7 << 29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANG90 / 2; + else if (delta < 0) + actor->angle += ANG90 / 2; + } + + if (!actor->target + || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true)) + return; // got a new target + + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + // do not attack twice in a row + if (actor->flags & MF_JUSTATTACKED) + { + actor->flags &= ~MF_JUSTATTACKED; + if (gameskill != sk_nightmare && !fastparm) + P_NewChaseDir(actor); + return; + } + + // check for melee attack + if (actor->info->meleestate + && P_CheckMeleeRange(actor)) + { + if (actor->info->attacksound) + S_StartSound(actor, actor->info->attacksound); + + P_SetMobjState(actor, actor->info->meleestate); + return; + } + + // check for missile attack + if (actor->info->missilestate) + { + if (gameskill < sk_nightmare + && !fastparm && actor->movecount) + { + goto nomissile; + } + + if (!P_CheckMissileRange(actor)) + goto nomissile; + + P_SetMobjState(actor, actor->info->missilestate); + actor->flags |= MF_JUSTATTACKED; + return; + } + + // ? +nomissile: + // possibly choose another target + if (netgame + && !actor->threshold + && !P_CheckSight(actor, actor->target)) + { + if (P_LookForPlayers(actor, true)) + return; // got a new target + } + + // chase towards player + if (--actor->movecount < 0 + || !P_Move(actor)) + { + P_NewChaseDir(actor); + } + + // make active sound + if (actor->info->activesound + && P_Random() < 3) + { + S_StartSound(actor, actor->info->activesound); + } +} + + +// +// A_FaceTarget +// +void A_FaceTarget(mobj_t* actor) +{ + if (!actor->target) + return; + + actor->flags &= ~MF_AMBUSH; + + actor->angle = R_PointToAngle2(actor->x, + actor->y, + actor->target->x, + actor->target->y); + + if (actor->target->flags & MF_SHADOW) + actor->angle += (P_Random() - P_Random()) << 21; +} + + +// +// A_PosAttack +// +void A_PosAttack(mobj_t* actor) +{ + int angle; + int damage; + int slope; + + if (!actor->target) + return; + + A_FaceTarget(actor); + angle = actor->angle; + slope = P_AimLineAttack(actor, angle, MISSILERANGE); + + S_StartSound(actor, sfx_pistol); + angle += (P_Random() - P_Random()) << 20; + damage = ((P_Random() % 5) + 1) * 3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); +} + + +void A_SPosAttack(mobj_t* actor) +{ + int i; + int angle; + int bangle; + int damage; + int slope; + + if (!actor->target) + return; + + S_StartSound(actor, sfx_shotgn); + A_FaceTarget(actor); + bangle = actor->angle; + slope = P_AimLineAttack(actor, bangle, MISSILERANGE); + + for (i = 0; i < 3; i++) + { + angle = bangle + ((P_Random() - P_Random()) << 20); + damage = ((P_Random() % 5) + 1) * 3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); + } +} + + +void A_CPosAttack(mobj_t* actor) +{ + int angle; + int bangle; + int damage; + int slope; + + if (!actor->target) + return; + + S_StartSound(actor, sfx_shotgn); + A_FaceTarget(actor); + bangle = actor->angle; + slope = P_AimLineAttack(actor, bangle, MISSILERANGE); + + angle = bangle + ((P_Random() - P_Random()) << 20); + damage = ((P_Random() % 5) + 1) * 3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); +} + + +void A_CPosRefire(mobj_t* actor) +{ + // keep firing unless target got out of sight + A_FaceTarget(actor); + + if (P_Random() < 40) + return; + + if (!actor->target + || actor->target->health <= 0 + || !P_CheckSight(actor, actor->target)) + { + P_SetMobjState(actor, actor->info->seestate); + } +} + + +void A_SpidRefire(mobj_t* actor) +{ + // keep firing unless target got out of sight + A_FaceTarget(actor); + + if (P_Random() < 10) + return; + + if (!actor->target + || actor->target->health <= 0 + || !P_CheckSight(actor, actor->target)) + { + P_SetMobjState(actor, actor->info->seestate); + } +} + + +void A_BspiAttack(mobj_t* actor) +{ + if (!actor->target) + return; + + A_FaceTarget(actor); + + // launch a missile + P_SpawnMissile(actor, actor->target, MT_ARACHPLAZ); +} + + +// +// A_TroopAttack +// +void A_TroopAttack(mobj_t* actor) +{ + int damage; + + if (!actor->target) + return; + + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + S_StartSound(actor, sfx_claw); + damage = (P_Random() % 8 + 1) * 3; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + + // launch a missile + P_SpawnMissile(actor, actor->target, MT_TROOPSHOT); +} + + +void A_SargAttack(mobj_t* actor) +{ + int damage; + + if (!actor->target) + return; + + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + damage = ((P_Random() % 10) + 1) * 4; + P_DamageMobj(actor->target, actor, actor, damage); + } +} + + +void A_HeadAttack(mobj_t* actor) +{ + int damage; + + if (!actor->target) + return; + + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + damage = (P_Random() % 6 + 1) * 10; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + + // launch a missile + P_SpawnMissile(actor, actor->target, MT_HEADSHOT); +} + + +void A_CyberAttack(mobj_t* actor) +{ + if (!actor->target) + return; + + A_FaceTarget(actor); + P_SpawnMissile(actor, actor->target, MT_ROCKET); +} + + +void A_BruisAttack(mobj_t* actor) +{ + int damage; + + if (!actor->target) + return; + + if (P_CheckMeleeRange(actor)) + { + S_StartSound(actor, sfx_claw); + damage = (P_Random() % 8 + 1) * 10; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + + // launch a missile + P_SpawnMissile(actor, actor->target, MT_BRUISERSHOT); +} + + +// +// A_SkelMissile +// +void A_SkelMissile(mobj_t* actor) +{ + mobj_t* mo; + + if (!actor->target) + return; + + A_FaceTarget(actor); + actor->z += 16 * FRACUNIT; // so missile spawns higher + mo = P_SpawnMissile(actor, actor->target, MT_TRACER); + actor->z -= 16 * FRACUNIT; // back to normal + + mo->x += mo->momx; + mo->y += mo->momy; + mo->tracer = actor->target; +} + + +void A_Tracer(mobj_t* actor) +{ + angle_t exact; + fixed_t dist; + fixed_t slope; + mobj_t* dest; + mobj_t* th; + + if (gametic & 3) + return; + + // spawn a puff of smoke behind the rocket + P_SpawnPuff(actor->x, actor->y, actor->z); + + th = P_SpawnMobj(actor->x - actor->momx, + actor->y - actor->momy, + actor->z, MT_SMOKE); + + th->momz = FRACUNIT; + th->tics -= P_Random() & 3; + if (th->tics < 1) + th->tics = 1; + + // adjust direction + dest = actor->tracer; + + if (!dest || dest->health <= 0) + return; + + // change angle + exact = R_PointToAngle2(actor->x, + actor->y, + dest->x, + dest->y); + + if (exact != actor->angle) + { + if (exact - actor->angle > 0x80000000) + { + actor->angle -= TRACEANGLE; + if (exact - actor->angle < 0x80000000) + actor->angle = exact; + } + else + { + actor->angle += TRACEANGLE; + if (exact - actor->angle > 0x80000000) + actor->angle = exact; + } + } + + exact = actor->angle >> ANGLETOFINESHIFT; + actor->momx = FixedMul(actor->info->speed, finecosine[exact]); + actor->momy = FixedMul(actor->info->speed, finesine[exact]); + + // change slope + dist = P_AproxDistance(dest->x - actor->x, + dest->y - actor->y); + + dist = dist / actor->info->speed; + + if (dist < 1) + dist = 1; + slope = (dest->z + 40 * FRACUNIT - actor->z) / dist; + + if (slope < actor->momz) + actor->momz -= FRACUNIT / 8; + else + actor->momz += FRACUNIT / 8; +} + + +void A_SkelWhoosh(mobj_t* actor) +{ + if (!actor->target) + return; + + A_FaceTarget(actor); + S_StartSound(actor, sfx_skeswg); +} + + +void A_SkelFist(mobj_t* actor) +{ + int damage; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (P_CheckMeleeRange(actor)) + { + damage = ((P_Random() % 10) + 1) * 6; + S_StartSound(actor, sfx_skepch); + P_DamageMobj(actor->target, actor, actor, damage); + } +} + + +// +// PIT_VileCheck +// Detect a corpse that could be raised. +// +doom_boolean PIT_VileCheck(mobj_t* thing) +{ + int maxdist; + doom_boolean check; + + if (!(thing->flags & MF_CORPSE)) + return true; // not a monster + + if (thing->tics != -1) + return true; // not lying still yet + + if (thing->info->raisestate == S_NULL) + return true; // monster doesn't have a raise state + + maxdist = thing->info->radius + mobjinfo[MT_VILE].radius; + + if (doom_abs(thing->x - viletryx) > maxdist + || doom_abs(thing->y - viletryy) > maxdist) + return true; // not actually touching + + corpsehit = thing; + corpsehit->momx = corpsehit->momy = 0; + corpsehit->height <<= 2; + check = P_CheckPosition(corpsehit, corpsehit->x, corpsehit->y); + corpsehit->height >>= 2; + + if (!check) + return true; // doesn't fit here + + return false; // got one, so stop checking +} + + +// +// A_VileChase +// Check for ressurecting a body +// +void A_VileChase(mobj_t* actor) +{ + int xl; + int xh; + int yl; + int yh; + + int bx; + int by; + + mobjinfo_t* info; + mobj_t* temp; + + if (actor->movedir != DI_NODIR) + { + // check for corpses to raise + viletryx = + actor->x + actor->info->speed * xspeed[actor->movedir]; + viletryy = + actor->y + actor->info->speed * yspeed[actor->movedir]; + + xl = (viletryx - bmaporgx - MAXRADIUS * 2) >> MAPBLOCKSHIFT; + xh = (viletryx - bmaporgx + MAXRADIUS * 2) >> MAPBLOCKSHIFT; + yl = (viletryy - bmaporgy - MAXRADIUS * 2) >> MAPBLOCKSHIFT; + yh = (viletryy - bmaporgy + MAXRADIUS * 2) >> MAPBLOCKSHIFT; + + vileobj = actor; + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + // Call PIT_VileCheck to check + // whether object is a corpse + // that canbe raised. + if (!P_BlockThingsIterator(bx, by, PIT_VileCheck)) + { + // got one! + temp = actor->target; + actor->target = corpsehit; + A_FaceTarget(actor); + actor->target = temp; + + P_SetMobjState(actor, S_VILE_HEAL1); + S_StartSound(corpsehit, sfx_slop); + info = corpsehit->info; + + P_SetMobjState(corpsehit, info->raisestate); + corpsehit->height <<= 2; + corpsehit->flags = info->flags; + corpsehit->health = info->spawnhealth; + corpsehit->target = 0; + + return; + } + } + } + } + + // Return to normal attack. + A_Chase(actor); +} + + +// +// A_VileStart +// +void A_VileStart(mobj_t* actor) +{ + S_StartSound(actor, sfx_vilatk); +} + + +// +// A_Fire +// Keep fire in front of player unless out of sight +// +void A_StartFire(mobj_t* actor) +{ + S_StartSound(actor, sfx_flamst); + A_Fire(actor); +} + + +void A_FireCrackle(mobj_t* actor) +{ + S_StartSound(actor, sfx_flame); + A_Fire(actor); +} + + +void A_Fire(mobj_t* actor) +{ + mobj_t* dest; + unsigned an; + + dest = actor->tracer; + if (!dest) + return; + + // don't move it if the vile lost sight + if (!P_CheckSight(actor->target, dest)) + return; + + an = dest->angle >> ANGLETOFINESHIFT; + + P_UnsetThingPosition(actor); + actor->x = dest->x + FixedMul(24 * FRACUNIT, finecosine[an]); + actor->y = dest->y + FixedMul(24 * FRACUNIT, finesine[an]); + actor->z = dest->z; + P_SetThingPosition(actor); +} + + +// +// A_VileTarget +// Spawn the hellfire +// +void A_VileTarget(mobj_t* actor) +{ + mobj_t* fog; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + fog = P_SpawnMobj(actor->target->x, + actor->target->x, + actor->target->z, MT_FIRE); + + actor->tracer = fog; + fog->target = actor; + fog->tracer = actor->target; + A_Fire(fog); +} + + +// +// A_VileAttack +// +void A_VileAttack(mobj_t* actor) +{ + mobj_t* fire; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (!P_CheckSight(actor, actor->target)) + return; + + S_StartSound(actor, sfx_barexp); + P_DamageMobj(actor->target, actor, actor, 20); + actor->target->momz = 1000 * FRACUNIT / actor->target->info->mass; + + an = actor->angle >> ANGLETOFINESHIFT; + + fire = actor->tracer; + + if (!fire) + return; + + // move the fire between the vile and the player + fire->x = actor->target->x - FixedMul(24 * FRACUNIT, finecosine[an]); + fire->y = actor->target->y - FixedMul(24 * FRACUNIT, finesine[an]); + P_RadiusAttack(fire, actor, 70); +} + + +// +// Mancubus attack, +// firing three missiles (bruisers) +// in three different directions? +// Doesn't look like it. +// +void A_FatRaise(mobj_t* actor) +{ + A_FaceTarget(actor); + S_StartSound(actor, sfx_manatk); +} + + +void A_FatAttack1(mobj_t* actor) +{ + mobj_t* mo; + int an; + + A_FaceTarget(actor); + // Change direction to ... + actor->angle += FATSPREAD; + P_SpawnMissile(actor, actor->target, MT_FATSHOT); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle += FATSPREAD; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + + +void A_FatAttack2(mobj_t* actor) +{ + mobj_t* mo; + int an; + + A_FaceTarget(actor); + // Now here choose opposite deviation. + actor->angle -= FATSPREAD; + P_SpawnMissile(actor, actor->target, MT_FATSHOT); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle -= FATSPREAD * 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + + +void A_FatAttack3(mobj_t* actor) +{ + mobj_t* mo; + int an; + + A_FaceTarget(actor); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle -= FATSPREAD / 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle += FATSPREAD / 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + + +// +// SkullAttack +// Fly at the player like a missile. +// +void A_SkullAttack(mobj_t* actor) +{ + mobj_t* dest; + angle_t an; + int dist; + + if (!actor->target) + return; + + dest = actor->target; + actor->flags |= MF_SKULLFLY; + + S_StartSound(actor, actor->info->attacksound); + A_FaceTarget(actor); + an = actor->angle >> ANGLETOFINESHIFT; + actor->momx = FixedMul(SKULLSPEED, finecosine[an]); + actor->momy = FixedMul(SKULLSPEED, finesine[an]); + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + dist = dist / SKULLSPEED; + + if (dist < 1) + dist = 1; + actor->momz = (dest->z + (dest->height >> 1) - actor->z) / dist; +} + + +// +// A_PainShootSkull +// Spawn a lost soul and launch it at the target +// +void A_PainShootSkull(mobj_t* actor, angle_t angle) +{ + fixed_t x; + fixed_t y; + fixed_t z; + + mobj_t* newmobj; + angle_t an; + int prestep; + int count; + thinker_t* currentthinker; + + // count total number of skull currently on the level + count = 0; + + currentthinker = thinkercap.next; + while (currentthinker != &thinkercap) + { + if ((currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) + && ((mobj_t*)currentthinker)->type == MT_SKULL) + count++; + currentthinker = currentthinker->next; + } + + // 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 + an = angle >> ANGLETOFINESHIFT; + + prestep = + 4 * FRACUNIT + + 3 * (actor->info->radius + mobjinfo[MT_SKULL].radius) / 2; + + x = actor->x + FixedMul(prestep, finecosine[an]); + y = actor->y + FixedMul(prestep, finesine[an]); + z = actor->z + 8 * FRACUNIT; + + newmobj = P_SpawnMobj(x, y, z, MT_SKULL); + + // Check for movements. + if (!P_TryMove(newmobj, newmobj->x, newmobj->y)) + { + // kill it immediately + P_DamageMobj(newmobj, actor, actor, 10000); + return; + } + + newmobj->target = actor->target; + A_SkullAttack(newmobj); +} + + +// +// A_PainAttack +// Spawn a lost soul and launch it at the target +// +void A_PainAttack(mobj_t* actor) +{ + if (!actor->target) + return; + + A_FaceTarget(actor); + A_PainShootSkull(actor, actor->angle); +} + + +void A_PainDie(mobj_t* actor) +{ + A_Fall(actor); + A_PainShootSkull(actor, actor->angle + ANG90); + A_PainShootSkull(actor, actor->angle + ANG180); + A_PainShootSkull(actor, actor->angle + ANG270); +} + + +void A_Scream(mobj_t* actor) +{ + int sound; + + switch (actor->info->deathsound) + { + case 0: + return; + + case sfx_podth1: + case sfx_podth2: + case sfx_podth3: + sound = sfx_podth1 + P_Random() % 3; + break; + + case sfx_bgdth1: + case sfx_bgdth2: + sound = sfx_bgdth1 + P_Random() % 2; + break; + + default: + sound = actor->info->deathsound; + break; + } + + // Check for bosses. + if (actor->type == MT_SPIDER + || actor->type == MT_CYBORG) + { + // full volume + S_StartSound(0, sound); + } + else + S_StartSound(actor, sound); +} + + +void A_XScream(mobj_t* actor) +{ + S_StartSound(actor, sfx_slop); +} + + +void A_Pain(mobj_t* actor) +{ + if (actor->info->painsound) + S_StartSound(actor, actor->info->painsound); +} + + +void A_Fall(mobj_t* actor) +{ + // actor is on ground, it can be walked over + actor->flags &= ~MF_SOLID; + + // So change this if corpse objects + // are meant to be obstacles. +} + + +// +// A_Explode +// +void A_Explode(mobj_t* thingy) +{ + P_RadiusAttack(thingy, thingy->target, 128); +} + + +// +// A_BossDeath +// Possibly trigger special effects +// if on first boss level +// +void A_BossDeath(mobj_t* mo) +{ + thinker_t* th; + mobj_t* mo2; + line_t junk; + int i; + + if (gamemode == commercial) + { + if (gamemap != 7) + return; + + if ((mo->type != MT_FATSO) + && (mo->type != MT_BABY)) + return; + } + else + { + switch (gameepisode) + { + case 1: + if (gamemap != 8) + return; + + if (mo->type != MT_BRUISER) + return; + break; + + case 2: + if (gamemap != 8) + return; + + if (mo->type != MT_CYBORG) + return; + break; + + case 3: + if (gamemap != 8) + return; + + if (mo->type != MT_SPIDER) + return; + + break; + + case 4: + switch (gamemap) + { + case 6: + if (mo->type != MT_CYBORG) + return; + break; + + case 8: + if (mo->type != MT_SPIDER) + return; + break; + + default: + return; + break; + } + break; + + default: + if (gamemap != 8) + return; + break; + } + } + + // make sure there is a player alive for victory + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].health > 0) + break; + + if (i == MAXPLAYERS) + return; // no one left alive, so do not end game + + // scan the remaining thinkers to see + // if all bosses are dead + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t*)th; + if (mo2 != mo + && mo2->type == mo->type + && mo2->health > 0) + { + // other boss not dead + return; + } + } + + // victory! + if (gamemode == commercial) + { + if (gamemap == 7) + { + if (mo->type == MT_FATSO) + { + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + } + + if (mo->type == MT_BABY) + { + junk.tag = 667; + EV_DoFloor(&junk, raiseToTexture); + return; + } + } + } + else + { + switch (gameepisode) + { + case 1: + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + break; + + case 4: + switch (gamemap) + { + case 6: + junk.tag = 666; + EV_DoDoor(&junk, blazeOpen); + return; + break; + + case 8: + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + break; + } + } + } + + G_ExitLevel(); +} + + +void A_Hoof(mobj_t* mo) +{ + S_StartSound(mo, sfx_hoof); + A_Chase(mo); +} + + +void A_Metal(mobj_t* mo) +{ + S_StartSound(mo, sfx_metal); + A_Chase(mo); +} + + +void A_BabyMetal(mobj_t* mo) +{ + S_StartSound(mo, sfx_bspwlk); + A_Chase(mo); +} + + +void A_OpenShotgun2(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_dbopn); +} + + +void A_LoadShotgun2(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_dbload); +} + + +void A_CloseShotgun2(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_dbcls); + A_ReFire(player, psp); +} + + +void A_BrainAwake(mobj_t* mo) +{ + thinker_t* thinker; + mobj_t* m; + + // find all the target spots + numbraintargets = 0; + braintargeton = 0; + + thinker = thinkercap.next; + for (thinker = thinkercap.next; + thinker != &thinkercap; + thinker = thinker->next) + { + if (thinker->function.acp1 != (actionf_p1)P_MobjThinker) + continue; // not a mobj + + m = (mobj_t*)thinker; + + if (m->type == MT_BOSSTARGET) + { + braintargets[numbraintargets] = m; + numbraintargets++; + } + } + + S_StartSound(0, sfx_bossit); +} + + +void A_BrainPain(mobj_t* mo) +{ + S_StartSound(0, sfx_bospn); +} + + +void A_BrainScream(mobj_t* mo) +{ + int x; + int y; + int z; + mobj_t* th; + + for (x = mo->x - 196 * FRACUNIT; x < mo->x + 320 * FRACUNIT; x += FRACUNIT * 8) + { + y = mo->y - 320 * FRACUNIT; + z = 128 + P_Random() * 2 * FRACUNIT; + th = P_SpawnMobj(x, y, z, MT_ROCKET); + th->momz = P_Random() * 512; + + P_SetMobjState(th, S_BRAINEXPLODE1); + + th->tics -= P_Random() & 7; + if (th->tics < 1) + th->tics = 1; + } + + S_StartSound(0, sfx_bosdth); +} + + +void A_BrainExplode(mobj_t* mo) +{ + int x; + int y; + int z; + mobj_t* th; + + x = mo->x + (P_Random() - P_Random()) * 2048; + y = mo->y; + z = 128 + P_Random() * 2 * FRACUNIT; + th = P_SpawnMobj(x, y, z, MT_ROCKET); + th->momz = P_Random() * 512; + + P_SetMobjState(th, S_BRAINEXPLODE1); + + th->tics -= P_Random() & 7; + if (th->tics < 1) + th->tics = 1; +} + + +void A_BrainDie(mobj_t* mo) +{ + G_ExitLevel(); +} + + +void A_BrainSpit(mobj_t* mo) +{ + mobj_t* targ; + mobj_t* newmobj; + + static int easy = 0; + + easy ^= 1; + if (gameskill <= sk_easy && (!easy)) + return; + + // shoot a cube at current target + targ = braintargets[braintargeton]; + braintargeton = (braintargeton + 1) % numbraintargets; + + // spawn brain missile + newmobj = P_SpawnMissile(mo, targ, MT_SPAWNSHOT); + newmobj->target = targ; + newmobj->reactiontime = + ((targ->y - mo->y) / newmobj->momy) / newmobj->state->tics; + + S_StartSound(0, sfx_bospit); +} + + +// travelling cube sound +void A_SpawnSound(mobj_t* mo) +{ + S_StartSound(mo, sfx_boscub); + A_SpawnFly(mo); +} + + +void A_SpawnFly(mobj_t* mo) +{ + mobj_t* newmobj; + mobj_t* fog; + mobj_t* targ; + int r; + mobjtype_t type; + + if (--mo->reactiontime) + return; // still flying + + targ = mo->target; + + // First spawn teleport fog. + fog = P_SpawnMobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE); + S_StartSound(fog, sfx_telept); + + // Randomly select monster to spawn. + r = P_Random(); + + // Probability distribution (kind of :), + // decreasing likelihood. + if (r < 50) + type = MT_TROOP; + else if (r < 90) + type = MT_SERGEANT; + else if (r < 120) + type = MT_SHADOWS; + else if (r < 130) + type = MT_PAIN; + else if (r < 160) + type = MT_HEAD; + else if (r < 162) + type = MT_VILE; + else if (r < 172) + type = MT_UNDEAD; + else if (r < 192) + type = MT_BABY; + else if (r < 222) + type = MT_FATSO; + else if (r < 246) + type = MT_KNIGHT; + else + type = MT_BRUISER; + + newmobj = P_SpawnMobj(targ->x, targ->y, targ->z, type); + if (P_LookForPlayers(newmobj, true)) + P_SetMobjState(newmobj, newmobj->info->seestate); + + // telefrag anything in this spot + P_TeleportMove(newmobj, newmobj->x, newmobj->y); + + // remove self (i.e., cube). + P_RemoveMobj(mo); +} + + +void A_PlayerScream(mobj_t* mo) +{ + // Default death sound. + int sound = sfx_pldeth; + + if ((gamemode == commercial) + && (mo->health < -50)) + { + // IF THE PLAYER DIES + // LESS THAN -50% WITHOUT GIBBING + sound = sfx_pdiehi; + } + + S_StartSound(mo, sound); +} diff --git a/src/DOOM/p_floor.c b/src/DOOM/p_floor.c new file mode 100644 index 0000000..305c23c --- /dev/null +++ b/src/DOOM/p_floor.c @@ -0,0 +1,541 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Floor animation: raising stairs. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "z_zone.h" +#include "doomdef.h" +#include "p_local.h" +#include "s_sound.h" +#include "doomstat.h" // State. +#include "r_state.h" // State. +#include "sounds.h" // Data. + + +// +// FLOORS +// + +// +// Move a plane (floor or ceiling) and check for crushing +// +result_e T_MovePlane(sector_t* sector, + fixed_t speed, + fixed_t dest, + doom_boolean crush, + int floorOrCeiling, + int direction) +{ + doom_boolean flag; + fixed_t lastpos; + + switch (floorOrCeiling) + { + case 0: + // FLOOR + switch (direction) + { + case -1: + // DOWN + if (sector->floorheight - speed < dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = P_ChangeSector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + P_ChangeSector(sector, crush); + //return crushed; + } + return pastdest; + } + else + { + lastpos = sector->floorheight; + sector->floorheight -= speed; + flag = P_ChangeSector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + P_ChangeSector(sector, crush); + return crushed; + } + } + break; + + case 1: + // UP + if (sector->floorheight + speed > dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = P_ChangeSector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + P_ChangeSector(sector, crush); + //return crushed; + } + return pastdest; + } + else + { + // COULD GET CRUSHED + lastpos = sector->floorheight; + sector->floorheight += speed; + flag = P_ChangeSector(sector, crush); + if (flag == true) + { + if (crush == true) + return crushed; + sector->floorheight = lastpos; + P_ChangeSector(sector, crush); + return crushed; + } + } + break; + } + break; + + case 1: + // CEILING + switch (direction) + { + case -1: + // DOWN + if (sector->ceilingheight - speed < dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = P_ChangeSector(sector, crush); + + if (flag == true) + { + sector->ceilingheight = lastpos; + P_ChangeSector(sector, crush); + //return crushed; + } + return pastdest; + } + else + { + // COULD GET CRUSHED + lastpos = sector->ceilingheight; + sector->ceilingheight -= speed; + flag = P_ChangeSector(sector, crush); + + if (flag == true) + { + if (crush == true) + return crushed; + sector->ceilingheight = lastpos; + P_ChangeSector(sector, crush); + return crushed; + } + } + break; + + case 1: + // UP + if (sector->ceilingheight + speed > dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = P_ChangeSector(sector, crush); + if (flag == true) + { + sector->ceilingheight = lastpos; + P_ChangeSector(sector, crush); + //return crushed; + } + return pastdest; + } + else + { + lastpos = sector->ceilingheight; + sector->ceilingheight += speed; + flag = P_ChangeSector(sector, crush); + // UNUSED +#if 0 + if (flag == true) + { + sector->ceilingheight = lastpos; + P_ChangeSector(sector, crush); + return crushed; + } +#endif + } + break; + } + break; + } + + return ok; +} + + +// +// MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) +// +void T_MoveFloor(floormove_t* floor) +{ + result_e res; + + res = T_MovePlane(floor->sector, + floor->speed, + floor->floordestheight, + floor->crush, 0, floor->direction); + + if (!(leveltime & 7)) + S_StartSound((mobj_t*)&floor->sector->soundorg, + sfx_stnmov); + + if (res == pastdest) + { + floor->sector->specialdata = 0; + + if (floor->direction == 1) + { + switch (floor->type) + { + case donutRaise: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + default: + break; + } + } + else if (floor->direction == -1) + { + switch (floor->type) + { + case lowerAndChange: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + default: + break; + } + } + P_RemoveThinker(&floor->thinker); + + S_StartSound((mobj_t*)&floor->sector->soundorg, + sfx_pstop); + } +} + + +// +// HANDLE FLOOR TYPES +// +int EV_DoFloor(line_t* line, floor_e floortype) +{ + int secnum; + int rtn; + int i; + sector_t* sec; + floormove_t* floor; + + secnum = -1; + rtn = 0; + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (sec->specialdata) + continue; + + // new floor thinker + rtn = 1; + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + P_AddThinker(&floor->thinker); + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + floor->type = floortype; + floor->crush = false; + + switch (floortype) + { + case lowerFloor: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindHighestFloorSurrounding(sec); + break; + + case lowerFloorToLowest: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindLowestFloorSurrounding(sec); + break; + + case turboLower: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = + P_FindHighestFloorSurrounding(sec); + if (floor->floordestheight != sec->floorheight) + floor->floordestheight += 8 * FRACUNIT; + break; + + case raiseFloorCrush: + floor->crush = true; + case raiseFloor: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindLowestCeilingSurrounding(sec); + if (floor->floordestheight > sec->ceilingheight) + floor->floordestheight = sec->ceilingheight; + floor->floordestheight -= (8 * FRACUNIT) * + (floortype == raiseFloorCrush); + break; + + case raiseFloorTurbo: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = + P_FindNextHighestFloor(sec, sec->floorheight); + break; + + case raiseFloorToNearest: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindNextHighestFloor(sec, sec->floorheight); + break; + + case raiseFloor24: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + + 24 * FRACUNIT; + break; + case raiseFloor512: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + + 512 * FRACUNIT; + break; + + case raiseFloor24AndChange: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + + 24 * FRACUNIT; + sec->floorpic = line->frontsector->floorpic; + sec->special = line->frontsector->special; + break; + + case raiseToTexture: + { + int minsize = DOOM_MAXINT; + side_t* side; + + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + for (i = 0; i < sec->linecount; i++) + { + if (twoSided(secnum, i)) + { + side = getSide(secnum, i, 0); + if (side->bottomtexture >= 0) + if (textureheight[side->bottomtexture] < + minsize) + minsize = + textureheight[side->bottomtexture]; + side = getSide(secnum, i, 1); + if (side->bottomtexture >= 0) + if (textureheight[side->bottomtexture] < + minsize) + minsize = + textureheight[side->bottomtexture]; + } + } + floor->floordestheight = + floor->sector->floorheight + minsize; + } + break; + + case lowerAndChange: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindLowestFloorSurrounding(sec); + floor->texture = sec->floorpic; + + for (i = 0; i < sec->linecount; i++) + { + if (twoSided(secnum, i)) + { + if (getSide(secnum, i, 0)->sector - sectors == secnum) + { + sec = getSector(secnum, i, 1); + + if (sec->floorheight == floor->floordestheight) + { + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + break; + } + } + else + { + sec = getSector(secnum, i, 0); + + if (sec->floorheight == floor->floordestheight) + { + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + break; + } + } + } + } + default: + break; + } + } + + return rtn; +} + + +// +// BUILD A STAIRCASE! +// +int EV_BuildStairs(line_t* line, stair_e type) +{ + int secnum; + int height; + int i; + int newsecnum; + int texture; + int ok; + int rtn; + + sector_t* sec; + sector_t* tsec; + + floormove_t* floor; + + fixed_t stairsize; + fixed_t speed; + + secnum = -1; + rtn = 0; + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (sec->specialdata) + continue; + + // new floor thinker + rtn = 1; + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + P_AddThinker(&floor->thinker); + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + floor->direction = 1; + floor->sector = sec; + switch (type) + { + case build8: + speed = FLOORSPEED / 4; + stairsize = 8 * FRACUNIT; + break; + case turbo16: + speed = FLOORSPEED * 4; + stairsize = 16 * FRACUNIT; + break; + } + floor->speed = speed; + height = sec->floorheight + stairsize; + floor->floordestheight = height; + + texture = sec->floorpic; + + // Find next sector to raise + // 1. Find 2-sided line with same sector side[0] + // 2. Other side is the next sector to raise + do + { + ok = 0; + for (i = 0; i < sec->linecount; i++) + { + if (!((sec->lines[i])->flags & ML_TWOSIDED)) + continue; + + tsec = (sec->lines[i])->frontsector; + newsecnum = (int)(tsec - sectors); + + if (secnum != newsecnum) + continue; + + tsec = (sec->lines[i])->backsector; + newsecnum = (int)(tsec - sectors); + + if (tsec->floorpic != texture) + continue; + + height += stairsize; + + if (tsec->specialdata) + continue; + + sec = tsec; + secnum = newsecnum; + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + + P_AddThinker(&floor->thinker); + + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + floor->direction = 1; + floor->sector = sec; + floor->speed = speed; + floor->floordestheight = height; + ok = 1; + break; + } + } while (ok); + } + + return rtn; +} diff --git a/src/DOOM/p_inter.c b/src/DOOM/p_inter.c new file mode 100644 index 0000000..78ad78c --- /dev/null +++ b/src/DOOM/p_inter.c @@ -0,0 +1,924 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Handling interactions (i.e., collisions). +// +//----------------------------------------------------------------------------- + +#include "doom_config.h" + + + + +// Data. +#include "doomdef.h" +#include "dstrings.h" +#include "sounds.h" + +#include "doomstat.h" + +#include "m_random.h" +#include "i_system.h" + +#include "am_map.h" + +#include "p_local.h" + +#include "s_sound.h" + +#ifdef __GNUG__ +#pragma implementation "p_inter.h" +#endif +#include "p_inter.h" + + +#define BONUSADD 6 + + + + +// a weapon is found with two clip loads, +// a big item has five clip loads +int maxammo[NUMAMMO] = {200, 50, 300, 50}; +int clipammo[NUMAMMO] = {10, 4, 20, 1}; + + +// +// GET STUFF +// + +// +// P_GiveAmmo +// Num is the number of clip loads, +// not the individual count (0= 1/2 clip). +// Returns false if the ammo can't be picked up at all +// + +doom_boolean +P_GiveAmmo +( player_t* player, + ammotype_t ammo, + int num ) +{ + int oldammo; + + if (ammo == am_noammo) + return false; + + if (ammo < 0 || ammo > NUMAMMO) + { + //I_Error ("P_GiveAmmo: bad type %i", ammo); + + doom_strcpy(error_buf, "P_GiveAmmo: bad type "); + doom_concat(error_buf, doom_itoa(ammo, 10)); + I_Error(error_buf); + } + + if ( player->ammo[ammo] == player->maxammo[ammo] ) + return false; + + if (num) + num *= clipammo[ammo]; + else + num = clipammo[ammo]/2; + + if (gameskill == sk_baby + || gameskill == sk_nightmare) + { + // give double ammo in trainer mode, + // you'll need in nightmare + num <<= 1; + } + + + oldammo = player->ammo[ammo]; + player->ammo[ammo] += num; + + if (player->ammo[ammo] > player->maxammo[ammo]) + player->ammo[ammo] = player->maxammo[ammo]; + + // If non zero ammo, + // don't change up weapons, + // player was lower on purpose. + if (oldammo) + return true; + + // We were down to zero, + // so select a new weapon. + // Preferences are not user selectable. + switch (ammo) + { + case am_clip: + if (player->readyweapon == wp_fist) + { + if (player->weaponowned[wp_chaingun]) + player->pendingweapon = wp_chaingun; + else + player->pendingweapon = wp_pistol; + } + break; + + case am_shell: + if (player->readyweapon == wp_fist + || player->readyweapon == wp_pistol) + { + if (player->weaponowned[wp_shotgun]) + player->pendingweapon = wp_shotgun; + } + break; + + case am_cell: + if (player->readyweapon == wp_fist + || player->readyweapon == wp_pistol) + { + if (player->weaponowned[wp_plasma]) + player->pendingweapon = wp_plasma; + } + break; + + case am_misl: + if (player->readyweapon == wp_fist) + { + if (player->weaponowned[wp_missile]) + player->pendingweapon = wp_missile; + } + default: + break; + } + + return true; +} + + +// +// P_GiveWeapon +// The weapon name may have a MF_DROPPED flag ored in. +// +doom_boolean +P_GiveWeapon +( player_t* player, + weapontype_t weapon, + doom_boolean dropped ) +{ + doom_boolean gaveammo; + doom_boolean gaveweapon; + + if (netgame + && (deathmatch!=2) + && !dropped ) + { + // leave placed weapons forever on net games + if (player->weaponowned[weapon]) + return false; + + player->bonuscount += BONUSADD; + player->weaponowned[weapon] = true; + + if (deathmatch) + P_GiveAmmo (player, weaponinfo[weapon].ammo, 5); + else + P_GiveAmmo (player, weaponinfo[weapon].ammo, 2); + player->pendingweapon = weapon; + + if (player == &players[consoleplayer]) + S_StartSound (0, sfx_wpnup); + return false; + } + + if (weaponinfo[weapon].ammo != am_noammo) + { + // give one clip with a dropped weapon, + // two clips with a found weapon + if (dropped) + gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1); + else + gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2); + } + else + gaveammo = false; + + if (player->weaponowned[weapon]) + gaveweapon = false; + else + { + gaveweapon = true; + player->weaponowned[weapon] = true; + player->pendingweapon = weapon; + } + + return (gaveweapon || gaveammo); +} + + + +// +// P_GiveBody +// Returns false if the body isn't needed at all +// +doom_boolean +P_GiveBody +( player_t* player, + int num ) +{ + if (player->health >= MAXHEALTH) + return false; + + player->health += num; + if (player->health > MAXHEALTH) + player->health = MAXHEALTH; + player->mo->health = player->health; + + return true; +} + + + +// +// P_GiveArmor +// Returns false if the armor is worse +// than the current armor. +// +doom_boolean +P_GiveArmor +( player_t* player, + int armortype ) +{ + int hits; + + hits = armortype*100; + if (player->armorpoints >= hits) + return false; // don't pick up + + player->armortype = armortype; + player->armorpoints = hits; + + return true; +} + + + +// +// P_GiveCard +// +void +P_GiveCard +( player_t* player, + card_t card ) +{ + if (player->cards[card]) + return; + + player->bonuscount = BONUSADD; + player->cards[card] = 1; +} + + +// +// P_GivePower +// +doom_boolean +P_GivePower +( player_t* player, + int /*powertype_t*/ power ) +{ + if (power == pw_invulnerability) + { + player->powers[power] = INVULNTICS; + return true; + } + + if (power == pw_invisibility) + { + player->powers[power] = INVISTICS; + player->mo->flags |= MF_SHADOW; + return true; + } + + if (power == pw_infrared) + { + player->powers[power] = INFRATICS; + return true; + } + + if (power == pw_ironfeet) + { + player->powers[power] = IRONTICS; + return true; + } + + if (power == pw_strength) + { + P_GiveBody (player, 100); + player->powers[power] = 1; + return true; + } + + if (player->powers[power]) + return false; // already got it + + player->powers[power] = 1; + return true; +} + + + +// +// P_TouchSpecialThing +// +void +P_TouchSpecialThing +( mobj_t* special, + mobj_t* toucher ) +{ + player_t* player; + int i; + fixed_t delta; + int sound; + + delta = special->z - toucher->z; + + if (delta > toucher->height + || delta < -8*FRACUNIT) + { + // out of reach + return; + } + + + sound = sfx_itemup; + 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 SPR_ARM1: + if (!P_GiveArmor (player, 1)) + return; + player->message = GOTARMOR; + break; + + case SPR_ARM2: + if (!P_GiveArmor (player, 2)) + return; + player->message = GOTMEGA; + break; + + // bonus items + case SPR_BON1: + player->health++; // can go over 100% + if (player->health > 200) + player->health = 200; + player->mo->health = player->health; + player->message = GOTHTHBONUS; + break; + + case SPR_BON2: + player->armorpoints++; // can go over 100% + if (player->armorpoints > 200) + player->armorpoints = 200; + if (!player->armortype) + player->armortype = 1; + player->message = GOTARMBONUS; + break; + + case SPR_SOUL: + player->health += 100; + if (player->health > 200) + player->health = 200; + player->mo->health = player->health; + player->message = GOTSUPER; + sound = sfx_getpow; + break; + + case SPR_MEGA: + if (gamemode != commercial) + return; + player->health = 200; + player->mo->health = player->health; + P_GiveArmor (player,2); + player->message = GOTMSPHERE; + sound = sfx_getpow; + break; + + // cards + // leave cards for everyone + case SPR_BKEY: + if (!player->cards[it_bluecard]) + player->message = GOTBLUECARD; + P_GiveCard (player, it_bluecard); + if (!netgame) + break; + return; + + case SPR_YKEY: + if (!player->cards[it_yellowcard]) + player->message = GOTYELWCARD; + P_GiveCard (player, it_yellowcard); + if (!netgame) + break; + return; + + case SPR_RKEY: + if (!player->cards[it_redcard]) + player->message = GOTREDCARD; + P_GiveCard (player, it_redcard); + if (!netgame) + break; + return; + + case SPR_BSKU: + if (!player->cards[it_blueskull]) + player->message = GOTBLUESKUL; + P_GiveCard (player, it_blueskull); + if (!netgame) + break; + return; + + case SPR_YSKU: + if (!player->cards[it_yellowskull]) + player->message = GOTYELWSKUL; + P_GiveCard (player, it_yellowskull); + if (!netgame) + break; + return; + + case SPR_RSKU: + if (!player->cards[it_redskull]) + player->message = GOTREDSKULL; + P_GiveCard (player, it_redskull); + if (!netgame) + break; + return; + + // medikits, heals + case SPR_STIM: + if (!P_GiveBody (player, 10)) + return; + player->message = GOTSTIM; + break; + + case SPR_MEDI: + if (!P_GiveBody (player, 25)) + return; + + if (player->health < 25) + player->message = GOTMEDINEED; + else + player->message = GOTMEDIKIT; + break; + + + // power ups + case SPR_PINV: + if (!P_GivePower (player, pw_invulnerability)) + return; + player->message = GOTINVUL; + sound = sfx_getpow; + break; + + case SPR_PSTR: + if (!P_GivePower (player, pw_strength)) + return; + player->message = GOTBERSERK; + if (player->readyweapon != wp_fist) + player->pendingweapon = wp_fist; + sound = sfx_getpow; + break; + + case SPR_PINS: + if (!P_GivePower (player, pw_invisibility)) + return; + player->message = GOTINVIS; + sound = sfx_getpow; + break; + + case SPR_SUIT: + if (!P_GivePower (player, pw_ironfeet)) + return; + player->message = GOTSUIT; + sound = sfx_getpow; + break; + + case SPR_PMAP: + if (!P_GivePower (player, pw_allmap)) + return; + player->message = GOTMAP; + sound = sfx_getpow; + break; + + case SPR_PVIS: + if (!P_GivePower (player, pw_infrared)) + return; + player->message = GOTVISOR; + sound = sfx_getpow; + break; + + // ammo + case SPR_CLIP: + if (special->flags & MF_DROPPED) + { + if (!P_GiveAmmo (player,am_clip,0)) + return; + } + else + { + if (!P_GiveAmmo (player,am_clip,1)) + return; + } + player->message = GOTCLIP; + break; + + case SPR_AMMO: + if (!P_GiveAmmo (player, am_clip,5)) + return; + player->message = GOTCLIPBOX; + break; + + case SPR_ROCK: + if (!P_GiveAmmo (player, am_misl,1)) + return; + player->message = GOTROCKET; + break; + + case SPR_BROK: + if (!P_GiveAmmo (player, am_misl,5)) + return; + player->message = GOTROCKBOX; + break; + + case SPR_CELL: + if (!P_GiveAmmo (player, am_cell,1)) + return; + player->message = GOTCELL; + break; + + case SPR_CELP: + if (!P_GiveAmmo (player, am_cell,5)) + return; + player->message = GOTCELLBOX; + break; + + case SPR_SHEL: + if (!P_GiveAmmo (player, am_shell,1)) + return; + player->message = GOTSHELLS; + break; + + case SPR_SBOX: + if (!P_GiveAmmo (player, am_shell,5)) + return; + player->message = GOTSHELLBOX; + break; + + case SPR_BPAK: + if (!player->backpack) + { + for (i=0 ; imaxammo[i] *= 2; + player->backpack = true; + } + for (i=0 ; imessage = GOTBACKPACK; + break; + + // weapons + case SPR_BFUG: + if (!P_GiveWeapon (player, wp_bfg, false) ) + return; + player->message = GOTBFG9000; + sound = sfx_wpnup; + break; + + case SPR_MGUN: + if (!P_GiveWeapon (player, wp_chaingun, special->flags&MF_DROPPED) ) + return; + player->message = GOTCHAINGUN; + sound = sfx_wpnup; + break; + + case SPR_CSAW: + if (!P_GiveWeapon (player, wp_chainsaw, false) ) + return; + player->message = GOTCHAINSAW; + sound = sfx_wpnup; + break; + + case SPR_LAUN: + if (!P_GiveWeapon (player, wp_missile, false) ) + return; + player->message = GOTLAUNCHER; + sound = sfx_wpnup; + break; + + case SPR_PLAS: + if (!P_GiveWeapon (player, wp_plasma, false) ) + return; + player->message = GOTPLASMA; + sound = sfx_wpnup; + break; + + case SPR_SHOT: + if (!P_GiveWeapon (player, wp_shotgun, special->flags&MF_DROPPED ) ) + return; + player->message = GOTSHOTGUN; + sound = sfx_wpnup; + break; + + case SPR_SGN2: + if (!P_GiveWeapon (player, wp_supershotgun, special->flags&MF_DROPPED ) ) + return; + player->message = GOTSHOTGUN2; + sound = sfx_wpnup; + break; + + default: + I_Error ("P_SpecialThing: Unknown gettable thing"); + } + + if (special->flags & MF_COUNTITEM) + player->itemcount++; + P_RemoveMobj (special); + player->bonuscount += BONUSADD; + if (player == &players[consoleplayer]) + S_StartSound (0, sound); +} + + +// +// KillMobj +// +void +P_KillMobj +( mobj_t* source, + mobj_t* target ) +{ + mobjtype_t item; + mobj_t* mo; + + target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY); + + if (target->type != MT_SKULL) + target->flags &= ~MF_NOGRAVITY; + + target->flags |= MF_CORPSE|MF_DROPOFF; + target->height >>= 2; + + if (source && source->player) + { + // count for intermission + if (target->flags & MF_COUNTKILL) + source->player->killcount++; + + if (target->player) + source->player->frags[target->player-players]++; + } + else if (!netgame && (target->flags & MF_COUNTKILL) ) + { + // count all monster deaths, + // even those caused by other monsters + players[0].killcount++; + } + + if (target->player) + { + // count environment kills against you + if (!source) + target->player->frags[target->player-players]++; + + target->flags &= ~MF_SOLID; + target->player->playerstate = PST_DEAD; + P_DropWeapon (target->player); + + if (target->player == &players[consoleplayer] + && automapactive) + { + // don't die in auto map, + // switch view prior to dying + AM_Stop (); + } + + } + + if (target->health < -target->info->spawnhealth + && target->info->xdeathstate) + { + P_SetMobjState (target, target->info->xdeathstate); + } + else + P_SetMobjState (target, target->info->deathstate); + target->tics -= P_Random()&3; + + if (target->tics < 1) + target->tics = 1; + + // I_StartSound (&actor->r, actor->info->deathsound); + + + // Drop stuff. + // This determines the kind of object spawned + // during the death frame of a thing. + switch (target->type) + { + case MT_WOLFSS: + case MT_POSSESSED: + item = MT_CLIP; + break; + + case MT_SHOTGUY: + item = MT_SHOTGUN; + break; + + case MT_CHAINGUY: + item = MT_CHAINGUN; + break; + + default: + return; + } + + mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item); + mo->flags |= MF_DROPPED; // special versions of items +} + + + + +// +// P_DamageMobj +// Damages both enemies and players +// "inflictor" is the thing that caused the damage +// creature or missile, can be 0 (slime, etc) +// "source" is the thing to target after taking damage +// creature or 0 +// Source and inflictor are the same for melee attacks. +// Source can be 0 for slime, barrel explosions +// and other environmental stuff. +// +void +P_DamageMobj +( mobj_t* target, + mobj_t* inflictor, + mobj_t* source, + int damage ) +{ + unsigned ang; + int saved; + player_t* player; + fixed_t thrust; + int temp; + + if ( !(target->flags & MF_SHOOTABLE) ) + return; // shouldn't happen... + + if (target->health <= 0) + return; + + if ( target->flags & MF_SKULLFLY ) + { + target->momx = target->momy = target->momz = 0; + } + + player = target->player; + if (player && gameskill == sk_baby) + damage >>= 1; // take half damage in trainer mode + + + // Some close combat weapons should not + // inflict thrust and push the victim out of reach, + // thus kick away unless using the chainsaw. + if (inflictor + && !(target->flags & MF_NOCLIP) + && (!source + || !source->player + || source->player->readyweapon != wp_chainsaw)) + { + ang = R_PointToAngle2 ( inflictor->x, + inflictor->y, + target->x, + target->y); + + thrust = damage*(FRACUNIT>>3)*100/target->info->mass; + + // make fall forwards sometimes + if ( damage < 40 + && damage > target->health + && target->z - inflictor->z > 64*FRACUNIT + && (P_Random ()&1) ) + { + ang += ANG180; + thrust *= 4; + } + + ang >>= ANGLETOFINESHIFT; + target->momx += FixedMul (thrust, finecosine[ang]); + target->momy += FixedMul (thrust, finesine[ang]); + } + + // player specific + if (player) + { + // end of game hell hack + if (target->subsector->sector->special == 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&CF_GODMODE) + || player->powers[pw_invulnerability] ) ) + { + return; + } + + if (player->armortype) + { + 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; + } + player->health -= damage; // mirror mobj health here for Dave + if (player->health < 0) + player->health = 0; + + player->attacker = source; + player->damagecount += damage; // add damage after armor / invuln + + if (player->damagecount > 100) + player->damagecount = 100; // teleport stomp does 10k points... + + temp = damage < 100 ? damage : 100; + + if (player == &players[consoleplayer]) + I_Tactile (40,10,40+temp*2); + } + + // do the damage + target->health -= damage; + if (target->health <= 0) + { + P_KillMobj (source, target); + return; + } + + if ( (P_Random () < target->info->painchance) + && !(target->flags&MF_SKULLFLY) ) + { + target->flags |= MF_JUSTHIT; // fight back! + + P_SetMobjState (target, target->info->painstate); + } + + target->reactiontime = 0; // we're awake now... + + if ( (!target->threshold || target->type == MT_VILE) + && source && source != target + && source->type != MT_VILE) + { + // if not intent on another player, + // chase after this one + target->target = source; + target->threshold = BASETHRESHOLD; + if (target->state == &states[target->info->spawnstate] + && target->info->seestate != S_NULL) + P_SetMobjState (target, target->info->seestate); + } + +} + diff --git a/src/DOOM/p_inter.h b/src/DOOM/p_inter.h new file mode 100644 index 0000000..fc51e52 --- /dev/null +++ b/src/DOOM/p_inter.h @@ -0,0 +1,38 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// +// +//----------------------------------------------------------------------------- + +#ifndef __P_INTER__ +#define __P_INTER__ + + +#include "d_player.h" + + +doom_boolean P_GivePower(player_t*, int); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/p_lights.c b/src/DOOM/p_lights.c new file mode 100644 index 0000000..be9a4c3 --- /dev/null +++ b/src/DOOM/p_lights.c @@ -0,0 +1,333 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Handle Sector base lighting effects. +// Muzzle flash? +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "z_zone.h" +#include "m_random.h" +#include "doomdef.h" +#include "p_local.h" +#include "r_state.h" // State. + + +// +// FIRELIGHT FLICKER +// + +// +// T_FireFlicker +// +void T_FireFlicker(fireflicker_t* flick) +{ + int amount; + + if (--flick->count) + return; + + amount = (P_Random() & 3) * 16; + + if (flick->sector->lightlevel - amount < flick->minlight) + flick->sector->lightlevel = flick->minlight; + else + flick->sector->lightlevel = flick->maxlight - amount; + + flick->count = 4; +} + + +// +// P_SpawnFireFlicker +// +void P_SpawnFireFlicker(sector_t* sector) +{ + fireflicker_t* flick; + + // Note that we are resetting sector attributes. + // Nothing special about it during gameplay. + sector->special = 0; + + flick = Z_Malloc(sizeof(*flick), PU_LEVSPEC, 0); + + P_AddThinker(&flick->thinker); + + flick->thinker.function.acp1 = (actionf_p1)T_FireFlicker; + flick->sector = sector; + flick->maxlight = sector->lightlevel; + flick->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel) + 16; + flick->count = 4; +} + + +// +// BROKEN LIGHT FLASHING +// + +// +// T_LightFlash +// Do flashing lights. +// +void T_LightFlash(lightflash_t* flash) +{ + if (--flash->count) + return; + + if (flash->sector->lightlevel == flash->maxlight) + { + flash->sector->lightlevel = flash->minlight; + flash->count = (P_Random() & flash->mintime) + 1; + } + else + { + flash->sector->lightlevel = flash->maxlight; + flash->count = (P_Random() & flash->maxtime) + 1; + } +} + + +// +// P_SpawnLightFlash +// After the map has been loaded, scan each sector +// for specials that spawn thinkers +// +void P_SpawnLightFlash(sector_t* sector) +{ + lightflash_t* flash; + + // nothing special about it during gameplay + sector->special = 0; + + flash = Z_Malloc(sizeof(*flash), PU_LEVSPEC, 0); + + P_AddThinker(&flash->thinker); + + flash->thinker.function.acp1 = (actionf_p1)T_LightFlash; + flash->sector = sector; + flash->maxlight = sector->lightlevel; + + flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); + flash->maxtime = 64; + flash->mintime = 7; + flash->count = (P_Random() & flash->maxtime) + 1; +} + + +// +// STROBE LIGHT FLASHING +// + +// +// T_StrobeFlash +// +void T_StrobeFlash(strobe_t* flash) +{ + if (--flash->count) + return; + + if (flash->sector->lightlevel == flash->minlight) + { + flash->sector->lightlevel = flash->maxlight; + flash->count = flash->brighttime; + } + else + { + flash->sector->lightlevel = flash->minlight; + flash->count = flash->darktime; + } +} + + +// +// P_SpawnStrobeFlash +// After the map has been loaded, scan each sector +// for specials that spawn thinkers +// +void P_SpawnStrobeFlash(sector_t* sector, int fastOrSlow, int inSync) +{ + strobe_t* flash; + + flash = Z_Malloc(sizeof(*flash), PU_LEVSPEC, 0); + + P_AddThinker(&flash->thinker); + + flash->sector = sector; + flash->darktime = fastOrSlow; + flash->brighttime = STROBEBRIGHT; + flash->thinker.function.acp1 = (actionf_p1)T_StrobeFlash; + flash->maxlight = sector->lightlevel; + flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); + + if (flash->minlight == flash->maxlight) + flash->minlight = 0; + + // nothing special about it during gameplay + sector->special = 0; + + if (!inSync) + flash->count = (P_Random() & 7) + 1; + else + flash->count = 1; +} + + +// +// Start strobing lights (usually from a trigger) +// +void EV_StartLightStrobing(line_t* line) +{ + int secnum; + sector_t* sec; + + secnum = -1; + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) + continue; + + P_SpawnStrobeFlash(sec, SLOWDARK, 0); + } +} + + +// +// TURN LINE'S TAG LIGHTS OFF +// +void EV_TurnTagLightsOff(line_t* line) +{ + int i; + int j; + int min; + sector_t* sector; + sector_t* tsec; + line_t* templine; + + sector = sectors; + + for (j = 0; j < numsectors; j++, sector++) + { + if (sector->tag == line->tag) + { + min = sector->lightlevel; + for (i = 0; i < sector->linecount; i++) + { + templine = sector->lines[i]; + tsec = getNextSector(templine, sector); + if (!tsec) + continue; + if (tsec->lightlevel < min) + min = tsec->lightlevel; + } + sector->lightlevel = min; + } + } +} + + +// +// TURN LINE'S TAG LIGHTS ON +// +void EV_LightTurnOn(line_t* line, int bright) +{ + int i; + int j; + sector_t* sector; + sector_t* temp; + line_t* templine; + + sector = sectors; + + for (i = 0; i < numsectors; i++, sector++) + { + if (sector->tag == line->tag) + { + // bright = 0 means to search + // for highest light level + // surrounding sector + if (!bright) + { + for (j = 0; j < sector->linecount; j++) + { + templine = sector->lines[j]; + temp = getNextSector(templine, sector); + + if (!temp) + continue; + + if (temp->lightlevel > bright) + bright = temp->lightlevel; + } + } + sector->lightlevel = bright; + } + } +} + + +// +// Spawn glowing light +// +void T_Glow(glow_t* g) +{ + switch (g->direction) + { + case -1: + // DOWN + g->sector->lightlevel -= GLOWSPEED; + if (g->sector->lightlevel <= g->minlight) + { + g->sector->lightlevel += GLOWSPEED; + g->direction = 1; + } + break; + + case 1: + // UP + g->sector->lightlevel += GLOWSPEED; + if (g->sector->lightlevel >= g->maxlight) + { + g->sector->lightlevel -= GLOWSPEED; + g->direction = -1; + } + break; + } +} + + +void P_SpawnGlowingLight(sector_t* sector) +{ + glow_t* g; + + g = Z_Malloc(sizeof(*g), PU_LEVSPEC, 0); + + P_AddThinker(&g->thinker); + + g->sector = sector; + g->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); + g->maxlight = sector->lightlevel; + g->thinker.function.acp1 = (actionf_p1)T_Glow; + g->direction = -1; + + sector->special = 0; +} diff --git a/src/DOOM/p_local.h b/src/DOOM/p_local.h new file mode 100644 index 0000000..1bdbed3 --- /dev/null +++ b/src/DOOM/p_local.h @@ -0,0 +1,256 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Play functions, animation, global header. +// +//----------------------------------------------------------------------------- + +#ifndef __P_LOCAL__ +#define __P_LOCAL__ + + +#ifndef __R_LOCAL__ +#include "r_local.h" +#endif + + +#define FLOATSPEED (FRACUNIT*4) + +#define MAXHEALTH 100 +#define VIEWHEIGHT (41*FRACUNIT) + +// mapblocks are used to check movement +// against lines and things +#define MAPBLOCKUNITS 128 +#define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) +#define MAPBLOCKSHIFT (FRACBITS+7) +#define MAPBMASK (MAPBLOCKSIZE-1) +#define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) + + +// player radius for movement checking +#define PLAYERRADIUS 16*FRACUNIT + +// MAXRADIUS is for precalculated sector block boxes +// the spider demon is larger, +// but we do not have any moving sectors nearby +#define MAXRADIUS 32*FRACUNIT + +#define GRAVITY FRACUNIT +#define MAXMOVE (30*FRACUNIT) + +#define USERANGE (64*FRACUNIT) +#define MELEERANGE (64*FRACUNIT) +#define MISSILERANGE (32*64*FRACUNIT) + +// follow a player exlusively for 3 seconds +#define BASETHRESHOLD 100 + + +// +// P_TICK +// + +// both the head and tail of the thinker list +extern thinker_t thinkercap; + + +void P_InitThinkers(void); +void P_AddThinker(thinker_t* thinker); +void P_RemoveThinker(thinker_t* thinker); + + +// +// P_PSPR +// +void P_SetupPsprites(player_t* curplayer); +void P_MovePsprites(player_t* curplayer); +void P_DropWeapon(player_t* player); + + +// +// P_USER +// +void P_PlayerThink(player_t* player); + + +// +// P_MOBJ +// +#define ONFLOORZ DOOM_MININT +#define ONCEILINGZ DOOM_MAXINT + +// Time interval for item respawning. +#define ITEMQUESIZE 128 + + +extern mapthing_t itemrespawnque[ITEMQUESIZE]; +extern int itemrespawntime[ITEMQUESIZE]; +extern int iquehead; +extern int iquetail; + + +void P_RespawnSpecials(void); +mobj_t* P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); +void P_RemoveMobj(mobj_t* th); +doom_boolean P_SetMobjState(mobj_t* mobj, statenum_t state); +void P_MobjThinker(mobj_t* mobj); +void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z); +void P_SpawnBlood(fixed_t x, fixed_t y, fixed_t z, int damage); +mobj_t* P_SpawnMissile(mobj_t* source, mobj_t* dest, mobjtype_t type); +void P_SpawnPlayerMissile(mobj_t* source, mobjtype_t type); + + +// +// P_ENEMY +// +void P_NoiseAlert(mobj_t* target, mobj_t* emmiter); + + +// +// P_MAPUTL +// +typedef struct +{ + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; +} divline_t; + + +typedef struct +{ + fixed_t frac; // along trace line + doom_boolean isaline; + union + { + mobj_t* thing; + line_t* line; + } d; +} intercept_t; + + +#define MAXINTERCEPTS 128 +extern intercept_t intercepts[MAXINTERCEPTS]; +extern intercept_t* intercept_p; + + +typedef doom_boolean(*traverser_t) (intercept_t* in); + + +fixed_t P_AproxDistance(fixed_t dx, fixed_t dy); +int P_PointOnLineSide(fixed_t x, fixed_t y, line_t* line); +int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t* line); +void P_MakeDivline(line_t* li, divline_t* dl); +fixed_t P_InterceptVector(divline_t* v2, divline_t* v1); +int P_BoxOnLineSide(fixed_t* tmbox, line_t* ld); + + +extern fixed_t opentop; +extern fixed_t openbottom; +extern fixed_t openrange; +extern fixed_t lowfloor; + + +void P_LineOpening(line_t* linedef); + + +doom_boolean P_BlockLinesIterator(int x, int y, doom_boolean(*func)(line_t*)); +doom_boolean P_BlockThingsIterator(int x, int y, doom_boolean(*func)(mobj_t*)); + + +#define PT_ADDLINES 1 +#define PT_ADDTHINGS 2 +#define PT_EARLYOUT 4 + + +extern divline_t trace; + + +doom_boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, doom_boolean(*trav) (intercept_t*)); +void P_UnsetThingPosition(mobj_t* thing); +void P_SetThingPosition(mobj_t* thing); + + +// +// P_MAP +// + +// If "floatok" true, move would be ok +// if within "tmfloorz - tmceilingz". +extern doom_boolean floatok; +extern fixed_t tmfloorz; +extern fixed_t tmceilingz; + +extern line_t* ceilingline; + + +doom_boolean P_CheckPosition(mobj_t* thing, fixed_t x, fixed_t y); +doom_boolean P_TryMove(mobj_t* thing, fixed_t x, fixed_t y); +doom_boolean P_TeleportMove(mobj_t* thing, fixed_t x, fixed_t y); +void P_SlideMove(mobj_t* mo); +doom_boolean P_CheckSight(mobj_t* t1, mobj_t* t2); +void P_UseLines(player_t* player); +doom_boolean P_ChangeSector(sector_t* sector, doom_boolean crunch); + + +extern mobj_t* linetarget; // who got hit (or 0) + + +fixed_t P_AimLineAttack(mobj_t* t1, angle_t angle, fixed_t distance); +void P_LineAttack(mobj_t* t1, angle_t angle, fixed_t distance, fixed_t slope, int damage); +void P_RadiusAttack(mobj_t* spot, mobj_t* source, int damage); + + +// +// P_SETUP +// +extern byte* rejectmatrix; // for fast sight rejection +extern short* blockmaplump; // offsets in blockmap are from here +extern short* blockmap; +extern int bmapwidth; +extern int bmapheight; // in mapblocks +extern fixed_t bmaporgx; +extern fixed_t bmaporgy; // origin of block map +extern mobj_t** blocklinks; // for thing chains + + +// +// P_INTER +// +extern int maxammo[NUMAMMO]; +extern int clipammo[NUMAMMO]; + + +void P_TouchSpecialThing(mobj_t* special, mobj_t* toucher); +void P_DamageMobj(mobj_t* target, mobj_t* inflictor, mobj_t* source, int damage); + + +// +// P_SPEC +// +#include "p_spec.h" + + +#endif // __P_LOCAL__ + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/p_map.c b/src/DOOM/p_map.c new file mode 100644 index 0000000..8f50c85 --- /dev/null +++ b/src/DOOM/p_map.c @@ -0,0 +1,1291 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Movement, collision handling. +// Shooting and aiming. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "m_bbox.h" +#include "m_random.h" +#include "i_system.h" +#include "doomdef.h" +#include "p_local.h" +#include "s_sound.h" +#include "doomstat.h" // State. +#include "r_state.h" // State. +#include "sounds.h" // Data. + + +#define MAXSPECIALCROSS 8 + + +fixed_t tmbbox[4]; +mobj_t* tmthing; +int tmflags; +fixed_t tmx; +fixed_t tmy; + +// If "floatok" true, move would be ok +// if within "tmfloorz - tmceilingz". +doom_boolean floatok; + +fixed_t tmfloorz; +fixed_t tmceilingz; +fixed_t tmdropoffz; + +// keep track of the line that lowers the ceiling, +// so missiles don't explode against sky hack walls +line_t* ceilingline; + +// keep track of special lines as they are hit, +// but don't process them until the move is proven valid + +line_t* spechit[MAXSPECIALCROSS]; +int numspechit; + +mobj_t* linetarget; // who got hit (or 0) +mobj_t* shootthing; + +// Height if not aiming up or down +// ???: use slope for monsters? +fixed_t shootz; + +int la_damage; +fixed_t attackrange; + +fixed_t aimslope; +mobj_t* usething; +doom_boolean crushchange; +doom_boolean nofit; + + +// slopes to top and bottom of target +extern fixed_t topslope; +extern fixed_t bottomslope; + + +// +// TELEPORT MOVE +// + +// +// PIT_StompThing +// +doom_boolean PIT_StompThing(mobj_t* thing) +{ + fixed_t blockdist; + + if (!(thing->flags & MF_SHOOTABLE)) + return true; + + blockdist = thing->radius + tmthing->radius; + + if (doom_abs(thing->x - tmx) >= blockdist + || doom_abs(thing->y - tmy) >= blockdist) + { + // didn't hit it + return true; + } + + // don't clip against self + if (thing == tmthing) + return true; + + // monsters don't stomp things except on boss level + if (!tmthing->player && gamemap != 30) + return false; + + P_DamageMobj(thing, tmthing, tmthing, 10000); + + return true; +} + + +// +// P_TeleportMove +// +doom_boolean P_TeleportMove(mobj_t* thing, fixed_t x, fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + + subsector_t* newsubsec; + + // kill anything occupying the position + tmthing = thing; + tmflags = thing->flags; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector(x, y); + ceilingline = 0; + + // The base floor/ceiling is from the subsector + // that contains the point. + // Any contacted lines the step closer together + // will adjust them. + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + + validcount++; + numspechit = 0; + + // stomp on any things contacted + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + + for (bx = xl; bx <= xh; bx++) + for (by = yl; by <= yh; by++) + if (!P_BlockThingsIterator(bx, by, PIT_StompThing)) + return false; + + // the move is ok, + // so link the thing into its new position + P_UnsetThingPosition(thing); + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->x = x; + thing->y = y; + + P_SetThingPosition(thing); + + return true; +} + + +// +// MOVEMENT ITERATOR FUNCTIONS +// + +// +// PIT_CheckLine +// Adjusts tmfloorz and tmceilingz as lines are contacted +// +doom_boolean PIT_CheckLine(line_t* ld) +{ + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] + || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] + || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) + return true; + + if (P_BoxOnLineSide(tmbbox, ld) != -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 (!ld->backsector) + return false; // one sided line + + if (!(tmthing->flags & MF_MISSILE)) + { + if (ld->flags & ML_BLOCKING) + return false; // explicitly blocking everything + + if (!tmthing->player && ld->flags & ML_BLOCKMONSTERS) + return false; // block monsters only + } + + // set openrange, opentop, openbottom + P_LineOpening(ld); + + // adjust floor / ceiling heights + if (opentop < tmceilingz) + { + tmceilingz = opentop; + ceilingline = ld; + } + + if (openbottom > tmfloorz) + tmfloorz = openbottom; + + if (lowfloor < tmdropoffz) + tmdropoffz = lowfloor; + + // if contacted a special line, add it to the list + if (ld->special) + { + spechit[numspechit] = ld; + numspechit++; + } + + return true; +} + + +// +// PIT_CheckThing +// +doom_boolean PIT_CheckThing(mobj_t* thing) +{ + fixed_t blockdist; + doom_boolean solid; + int damage; + + if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE))) + return true; + + blockdist = thing->radius + tmthing->radius; + + if (doom_abs(thing->x - tmx) >= blockdist + || doom_abs(thing->y - tmy) >= blockdist) + { + // didn't hit it + return true; + } + + // don't clip against self + if (thing == tmthing) + return true; + + // check for skulls slamming into things + if (tmthing->flags & MF_SKULLFLY) + { + damage = ((P_Random() % 8) + 1) * tmthing->info->damage; + + P_DamageMobj(thing, tmthing, tmthing, damage); + + tmthing->flags &= ~MF_SKULLFLY; + tmthing->momx = tmthing->momy = tmthing->momz = 0; + + P_SetMobjState(tmthing, tmthing->info->spawnstate); + + return false; // stop moving + } + + + // missiles can hit other things + if (tmthing->flags & MF_MISSILE) + { + // see if it went over / under + if (tmthing->z > thing->z + thing->height) + return true; // overhead + if (tmthing->z + tmthing->height < thing->z) + return true; // underneath + + if (tmthing->target && ( + tmthing->target->type == thing->type || + (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER) || + (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT))) + { + // Don't hit same species as originator. + if (thing == tmthing->target) + return true; + + if (thing->type != MT_PLAYER) + { + // Explode, but do no damage. + // Let players missile other players. + return false; + } + } + + if (!(thing->flags & MF_SHOOTABLE)) + { + // didn't do any damage + return !(thing->flags & MF_SOLID); + } + + // damage / explode + damage = ((P_Random() % 8) + 1) * tmthing->info->damage; + P_DamageMobj(thing, tmthing, tmthing->target, damage); + + // don't traverse any more + return false; + } + + // check for special pickup + if (thing->flags & MF_SPECIAL) + { + solid = thing->flags & MF_SOLID; + if (tmflags & MF_PICKUP) + { + // can remove thing + P_TouchSpecialThing(thing, tmthing); + } + return !solid; + } + + return !(thing->flags & MF_SOLID); +} + + +// +// MOVEMENT CLIPPING +// + +// +// P_CheckPosition +// This is purely informative, nothing is modified +// (except things picked up). +// +// in: +// a mobj_t (can be valid or invalid) +// a position to be checked +// (doesn't need to be related to the mobj_t->x,y) +// +// during: +// special things are touched if MF_PICKUP +// early out on solid lines? +// +// out: +// newsubsec +// floorz +// ceilingz +// tmdropoffz +// the lowest point contacted +// (monsters won't move to a dropoff) +// speciallines[] +// numspeciallines +// +doom_boolean P_CheckPosition(mobj_t* thing, fixed_t x, fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + subsector_t* newsubsec; + + tmthing = thing; + tmflags = thing->flags; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector(x, y); + ceilingline = 0; + + // The base floor / ceiling is from the subsector + // that contains the point. + // Any contacted lines the step closer together + // will adjust them. + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + + validcount++; + numspechit = 0; + + if (tmflags & MF_NOCLIP) + return true; + + // Check things first, possibly picking things up. + // The bounding box is extended by MAXRADIUS + // because mobj_ts are grouped into mapblocks + // based on their origin point, and can overlap + // into adjacent blocks by up to MAXRADIUS units. + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + + for (bx = xl; bx <= xh; bx++) + for (by = yl; by <= yh; by++) + if (!P_BlockThingsIterator(bx, by, PIT_CheckThing)) + return false; + + // check lines + xl = (tmbbox[BOXLEFT] - bmaporgx) >> MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx) >> MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy) >> MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy) >> MAPBLOCKSHIFT; + + for (bx = xl; bx <= xh; bx++) + for (by = yl; by <= yh; by++) + if (!P_BlockLinesIterator(bx, by, PIT_CheckLine)) + return false; + + return true; +} + + +// +// P_TryMove +// Attempt to move to a new position, +// crossing special lines unless MF_TELEPORT is set. +// +doom_boolean P_TryMove(mobj_t* thing, fixed_t x, fixed_t y) +{ + fixed_t oldx; + fixed_t oldy; + int side; + int oldside; + line_t* ld; + + floatok = false; + if (!P_CheckPosition(thing, x, y)) + return false; // solid wall or thing + + if (!(thing->flags & MF_NOCLIP)) + { + if (tmceilingz - tmfloorz < thing->height) + return false; // doesn't fit + + floatok = true; + + if (!(thing->flags & MF_TELEPORT) + && tmceilingz - thing->z < thing->height) + return false; // mobj must lower itself to fit + + if (!(thing->flags & MF_TELEPORT) + && tmfloorz - thing->z > 24 * FRACUNIT) + return false; // too big a step up + + if (!(thing->flags & (MF_DROPOFF | MF_FLOAT)) + && tmfloorz - tmdropoffz > 24 * FRACUNIT) + return false; // don't stand over a dropoff + } + + // the move is ok, + // so link the thing into its new position + P_UnsetThingPosition(thing); + + oldx = thing->x; + oldy = thing->y; + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->x = x; + thing->y = y; + + P_SetThingPosition(thing); + + // if any special lines were hit, do the effect + if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP))) + { + while (numspechit--) + { + // see if the line was crossed + ld = spechit[numspechit]; + side = P_PointOnLineSide(thing->x, thing->y, ld); + oldside = P_PointOnLineSide(oldx, oldy, ld); + if (side != oldside) + { + if (ld->special) + P_CrossSpecialLine((int)(ld - lines), oldside, thing); + } + } + } + + return true; +} + + +// +// P_ThingHeightClip +// Takes a valid thing and adjusts the thing->floorz, +// thing->ceilingz, and possibly thing->z. +// This is called for all nearby monsters +// whenever a sector changes height. +// If the thing doesn't fit, +// the z will be set to the lowest value +// and false will be returned. +// +doom_boolean P_ThingHeightClip(mobj_t* thing) +{ + doom_boolean onfloor; + + onfloor = (thing->z == thing->floorz); + + P_CheckPosition(thing, thing->x, thing->y); + // what about stranding a monster partially off an edge? + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + + 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; +} + + +// +// SLIDE MOVE +// Allows the player to slide along any angled walls. +// +fixed_t bestslidefrac; +fixed_t secondslidefrac; + +line_t* bestslideline; +line_t* secondslideline; + +mobj_t* slidemo; + +fixed_t tmxmove; +fixed_t tmymove; + + +// +// P_HitSlideLine +// Adjusts the xmove / ymove +// so that the next move will slide along the wall. +// +void P_HitSlideLine(line_t* ld) +{ + int side; + + angle_t lineangle; + angle_t moveangle; + angle_t deltaangle; + + fixed_t movelen; + fixed_t newlen; + + if (ld->slopetype == ST_HORIZONTAL) + { + tmymove = 0; + return; + } + + if (ld->slopetype == ST_VERTICAL) + { + tmxmove = 0; + return; + } + + side = P_PointOnLineSide(slidemo->x, slidemo->y, ld); + + lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy); + + if (side == 1) + lineangle += ANG180; + + moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove); + deltaangle = moveangle - lineangle; + + if (deltaangle > ANG180) + deltaangle += ANG180; + + lineangle >>= ANGLETOFINESHIFT; + deltaangle >>= ANGLETOFINESHIFT; + + movelen = P_AproxDistance(tmxmove, tmymove); + newlen = FixedMul(movelen, finecosine[deltaangle]); + + tmxmove = FixedMul(newlen, finecosine[lineangle]); + tmymove = FixedMul(newlen, finesine[lineangle]); +} + + +// +// PTR_SlideTraverse +// +doom_boolean PTR_SlideTraverse(intercept_t* in) +{ + line_t* li; + + if (!in->isaline) + I_Error("Error: PTR_SlideTraverse: not a line?"); + + li = in->d.line; + + if (!(li->flags & ML_TWOSIDED)) + { + if (P_PointOnLineSide(slidemo->x, slidemo->y, li)) + { + // don't hit the back side + return true; + } + goto isblocking; + } + + // set openrange, opentop, openbottom + P_LineOpening(li); + + if (openrange < slidemo->height) + goto isblocking; // doesn't fit + + if (opentop - slidemo->z < slidemo->height) + goto isblocking; // mobj is too high + + if (openbottom - slidemo->z > 24 * FRACUNIT) + goto isblocking; // too big a step up + + // this line doesn't block movement + return true; + + // the line does block movement, + // see if it is closer than best so far +isblocking: + if (in->frac < bestslidefrac) + { + secondslidefrac = bestslidefrac; + secondslideline = bestslideline; + bestslidefrac = in->frac; + bestslideline = li; + } + + return false; // stop +} + + +// +// P_SlideMove +// 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. +// +void P_SlideMove(mobj_t* mo) +{ + fixed_t leadx; + fixed_t leady; + fixed_t trailx; + fixed_t traily; + fixed_t newx; + fixed_t newy; + int hitcount; + + slidemo = mo; + hitcount = 0; + +retry: + if (++hitcount == 3) + goto stairstep; // don't loop forever + + + // trace along the three leading corners + if (mo->momx > 0) + { + leadx = mo->x + mo->radius; + trailx = mo->x - mo->radius; + } + else + { + leadx = mo->x - mo->radius; + trailx = mo->x + mo->radius; + } + + if (mo->momy > 0) + { + leady = mo->y + mo->radius; + traily = mo->y - mo->radius; + } + else + { + leady = mo->y - mo->radius; + traily = mo->y + mo->radius; + } + + bestslidefrac = FRACUNIT + 1; + + P_PathTraverse(leadx, leady, leadx + mo->momx, leady + mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + P_PathTraverse(trailx, leady, trailx + mo->momx, leady + mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + P_PathTraverse(leadx, traily, leadx + mo->momx, traily + mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + + // move up to the wall + if (bestslidefrac == FRACUNIT + 1) + { + // the move most have hit the middle, so stairstep +stairstep: + if (!P_TryMove(mo, mo->x, mo->y + mo->momy)) + P_TryMove(mo, mo->x + mo->momx, mo->y); + return; + } + + // fudge a bit to make sure it doesn't hit + bestslidefrac -= 0x800; + if (bestslidefrac > 0) + { + newx = FixedMul(mo->momx, bestslidefrac); + newy = FixedMul(mo->momy, bestslidefrac); + + if (!P_TryMove(mo, mo->x + newx, mo->y + newy)) + goto stairstep; + } + + // Now continue along the wall. + // First calculate remainder. + bestslidefrac = FRACUNIT - (bestslidefrac + 0x800); + + if (bestslidefrac > FRACUNIT) + bestslidefrac = FRACUNIT; + + if (bestslidefrac <= 0) + return; + + tmxmove = FixedMul(mo->momx, bestslidefrac); + tmymove = FixedMul(mo->momy, bestslidefrac); + + P_HitSlideLine(bestslideline); // clip the moves + + mo->momx = tmxmove; + mo->momy = tmymove; + + if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove)) + { + goto retry; + } +} + + +// +// P_LineAttack +// + +// +// PTR_AimTraverse +// Sets linetaget and aimslope when a target is aimed at. +// +doom_boolean +PTR_AimTraverse(intercept_t* in) +{ + line_t* li; + mobj_t* th; + fixed_t slope; + fixed_t thingtopslope; + fixed_t thingbottomslope; + fixed_t dist; + + if (in->isaline) + { + li = in->d.line; + + if (!(li->flags & ML_TWOSIDED)) + return false; // stop + + // Crosses a two sided line. + // A two sided line will restrict + // the possible target ranges. + P_LineOpening(li); + + if (openbottom >= opentop) + return false; // stop + + dist = FixedMul(attackrange, in->frac); + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv(openbottom - shootz, dist); + if (slope > bottomslope) + bottomslope = slope; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv(opentop - shootz, dist); + if (slope < topslope) + topslope = slope; + } + + if (topslope <= bottomslope) + return false; // stop + + return true; // shot continues + } + + // shoot a thing + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + + if (!(th->flags & MF_SHOOTABLE)) + return true; // corpse or something + + // check angles to see if the thing can be aimed at + dist = FixedMul(attackrange, in->frac); + thingtopslope = FixedDiv(th->z + th->height - shootz, dist); + + if (thingtopslope < bottomslope) + return true; // shot over the thing + + thingbottomslope = FixedDiv(th->z - shootz, dist); + + if (thingbottomslope > topslope) + return true; // shot under the thing + + // this thing can be hit! + if (thingtopslope > topslope) + thingtopslope = topslope; + + if (thingbottomslope < bottomslope) + thingbottomslope = bottomslope; + + aimslope = (thingtopslope + thingbottomslope) / 2; + linetarget = th; + + return false; // don't go any farther +} + + +// +// PTR_ShootTraverse +// +doom_boolean PTR_ShootTraverse(intercept_t* in) +{ + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t frac; + + line_t* li; + + mobj_t* th; + + fixed_t slope; + fixed_t dist; + fixed_t thingtopslope; + fixed_t thingbottomslope; + + if (in->isaline) + { + li = in->d.line; + + if (li->special) + P_ShootSpecialLine(shootthing, li); + + if (!(li->flags & ML_TWOSIDED)) + goto hitline; + + // crosses a two sided line + P_LineOpening(li); + + dist = FixedMul(attackrange, in->frac); + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv(openbottom - shootz, dist); + if (slope > aimslope) + goto hitline; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv(opentop - shootz, dist); + if (slope < aimslope) + goto hitline; + } + + // shot continues + return true; + + + // hit line +hitline: + // position a bit closer + frac = in->frac - FixedDiv(4 * FRACUNIT, attackrange); + x = trace.x + FixedMul(trace.dx, frac); + y = trace.y + FixedMul(trace.dy, frac); + z = shootz + FixedMul(aimslope, FixedMul(frac, attackrange)); + + if (li->frontsector->ceilingpic == skyflatnum) + { + // don't shoot the sky! + if (z > li->frontsector->ceilingheight) + return false; + + // it's a sky hack wall + if (li->backsector && li->backsector->ceilingpic == skyflatnum) + return false; + } + + // Spawn bullet puffs. + P_SpawnPuff(x, y, z); + + // don't go any farther + return false; + } + + // shoot a thing + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + + if (!(th->flags & MF_SHOOTABLE)) + return true; // corpse or something + + // check angles to see if the thing can be aimed at + dist = FixedMul(attackrange, in->frac); + thingtopslope = FixedDiv(th->z + th->height - shootz, dist); + + if (thingtopslope < aimslope) + return true; // shot over the thing + + thingbottomslope = FixedDiv(th->z - shootz, dist); + + if (thingbottomslope > aimslope) + return true; // shot under the thing + + + // hit thing + // position a bit closer + frac = in->frac - FixedDiv(10 * FRACUNIT, attackrange); + + x = trace.x + FixedMul(trace.dx, frac); + y = trace.y + FixedMul(trace.dy, frac); + z = shootz + FixedMul(aimslope, FixedMul(frac, attackrange)); + + // Spawn bullet puffs or blod spots, + // depending on target type. + if (in->d.thing->flags & MF_NOBLOOD) + P_SpawnPuff(x, y, z); + else + P_SpawnBlood(x, y, z, la_damage); + + if (la_damage) + P_DamageMobj(th, shootthing, shootthing, la_damage); + + // don't go any farther + return false; +} + + +// +// P_AimLineAttack +// +fixed_t P_AimLineAttack(mobj_t* t1, angle_t angle, fixed_t distance) +{ + fixed_t x2; + fixed_t y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + + x2 = t1->x + (distance >> FRACBITS) * finecosine[angle]; + y2 = t1->y + (distance >> FRACBITS) * finesine[angle]; + shootz = t1->z + (t1->height >> 1) + 8 * FRACUNIT; + + // can't shoot outside view angles + topslope = 100 * FRACUNIT / 160; + bottomslope = -100 * FRACUNIT / 160; + + attackrange = distance; + linetarget = 0; + + P_PathTraverse(t1->x, t1->y, + x2, y2, + PT_ADDLINES | PT_ADDTHINGS, + PTR_AimTraverse); + + if (linetarget) + return aimslope; + + return 0; +} + + +// +// P_LineAttack +// If damage == 0, it is just a test trace +// that will leave linetarget set. +// +void P_LineAttack(mobj_t* t1, angle_t angle, fixed_t distance, fixed_t slope, int damage) +{ + fixed_t x2; + fixed_t y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + la_damage = damage; + x2 = t1->x + (distance >> FRACBITS) * finecosine[angle]; + y2 = t1->y + (distance >> FRACBITS) * finesine[angle]; + shootz = t1->z + (t1->height >> 1) + 8 * FRACUNIT; + attackrange = distance; + aimslope = slope; + + P_PathTraverse(t1->x, t1->y, + x2, y2, + PT_ADDLINES | PT_ADDTHINGS, + PTR_ShootTraverse); +} + + +// +// USE LINES +// +doom_boolean PTR_UseTraverse(intercept_t* in) +{ + int side; + + if (!in->d.line->special) + { + P_LineOpening(in->d.line); + if (openrange <= 0) + { + S_StartSound(usething, sfx_noway); + + // can't use through a wall + return false; + } + // not a special line, but keep checking + return true; + } + + side = 0; + if (P_PointOnLineSide(usething->x, usething->y, in->d.line) == 1) + side = 1; + + P_UseSpecialLine(usething, in->d.line, side); + + // can't use for than one special line in a row + return false; +} + + +// +// P_UseLines +// Looks for special lines in front of the player to activate. +// +void P_UseLines(player_t* player) +{ + int angle; + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + usething = player->mo; + + angle = player->mo->angle >> ANGLETOFINESHIFT; + + x1 = player->mo->x; + y1 = player->mo->y; + x2 = x1 + (USERANGE >> FRACBITS) * finecosine[angle]; + y2 = y1 + (USERANGE >> FRACBITS) * finesine[angle]; + + P_PathTraverse(x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse); +} + + +// +// RADIUS ATTACK +// +mobj_t* bombsource; +mobj_t* bombspot; +int bombdamage; + + +// +// PIT_RadiusAttack +// "bombsource" is the creature +// that caused the explosion at "bombspot". +// +doom_boolean PIT_RadiusAttack(mobj_t* thing) +{ + fixed_t dx; + fixed_t dy; + fixed_t dist; + + if (!(thing->flags & MF_SHOOTABLE)) + return true; + + // Boss spider and cyborg + // take no damage from concussion. + if (thing->type == MT_CYBORG + || thing->type == MT_SPIDER) + return true; + + dx = doom_abs(thing->x - bombspot->x); + dy = doom_abs(thing->y - bombspot->y); + + dist = dx > dy ? dx : dy; + dist = (dist - thing->radius) >> FRACBITS; + + if (dist < 0) + dist = 0; + + if (dist >= bombdamage) + return true; // out of range + + if (P_CheckSight(thing, bombspot)) + { + // must be in direct path + P_DamageMobj(thing, bombspot, bombsource, bombdamage - dist); + } + + return true; +} + + +// +// P_RadiusAttack +// Source is the creature that caused the explosion at spot. +// +void P_RadiusAttack(mobj_t* spot, mobj_t* source, int damage) +{ + int x; + int y; + + int xl; + int xh; + int yl; + int yh; + + fixed_t dist; + + dist = (damage + MAXRADIUS) << FRACBITS; + yh = (spot->y + dist - bmaporgy) >> MAPBLOCKSHIFT; + yl = (spot->y - dist - bmaporgy) >> MAPBLOCKSHIFT; + xh = (spot->x + dist - bmaporgx) >> MAPBLOCKSHIFT; + xl = (spot->x - dist - bmaporgx) >> MAPBLOCKSHIFT; + bombspot = spot; + bombsource = source; + bombdamage = damage; + + for (y = yl; y <= yh; y++) + for (x = xl; x <= xh; x++) + P_BlockThingsIterator(x, y, PIT_RadiusAttack); +} + + +// +// 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. +// + +// +// PIT_ChangeSector +// +doom_boolean PIT_ChangeSector(mobj_t* thing) +{ + mobj_t* mo; + + if (P_ThingHeightClip(thing)) + { + // keep checking + return true; + } + + + // crunch bodies to giblets + if (thing->health <= 0) + { + P_SetMobjState(thing, S_GIBS); + + thing->flags &= ~MF_SOLID; + thing->height = 0; + thing->radius = 0; + + // keep checking + return true; + } + + // crunch dropped items + if (thing->flags & MF_DROPPED) + { + P_RemoveMobj(thing); + + // keep checking + return true; + } + + if (!(thing->flags & MF_SHOOTABLE)) + { + // assume it is bloody gibs or something + return true; + } + + nofit = true; + + if (crushchange && !(leveltime & 3)) + { + P_DamageMobj(thing, 0, 0, 10); + + // spray blood in a random direction + mo = P_SpawnMobj(thing->x, + thing->y, + thing->z + thing->height / 2, MT_BLOOD); + + mo->momx = (P_Random() - P_Random()) << 12; + mo->momy = (P_Random() - P_Random()) << 12; + } + + // keep checking (crush other things) + return true; +} + + +// +// P_ChangeSector +// +doom_boolean P_ChangeSector(sector_t* sector, doom_boolean crunch) +{ + int x; + int y; + + nofit = false; + crushchange = crunch; + + // re-check heights for all things near the moving sector + for (x = sector->blockbox[BOXLEFT]; x <= sector->blockbox[BOXRIGHT]; x++) + for (y = sector->blockbox[BOXBOTTOM]; y <= sector->blockbox[BOXTOP]; y++) + P_BlockThingsIterator(x, y, PIT_ChangeSector); + + return nofit; +} diff --git a/src/DOOM/p_maputl.c b/src/DOOM/p_maputl.c new file mode 100644 index 0000000..c64ad40 --- /dev/null +++ b/src/DOOM/p_maputl.c @@ -0,0 +1,788 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Movement/collision utility functions, +// as used by function in p_map.c. +// BLOCKMAP Iterator functions, +// and some PIT_* functions to use for iteration. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "m_bbox.h" +#include "doomdef.h" +#include "p_local.h" +#include "r_state.h" // State. + + +fixed_t opentop; +fixed_t openbottom; +fixed_t openrange; +fixed_t lowfloor; +intercept_t intercepts[MAXINTERCEPTS]; +intercept_t* intercept_p; +divline_t trace; +doom_boolean earlyout; +int ptflags; + + +// +// P_AproxDistance +// Gives an estimation of distance (not exact) +// +fixed_t P_AproxDistance(fixed_t dx, fixed_t dy) +{ + dx = doom_abs(dx); + dy = doom_abs(dy); + if (dx < dy) + return dx + dy - (dx >> 1); + return dx + dy - (dy >> 1); +} + + +// +// P_PointOnLineSide +// Returns 0 or 1 +// +int P_PointOnLineSide(fixed_t x, fixed_t y, line_t* line) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!line->dx) + { + if (x <= line->v1->x) + return line->dy > 0; + + return line->dy < 0; + } + if (!line->dy) + { + if (y <= line->v1->y) + return line->dx < 0; + + return line->dx > 0; + } + + dx = (x - line->v1->x); + dy = (y - line->v1->y); + + left = FixedMul(line->dy >> FRACBITS, dx); + right = FixedMul(dy, line->dx >> FRACBITS); + + if (right < left) + return 0; // front side + return 1; // back side +} + + +// +// P_BoxOnLineSide +// Considers the line to be infinite +// Returns side 0 or 1, -1 if box crosses the line. +// +int P_BoxOnLineSide(fixed_t* tmbox, line_t* ld) +{ + int p1; + int p2; + + switch (ld->slopetype) + { + case ST_HORIZONTAL: + p1 = tmbox[BOXTOP] > ld->v1->y; + p2 = tmbox[BOXBOTTOM] > ld->v1->y; + if (ld->dx < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case ST_VERTICAL: + p1 = tmbox[BOXRIGHT] < ld->v1->x; + p2 = tmbox[BOXLEFT] < ld->v1->x; + if (ld->dy < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case ST_POSITIVE: + p1 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld); + p2 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); + break; + + case ST_NEGATIVE: + p1 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld); + p2 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); + break; + } + + if (p1 == p2) + return p1; + return -1; +} + + +// +// P_PointOnDivlineSide +// Returns 0 or 1. +// +int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t* line) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!line->dx) + { + if (x <= line->x) + return line->dy > 0; + + return line->dy < 0; + } + if (!line->dy) + { + if (y <= line->y) + return line->dx < 0; + + return line->dx > 0; + } + + dx = (x - line->x); + dy = (y - line->y); + + // try to quickly decide by looking at sign bits + if ((line->dy ^ line->dx ^ dx ^ dy) & 0x80000000) + { + if ((line->dy ^ dx) & 0x80000000) + return 1; // (left is negative) + return 0; + } + + left = FixedMul(line->dy >> 8, dx >> 8); + right = FixedMul(dy >> 8, line->dx >> 8); + + if (right < left) + return 0; // front side + return 1; // back side +} + + +// +// P_MakeDivline +// +void P_MakeDivline(line_t* li, divline_t* dl) +{ + dl->x = li->v1->x; + dl->y = li->v1->y; + dl->dx = li->dx; + dl->dy = li->dy; +} + + +// +// P_InterceptVector +// Returns the fractional intercept point +// along the first divline. +// This is only called by the addthings +// and addlines traversers. +// +fixed_t P_InterceptVector(divline_t* v2, divline_t* v1) +{ + fixed_t frac; + fixed_t num; + fixed_t den; + + den = FixedMul(v1->dy >> 8, v2->dx) - FixedMul(v1->dx >> 8, v2->dy); + + if (den == 0) + return 0; + // I_Error ("P_InterceptVector: parallel"); + + num = + FixedMul((v1->x - v2->x) >> 8, v1->dy) + + FixedMul((v2->y - v1->y) >> 8, v1->dx); + + frac = FixedDiv(num, den); + + return frac; +} + + +// +// P_LineOpening +// Sets opentop and openbottom to the window +// through a two sided line. +// OPTIMIZE: keep this precalculated +// +void P_LineOpening(line_t* linedef) +{ + sector_t* front; + sector_t* back; + + if (linedef->sidenum[1] == -1) + { + // single sided line + openrange = 0; + return; + } + + front = linedef->frontsector; + back = linedef->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; +} + + +// +// THING POSITION SETTING +// + +// +// P_UnsetThingPosition +// 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. +// +void P_UnsetThingPosition(mobj_t* thing) +{ + int blockx; + int blocky; + + if (!(thing->flags & MF_NOSECTOR)) + { + // inert things don't need to be in blockmap? + // unlink from subsector + if (thing->snext) + thing->snext->sprev = thing->sprev; + + if (thing->sprev) + thing->sprev->snext = thing->snext; + else + thing->subsector->sector->thinglist = thing->snext; + } + + if (!(thing->flags & MF_NOBLOCKMAP)) + { + // inert things don't need to be in blockmap + // unlink from block map + if (thing->bnext) + thing->bnext->bprev = thing->bprev; + + if (thing->bprev) + thing->bprev->bnext = thing->bnext; + else + { + blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT; + blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT; + + if (blockx >= 0 && blockx < bmapwidth + && blocky >= 0 && blocky < bmapheight) + { + blocklinks[blocky * bmapwidth + blockx] = thing->bnext; + } + } + } +} + + +// +// P_SetThingPosition +// Links a thing into both a block and a subsector +// based on it's x y. +// Sets thing->subsector properly +// +void P_SetThingPosition(mobj_t* thing) +{ + subsector_t* ss; + sector_t* sec; + int blockx; + int blocky; + mobj_t** link; + + + // link into subsector + ss = R_PointInSubsector(thing->x, thing->y); + thing->subsector = ss; + + if (!(thing->flags & MF_NOSECTOR)) + { + // invisible things don't go into the sector links + sec = ss->sector; + + thing->sprev = 0; + thing->snext = sec->thinglist; + + if (sec->thinglist) + sec->thinglist->sprev = thing; + + sec->thinglist = thing; + } + + // link into blockmap + if (!(thing->flags & MF_NOBLOCKMAP)) + { + // inert things don't need to be in blockmap + blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT; + blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT; + + if (blockx >= 0 + && blockx < bmapwidth + && blocky >= 0 + && blocky < bmapheight) + { + link = &blocklinks[blocky * bmapwidth + blockx]; + thing->bprev = 0; + thing->bnext = *link; + if (*link) + (*link)->bprev = thing; + + *link = thing; + } + else + { + // thing is off the map + thing->bnext = thing->bprev = 0; + } + } +} + + +// +// BLOCK MAP ITERATORS +// For each line/thing in the given mapblock, +// call the passed PIT_* function. +// If the function returns false, +// exit with false without checking anything else. +// + +// +// P_BlockLinesIterator +// The validcount flags are used to avoid checking lines +// that are marked in multiple mapblocks, +// so increment validcount before the first call +// to P_BlockLinesIterator, then make one or more calls +// to it. +// +doom_boolean P_BlockLinesIterator(int x, int y, doom_boolean(*func)(line_t*)) +{ + int offset; + short* list; + line_t* ld; + + if (x < 0 + || y < 0 + || x >= bmapwidth + || y >= bmapheight) + { + return true; + } + + offset = y * bmapwidth + x; + + offset = *(blockmap + offset); + + for (list = blockmaplump + offset; *list != -1; list++) + { + ld = &lines[*list]; + + if (ld->validcount == validcount) + continue; // line has already been checked + + ld->validcount = validcount; + + if (!func(ld)) + return false; + } + return true; // everything was checked +} + + +// +// P_BlockThingsIterator +// +doom_boolean P_BlockThingsIterator(int x, int y, doom_boolean(*func)(mobj_t*)) +{ + mobj_t* mobj; + + if (x < 0 + || y < 0 + || x >= bmapwidth + || y >= bmapheight) + { + return true; + } + + + for (mobj = blocklinks[y * bmapwidth + x]; + mobj; + mobj = mobj->bnext) + { + if (!func(mobj)) + return false; + } + return true; +} + + +// +// INTERCEPT ROUTINES +// + +// +// PIT_AddLineIntercepts. +// 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 sides of the trace. +// Returns true if earlyout and a solid line hit. +// +doom_boolean PIT_AddLineIntercepts(line_t* ld) +{ + int s1; + int s2; + fixed_t frac; + divline_t dl; + + // avoid precision problems with two routines + if (trace.dx > FRACUNIT * 16 + || trace.dy > FRACUNIT * 16 + || trace.dx < -FRACUNIT * 16 + || trace.dy < -FRACUNIT * 16) + { + s1 = P_PointOnDivlineSide(ld->v1->x, ld->v1->y, &trace); + s2 = P_PointOnDivlineSide(ld->v2->x, ld->v2->y, &trace); + } + else + { + s1 = P_PointOnLineSide(trace.x, trace.y, ld); + s2 = P_PointOnLineSide(trace.x + trace.dx, trace.y + trace.dy, ld); + } + + if (s1 == s2) + return true; // line isn't crossed + + // hit the line + P_MakeDivline(ld, &dl); + frac = P_InterceptVector(&trace, &dl); + + if (frac < 0) + return true; // behind source + + // try to early out the check + if (earlyout + && frac < FRACUNIT + && !ld->backsector) + { + return false; // stop checking + } + + intercept_p->frac = frac; + intercept_p->isaline = true; + intercept_p->d.line = ld; + intercept_p++; + + return true; // continue +} + + +// +// PIT_AddThingIntercepts +// +doom_boolean PIT_AddThingIntercepts(mobj_t* thing) +{ + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + int s1; + int s2; + + doom_boolean tracepositive; + + divline_t dl; + + fixed_t frac; + + tracepositive = (trace.dx ^ trace.dy) > 0; + + // 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; + } + + s1 = P_PointOnDivlineSide(x1, y1, &trace); + s2 = P_PointOnDivlineSide(x2, y2, &trace); + + if (s1 == s2) + return true; // line isn't crossed + + dl.x = x1; + dl.y = y1; + dl.dx = x2 - x1; + dl.dy = y2 - y1; + + frac = P_InterceptVector(&trace, &dl); + + if (frac < 0) + return true; // behind source + + intercept_p->frac = frac; + intercept_p->isaline = false; + intercept_p->d.thing = thing; + intercept_p++; + + return true; // keep going +} + + +// +// P_TraverseIntercepts +// Returns true if the traverser function returns true +// for all lines. +// +doom_boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac) +{ + int count; + fixed_t dist; + intercept_t* scan; + intercept_t* in; + + count = (int)(intercept_p - intercepts); + + in = 0; // shut up compiler warning + + while (count--) + { + dist = DOOM_MAXINT; + for (scan = intercepts; scan < intercept_p; scan++) + { + if (scan->frac < dist) + { + dist = scan->frac; + in = scan; + } + } + + if (dist > maxfrac) + return true; // checked everything in range + +#if 0 // UNUSED + { + // don't check these yet, there may be others inserted + in = scan = intercepts; + for (scan = intercepts; scan < intercept_p; scan++) + if (scan->frac > maxfrac) + *in++ = *scan; + intercept_p = in; + return false; + } +#endif + + if (!func(in)) + return false; // don't bother going farther + + in->frac = DOOM_MAXINT; + } + + return true; // everything was traversed +} + + +// +// P_PathTraverse +// 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. +// +doom_boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, doom_boolean(*trav) (intercept_t*)) +{ + fixed_t xt1; + fixed_t yt1; + fixed_t xt2; + fixed_t yt2; + + fixed_t xstep; + fixed_t ystep; + + fixed_t partial; + + fixed_t xintercept; + fixed_t yintercept; + + int mapx; + int mapy; + + int mapxstep; + int mapystep; + + int count; + + earlyout = flags & PT_EARLYOUT; + + validcount++; + intercept_p = intercepts; + + if (((x1 - bmaporgx) & (MAPBLOCKSIZE - 1)) == 0) + x1 += FRACUNIT; // don't side exactly on a line + + if (((y1 - bmaporgy) & (MAPBLOCKSIZE - 1)) == 0) + y1 += FRACUNIT; // don't side exactly on a line + + trace.x = x1; + trace.y = y1; + trace.dx = x2 - x1; + trace.dy = y2 - y1; + + x1 -= bmaporgx; + y1 -= bmaporgy; + xt1 = x1 >> MAPBLOCKSHIFT; + yt1 = y1 >> MAPBLOCKSHIFT; + + x2 -= bmaporgx; + y2 -= bmaporgy; + xt2 = x2 >> MAPBLOCKSHIFT; + yt2 = y2 >> MAPBLOCKSHIFT; + + if (xt2 > xt1) + { + mapxstep = 1; + partial = FRACUNIT - ((x1 >> MAPBTOFRAC) & (FRACUNIT - 1)); + ystep = FixedDiv(y2 - y1, doom_abs(x2 - x1)); + } + else if (xt2 < xt1) + { + mapxstep = -1; + partial = (x1 >> MAPBTOFRAC) & (FRACUNIT - 1); + ystep = FixedDiv(y2 - y1, doom_abs(x2 - x1)); + } + else + { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256 * FRACUNIT; + } + + yintercept = (y1 >> MAPBTOFRAC) + FixedMul(partial, ystep); + + + if (yt2 > yt1) + { + mapystep = 1; + partial = FRACUNIT - ((y1 >> MAPBTOFRAC) & (FRACUNIT - 1)); + xstep = FixedDiv(x2 - x1, doom_abs(y2 - y1)); + } + else if (yt2 < yt1) + { + mapystep = -1; + partial = (y1 >> MAPBTOFRAC) & (FRACUNIT - 1); + xstep = FixedDiv(x2 - x1, doom_abs(y2 - y1)); + } + else + { + mapystep = 0; + partial = FRACUNIT; + xstep = 256 * FRACUNIT; + } + xintercept = (x1 >> MAPBTOFRAC) + FixedMul(partial, xstep); + + // Step through map blocks. + // Count is present to prevent a round off error + // from skipping the break. + mapx = xt1; + mapy = yt1; + + for (count = 0; count < 64; count++) + { + if (flags & PT_ADDLINES) + { + if (!P_BlockLinesIterator(mapx, mapy, PIT_AddLineIntercepts)) + return false; // early out + } + + if (flags & PT_ADDTHINGS) + { + if (!P_BlockThingsIterator(mapx, mapy, PIT_AddThingIntercepts)) + return false; // early out + } + + if (mapx == xt2 + && mapy == yt2) + { + break; + } + + if ((yintercept >> FRACBITS) == mapy) + { + yintercept += ystep; + mapx += mapxstep; + } + else if ((xintercept >> FRACBITS) == mapx) + { + xintercept += xstep; + mapy += mapystep; + } + } + + // go through the sorted list + return P_TraverseIntercepts(trav, FRACUNIT); +} diff --git a/src/DOOM/p_mobj.c b/src/DOOM/p_mobj.c new file mode 100644 index 0000000..6536963 --- /dev/null +++ b/src/DOOM/p_mobj.c @@ -0,0 +1,961 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Moving object handling. Spawn functions. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "i_system.h" +#include "z_zone.h" +#include "m_random.h" +#include "doomdef.h" +#include "p_local.h" +#include "sounds.h" +#include "st_stuff.h" +#include "hu_stuff.h" +#include "s_sound.h" +#include "doomstat.h" + + +#define STOPSPEED 0x1000 +#define FRICTION 0xe800 + + +// +// P_SetMobjState +// Returns true if the mobj is still present. +// +mapthing_t itemrespawnque[ITEMQUESIZE]; +int itemrespawntime[ITEMQUESIZE]; +int iquehead; +int iquetail; + + +extern fixed_t attackrange; + + +void G_PlayerReborn(int player); +void P_SpawnMapThing(mapthing_t* mthing); + + +doom_boolean P_SetMobjState(mobj_t* mobj, statenum_t state) +{ + state_t* st; + + do + { + if (state == S_NULL) + { + mobj->state = (state_t*)S_NULL; + P_RemoveMobj(mobj); + return false; + } + + st = &states[state]; + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + + // Modified handling. + // Call action functions when the state is set + if (st->action.acp1) + st->action.acp1(mobj); + + state = st->nextstate; + } while (!mobj->tics); + + return true; +} + + +// +// P_ExplodeMissile +// +void P_ExplodeMissile(mobj_t* mo) +{ + mo->momx = mo->momy = mo->momz = 0; + + P_SetMobjState(mo, mobjinfo[mo->type].deathstate); + + mo->tics -= P_Random() & 3; + + if (mo->tics < 1) + mo->tics = 1; + + mo->flags &= ~MF_MISSILE; + + if (mo->info->deathsound) + S_StartSound(mo, mo->info->deathsound); +} + + +// +// P_XYMovement +// +void P_XYMovement(mobj_t* mo) +{ + fixed_t ptryx; + fixed_t ptryy; + player_t* player; + fixed_t xmove; + fixed_t ymove; + + if (!mo->momx && !mo->momy) + { + if (mo->flags & MF_SKULLFLY) + { + // the skull slammed into something + mo->flags &= ~MF_SKULLFLY; + mo->momx = mo->momy = mo->momz = 0; + + P_SetMobjState(mo, mo->info->spawnstate); + } + return; + } + + player = mo->player; + + if (mo->momx > MAXMOVE) + mo->momx = MAXMOVE; + else if (mo->momx < -MAXMOVE) + mo->momx = -MAXMOVE; + + if (mo->momy > MAXMOVE) + mo->momy = MAXMOVE; + else if (mo->momy < -MAXMOVE) + mo->momy = -MAXMOVE; + + xmove = mo->momx; + ymove = mo->momy; + + do + { + if (xmove > MAXMOVE / 2 || ymove > MAXMOVE / 2) + { + ptryx = mo->x + xmove / 2; + ptryy = mo->y + ymove / 2; + xmove >>= 1; + ymove >>= 1; + } + else + { + ptryx = mo->x + xmove; + ptryy = mo->y + ymove; + xmove = ymove = 0; + } + + if (!P_TryMove(mo, ptryx, ptryy)) + { + // blocked move + if (mo->player) + { // try to slide along it + P_SlideMove(mo); + } + else if (mo->flags & MF_MISSILE) + { + // explode a missile + if (ceilingline && + ceilingline->backsector && + ceilingline->backsector->ceilingpic == skyflatnum) + { + // Hack to prevent missiles exploding + // against the sky. + // Does not handle sky floors. + P_RemoveMobj(mo); + return; + } + P_ExplodeMissile(mo); + } + else + mo->momx = mo->momy = 0; + } + } while (xmove || ymove); + + // slow down + if (player && player->cheats & CF_NOMOMENTUM) + { + // debug option for no sliding at all + mo->momx = mo->momy = 0; + return; + } + + if (mo->flags & (MF_MISSILE | MF_SKULLFLY)) + return; // no friction for missiles ever + + if (mo->z > mo->floorz) + return; // no friction when airborne + + if (mo->flags & MF_CORPSE) + { + // do not stop sliding + // if halfway off a step with some momentum + if (mo->momx > FRACUNIT / 4 + || mo->momx < -FRACUNIT / 4 + || mo->momy > FRACUNIT / 4 + || mo->momy < -FRACUNIT / 4) + { + if (mo->floorz != mo->subsector->sector->floorheight) + return; + } + } + + if (mo->momx > -STOPSPEED + && mo->momx < STOPSPEED + && mo->momy > -STOPSPEED + && mo->momy < STOPSPEED + && (!player + || (player->cmd.forwardmove == 0 + && player->cmd.sidemove == 0))) + { + // if in a walking frame, stop moving + if (player && (unsigned)((player->mo->state - states) - S_PLAY_RUN1) < 4) + P_SetMobjState(player->mo, S_PLAY); + + mo->momx = 0; + mo->momy = 0; + } + else + { + mo->momx = FixedMul(mo->momx, FRICTION); + mo->momy = FixedMul(mo->momy, FRICTION); + } +} + + +// +// P_ZMovement +// +void P_ZMovement(mobj_t* mo) +{ + fixed_t dist; + fixed_t delta; + + // check for smooth step up + if (mo->player && mo->z < mo->floorz) + { + mo->player->viewheight -= mo->floorz - mo->z; + + mo->player->deltaviewheight + = (VIEWHEIGHT - mo->player->viewheight) >> 3; + } + + // adjust height + mo->z += mo->momz; + + if (mo->flags & MF_FLOAT + && mo->target) + { + // float down towards target if too close + if (!(mo->flags & MF_SKULLFLY) + && !(mo->flags & MF_INFLOAT)) + { + dist = P_AproxDistance(mo->x - mo->target->x, + mo->y - mo->target->y); + + delta = (mo->target->z + (mo->height >> 1)) - mo->z; + + if (delta < 0 && dist < -(delta * 3)) + mo->z -= FLOATSPEED; + else if (delta > 0 && dist < (delta * 3)) + mo->z += FLOATSPEED; + } + + } + + // clip movement + if (mo->z <= mo->floorz) + { + // hit the floor + + // Note (id): + // somebody left this after the setting momz to 0, + // kinda useless there. + if (mo->flags & MF_SKULLFLY) + { + // the skull slammed into something + mo->momz = -mo->momz; + } + + if (mo->momz < 0) + { + if (mo->player + && mo->momz < -GRAVITY * 8) + { + // Squat down. + // Decrease viewheight for a moment + // after hitting the ground (hard), + // and utter appropriate sound. + mo->player->deltaviewheight = mo->momz >> 3; + S_StartSound(mo, sfx_oof); + } + mo->momz = 0; + } + mo->z = mo->floorz; + + if ((mo->flags & MF_MISSILE) + && !(mo->flags & MF_NOCLIP)) + { + P_ExplodeMissile(mo); + return; + } + } + else if (!(mo->flags & MF_NOGRAVITY)) + { + if (mo->momz == 0) + mo->momz = -GRAVITY * 2; + else + mo->momz -= GRAVITY; + } + + if (mo->z + mo->height > mo->ceilingz) + { + // hit the ceiling + if (mo->momz > 0) + mo->momz = 0; + { + mo->z = mo->ceilingz - mo->height; + } + + if (mo->flags & MF_SKULLFLY) + { // the skull slammed into something + mo->momz = -mo->momz; + } + + if ((mo->flags & MF_MISSILE) + && !(mo->flags & MF_NOCLIP)) + { + P_ExplodeMissile(mo); + return; + } + } +} + + +// +// P_NightmareRespawn +// +void P_NightmareRespawn(mobj_t* mobj) +{ + fixed_t x; + fixed_t y; + fixed_t z; + subsector_t* ss; + mobj_t* mo; + mapthing_t* mthing; + + x = mobj->spawnpoint.x << FRACBITS; + y = mobj->spawnpoint.y << FRACBITS; + + // somthing is occupying it's position? + if (!P_CheckPosition(mobj, x, y)) + return; // no respwan + + // spawn a teleport fog at old spot + // because of removal of the body? + mo = P_SpawnMobj(mobj->x, + mobj->y, + mobj->subsector->sector->floorheight, MT_TFOG); + // initiate teleport sound + S_StartSound(mo, sfx_telept); + + // spawn a teleport fog at the new spot + ss = R_PointInSubsector(x, y); + + mo = P_SpawnMobj(x, y, ss->sector->floorheight, MT_TFOG); + + S_StartSound(mo, sfx_telept); + + // spawn the new monster + mthing = &mobj->spawnpoint; + + // spawn it + if (mobj->info->flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + // inherit attributes from deceased one + mo = P_SpawnMobj(x, y, z, mobj->type); + mo->spawnpoint = mobj->spawnpoint; + mo->angle = ANG45 * (mthing->angle / 45); + + if (mthing->options & MTF_AMBUSH) + mo->flags |= MF_AMBUSH; + + mo->reactiontime = 18; + + // remove the old monster, + P_RemoveMobj(mobj); +} + + +// +// P_MobjThinker +// +void P_MobjThinker(mobj_t* mobj) +{ + // momentum movement + if (mobj->momx + || mobj->momy + || (mobj->flags & MF_SKULLFLY)) + { + P_XYMovement(mobj); + + // FIXME: decent NOP/0/Nil function pointer please. + if (mobj->thinker.function.acv == (actionf_v)(-1)) + return; // mobj was removed + } + if ((mobj->z != mobj->floorz) + || mobj->momz) + { + P_ZMovement(mobj); + + // FIXME: decent NOP/0/Nil function pointer please. + if (mobj->thinker.function.acv == (actionf_v)(-1)) + return; // mobj was removed + } + + + // cycle through states, + // calling action functions at transitions + if (mobj->tics != -1) + { + mobj->tics--; + + // you can cycle through multiple states in a tic + if (!mobj->tics) + if (!P_SetMobjState(mobj, mobj->state->nextstate)) + return; // freed itself + } + else + { + // check for nightmare respawn + if (!(mobj->flags & MF_COUNTKILL)) + return; + + if (!respawnmonsters) + return; + + mobj->movecount++; + + if (mobj->movecount < 12 * 35) + return; + + if (leveltime & 31) + return; + + if (P_Random() > 4) + return; + + P_NightmareRespawn(mobj); + } +} + + +// +// P_SpawnMobj +// +mobj_t* P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) +{ + mobj_t* mobj; + state_t* st; + mobjinfo_t* info; + + mobj = Z_Malloc(sizeof(*mobj), PU_LEVEL, 0); + doom_memset(mobj, 0, sizeof(*mobj)); + info = &mobjinfo[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 (gameskill != sk_nightmare) + mobj->reactiontime = info->reactiontime; + + mobj->lastlook = P_Random() % MAXPLAYERS; + // do not set the state with P_SetMobjState, + // because action routines can not be called yet + st = &states[info->spawnstate]; + + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + + // set subsector and/or block links + P_SetThingPosition(mobj); + + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + if (z == ONFLOORZ) + mobj->z = mobj->floorz; + else if (z == ONCEILINGZ) + mobj->z = mobj->ceilingz - mobj->info->height; + else + mobj->z = z; + + mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; + + P_AddThinker(&mobj->thinker); + + return mobj; +} + + +// +// P_RemoveMobj +// +void P_RemoveMobj(mobj_t* mobj) +{ + if ((mobj->flags & MF_SPECIAL) + && !(mobj->flags & MF_DROPPED) + && (mobj->type != MT_INV) + && (mobj->type != MT_INS)) + { + itemrespawnque[iquehead] = mobj->spawnpoint; + itemrespawntime[iquehead] = leveltime; + iquehead = (iquehead + 1) & (ITEMQUESIZE - 1); + + // lose one off the end? + if (iquehead == iquetail) + iquetail = (iquetail + 1) & (ITEMQUESIZE - 1); + } + + // unlink from sector and block lists + P_UnsetThingPosition(mobj); + + // stop any playing sound + S_StopSound(mobj); + + // free block + P_RemoveThinker((thinker_t*)mobj); +} + + +// +// P_RespawnSpecials +// +void P_RespawnSpecials(void) +{ + fixed_t x; + fixed_t y; + fixed_t z; + + subsector_t* ss; + mobj_t* mo; + mapthing_t* mthing; + + int i; + + // only respawn items in deathmatch + if (deathmatch != 2) + return; // + + // nothing left to respawn? + if (iquehead == iquetail) + return; + + // wait at least 30 seconds + if (leveltime - itemrespawntime[iquetail] < 30 * 35) + return; + + mthing = &itemrespawnque[iquetail]; + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + // spawn a teleport fog at the new spot + ss = R_PointInSubsector(x, y); + mo = P_SpawnMobj(x, y, ss->sector->floorheight, MT_IFOG); + S_StartSound(mo, sfx_itmbk); + + // find which type to spawn + for (i = 0; i < NUMMOBJTYPES; i++) + { + if (mthing->type == mobjinfo[i].doomednum) + break; + } + + // spawn it + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mo = P_SpawnMobj(x, y, z, i); + mo->spawnpoint = *mthing; + mo->angle = ANG45 * (mthing->angle / 45); + + // pull it from the que + iquetail = (iquetail + 1) & (ITEMQUESIZE - 1); +} + + +// +// P_SpawnPlayer +// Called when a player is spawned on the level. +// Most of the player structure stays unchanged +// between levels. +// +void P_SpawnPlayer(mapthing_t* mthing) +{ + player_t* p; + fixed_t x; + fixed_t y; + fixed_t z; + + mobj_t* mobj; + + int i; + + // not playing? + if (!playeringame[mthing->type - 1]) + return; + + p = &players[mthing->type - 1]; + + if (p->playerstate == PST_REBORN) + G_PlayerReborn(mthing->type - 1); + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + z = ONFLOORZ; + mobj = P_SpawnMobj(x, y, z, MT_PLAYER); + + // set color translations for player sprites + if (mthing->type > 1) + mobj->flags |= (mthing->type - 1) << MF_TRANSSHIFT; + + mobj->angle = ANG45 * (mthing->angle / 45); + mobj->player = p; + mobj->health = p->health; + + p->mo = mobj; + p->playerstate = PST_LIVE; + p->refire = 0; + p->message = 0; + p->damagecount = 0; + p->bonuscount = 0; + p->extralight = 0; + p->fixedcolormap = 0; + p->viewheight = VIEWHEIGHT; + + // setup gun psprite + P_SetupPsprites(p); + + // give all cards in death match mode + if (deathmatch) + for (i = 0; i < NUMCARDS; i++) + p->cards[i] = true; + + if (mthing->type - 1 == consoleplayer) + { + // wake up the status bar + ST_Start(); + // wake up the heads up text + HU_Start(); + } +} + + +// +// P_SpawnMapThing +// The fields of the mapthing should +// already be in host byte order. +// +void P_SpawnMapThing(mapthing_t* mthing) +{ + int i; + int bit; + mobj_t* mobj; + fixed_t x; + fixed_t y; + fixed_t z; + + // count deathmatch start positions + if (mthing->type == 11) + { + if (deathmatch_p < &deathmatchstarts[10]) + { + doom_memcpy(deathmatch_p, mthing, sizeof(*mthing)); + deathmatch_p++; + } + return; + } + + // check for players specially + if (mthing->type <= 4) + { + // save spots for respawning in network games + playerstarts[mthing->type - 1] = *mthing; + if (!deathmatch) + P_SpawnPlayer(mthing); + + return; + } + + // check for apropriate skill level + if (!netgame && (mthing->options & 16)) + return; + + if (gameskill == sk_baby) + bit = 1; + else if (gameskill == sk_nightmare) + bit = 4; + else + bit = 1 << (gameskill - 1); + + if (!(mthing->options & bit)) + return; + + // find which type to spawn + for (i = 0; i < NUMMOBJTYPES; i++) + if (mthing->type == mobjinfo[i].doomednum) + break; + + if (i == NUMMOBJTYPES) + { + //I_Error("Error: P_SpawnMapThing: Unknown type %i at (%i, %i)", + // mthing->type, + // mthing->x, mthing->y); + + + doom_strcpy(error_buf, "Error: P_SpawnMapThing: Unknown type "); + doom_concat(error_buf, doom_itoa(mthing->type, 10)); + doom_concat(error_buf, " at ("); + doom_concat(error_buf, doom_itoa(mthing->x, 10)); + doom_concat(error_buf, ", "); + doom_concat(error_buf, doom_itoa(mthing->y, 10)); + doom_concat(error_buf, ")"); + I_Error(error_buf); + } + + // don't spawn keycards and players in deathmatch + if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) + return; + + // don't spawn any monsters if -nomonsters + if (nomonsters + && (i == MT_SKULL + || (mobjinfo[i].flags & MF_COUNTKILL))) + { + return; + } + + // spawn it + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mobj = P_SpawnMobj(x, y, z, i); + mobj->spawnpoint = *mthing; + + if (mobj->tics > 0) + mobj->tics = 1 + (P_Random() % mobj->tics); + if (mobj->flags & MF_COUNTKILL) + totalkills++; + if (mobj->flags & MF_COUNTITEM) + totalitems++; + + mobj->angle = ANG45 * (mthing->angle / 45); + if (mthing->options & MTF_AMBUSH) + mobj->flags |= MF_AMBUSH; +} + + +// +// GAME SPAWN FUNCTIONS +// + +// +// P_SpawnPuff +// +void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z) +{ + mobj_t* th; + + z += ((P_Random() - P_Random()) << 10); + + th = P_SpawnMobj(x, y, z, MT_PUFF); + th->momz = FRACUNIT; + th->tics -= P_Random() & 3; + + if (th->tics < 1) + th->tics = 1; + + // don't make punches spark on the wall + if (attackrange == MELEERANGE) + P_SetMobjState(th, S_PUFF3); +} + + +// +// P_SpawnBlood +// +void P_SpawnBlood(fixed_t x, fixed_t y, fixed_t z, int damage) +{ + mobj_t* th; + + z += ((P_Random() - P_Random()) << 10); + th = P_SpawnMobj(x, y, z, MT_BLOOD); + th->momz = FRACUNIT * 2; + th->tics -= P_Random() & 3; + + if (th->tics < 1) + th->tics = 1; + + if (damage <= 12 && damage >= 9) + P_SetMobjState(th, S_BLOOD2); + else if (damage < 9) + P_SetMobjState(th, S_BLOOD3); +} + + +// +// P_CheckMissileSpawn +// Moves the missile forward a bit +// and possibly explodes it right there. +// +void P_CheckMissileSpawn(mobj_t* th) +{ + th->tics -= P_Random() & 3; + if (th->tics < 1) + th->tics = 1; + + // move a little forward so an angle can + // be computed if it immediately explodes + th->x += (th->momx >> 1); + th->y += (th->momy >> 1); + th->z += (th->momz >> 1); + + if (!P_TryMove(th, th->x, th->y)) + P_ExplodeMissile(th); +} + + +// +// P_SpawnMissile +// +mobj_t* P_SpawnMissile(mobj_t* source, mobj_t* dest, mobjtype_t type) +{ + mobj_t* th; + angle_t an; + int dist; + + th = P_SpawnMobj(source->x, + source->y, + source->z + 4 * 8 * FRACUNIT, type); + + if (th->info->seesound) + S_StartSound(th, th->info->seesound); + + th->target = source; // where it came from + an = R_PointToAngle2(source->x, source->y, dest->x, dest->y); + + // fuzzy player + if (dest->flags & MF_SHADOW) + an += (P_Random() - P_Random()) << 20; + + th->angle = an; + an >>= ANGLETOFINESHIFT; + th->momx = FixedMul(th->info->speed, finecosine[an]); + th->momy = FixedMul(th->info->speed, finesine[an]); + + dist = P_AproxDistance(dest->x - source->x, dest->y - source->y); + dist = dist / th->info->speed; + + if (dist < 1) + dist = 1; + + th->momz = (dest->z - source->z) / dist; + P_CheckMissileSpawn(th); + + return th; +} + + +// +// P_SpawnPlayerMissile +// Tries to aim at a nearby monster +// +void P_SpawnPlayerMissile(mobj_t* source, mobjtype_t type) +{ + mobj_t* th; + angle_t an; + + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t slope; + + // see which target is to be aimed at + an = source->angle; + slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an += 1 << 26; + slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an -= 2 << 26; + slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); + } + + if (!linetarget) + { + an = source->angle; + slope = 0; + } + } + + x = source->x; + y = source->y; + z = source->z + 4 * 8 * FRACUNIT; + + th = P_SpawnMobj(x, y, z, type); + + if (th->info->seesound) + S_StartSound(th, th->info->seesound); + + th->target = source; + th->angle = an; + th->momx = FixedMul(th->info->speed, + finecosine[an >> ANGLETOFINESHIFT]); + th->momy = FixedMul(th->info->speed, + finesine[an >> ANGLETOFINESHIFT]); + th->momz = FixedMul(th->info->speed, slope); + + P_CheckMissileSpawn(th); +} diff --git a/src/DOOM/p_mobj.h b/src/DOOM/p_mobj.h new file mode 100644 index 0000000..71825c4 --- /dev/null +++ b/src/DOOM/p_mobj.h @@ -0,0 +1,286 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Map Objects, MObj, definition and handling. +// +//----------------------------------------------------------------------------- + +#ifndef __P_MOBJ__ +#define __P_MOBJ__ + + +// Basics. +#include "tables.h" +#include "m_fixed.h" + +// We need the thinker_t stuff. +#include "d_think.h" + +// We need the WAD data structure for Map things, +// from the THINGS lump. +#include "doomdata.h" + +// States are tied to finite states are +// tied to animation frames. +// Needs precompiled tables/data structures. +#include "info.h" + + +// +// 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? +// + +// +// Misc. mobj flags +// +typedef enum +{ + // Call P_SpecialThing when touched. + MF_SPECIAL = 1, + // Blocks. + MF_SOLID = 2, + // Can be hit. + MF_SHOOTABLE = 4, + // Don't use the sector links (invisible but touchable). + MF_NOSECTOR = 8, + // Don't use the blocklinks (inert but displayable) + MF_NOBLOCKMAP = 16, + + // Not to be activated by sound, deaf monster. + MF_AMBUSH = 32, + // Will try to attack right back. + MF_JUSTHIT = 64, + // Will take at least one step before attacking. + MF_JUSTATTACKED = 128, + // On level spawning (initial position), + // hang from ceiling instead of stand on floor. + MF_SPAWNCEILING = 256, + // Don't apply gravity (every tic), + // that is, object will float, keeping current height + // or changing it actively. + MF_NOGRAVITY = 512, + + // Movement flags. + // This allows jumps from high places. + MF_DROPOFF = 0x400, + // For players, will pick up items. + MF_PICKUP = 0x800, + // Player cheat. ??? + MF_NOCLIP = 0x1000, + // Player: keep info about sliding along walls. + MF_SLIDE = 0x2000, + // Allow moves to any height, no gravity. + // For active floaters, e.g. cacodemons, pain elementals. + MF_FLOAT = 0x4000, + // Don't cross lines + // ??? or look at heights on teleport. + MF_TELEPORT = 0x8000, + // Don't hit same species, explode on block. + // Player missiles as well as fireballs of various kinds. + MF_MISSILE = 0x10000, + // Dropped by a demon, not level spawned. + // E.g. ammo clips dropped by dying former humans. + MF_DROPPED = 0x20000, + // Use fuzzy draw (shadow demons or spectres), + // temporary player invisibility powerup. + MF_SHADOW = 0x40000, + // Flag: don't bleed when shot (use puff), + // barrels and shootable furniture shall not bleed. + MF_NOBLOOD = 0x80000, + // Don't stop moving halfway off a step, + // that is, have dead bodies slide down all the way. + MF_CORPSE = 0x100000, + // Floating to a height for a move, ??? + // don't auto float to target's height. + MF_INFLOAT = 0x200000, + + // On kill, count this enemy object + // towards intermission kill total. + // Happy gathering. + MF_COUNTKILL = 0x400000, + + // On picking up, count this item object + // towards intermission item total. + MF_COUNTITEM = 0x800000, + + // Special handling: skull in flight. + // Neither a cacodemon nor a missile. + MF_SKULLFLY = 0x1000000, + + // Don't spawn this object + // in death match mode (e.g. key cards). + MF_NOTDMATCH = 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 + MF_TRANSLATION = 0xc000000, + // Hmm ???. + MF_TRANSSHIFT = 26 +} mobjflag_t; + + +// Map Object definition. +typedef struct mobj_s +{ + // List: thinker links. + thinker_t thinker; + + // Info for drawing: position. + fixed_t x; + fixed_t y; + fixed_t z; + + // More list: links in sector (if needed) + struct mobj_s* snext; + struct mobj_s* sprev; + + //More drawing info: to determine current sprite. + angle_t angle; // orientation + spritenum_t sprite; // used to find patch_t and flip value + int frame; // might be ORed with FF_FULLBRIGHT + + // Interaction info, by BLOCKMAP. + // Links in blocks (if needed). + struct mobj_s* bnext; + struct mobj_s* bprev; + + struct subsector_s* subsector; + + // The closest interval over all contacted Sectors. + fixed_t floorz; + fixed_t ceilingz; + + // For movement checking. + fixed_t radius; + fixed_t height; + + // Momentums, used to update position. + fixed_t momx; + fixed_t momy; + fixed_t momz; + + // If == validcount, already checked. + int validcount; + + mobjtype_t type; + mobjinfo_t* info; // &mobjinfo[mobj->type] + + int tics; // state tic counter + state_t* state; + int flags; + int health; + + // Movement direction, movement generation (zig-zagging). + int movedir; // 0-7 + int movecount; // when 0, select a new dir + + // Thing being chased/attacked (or 0), + // also the originator for missiles. + struct mobj_s* target; + + // Reaction time: if non 0, don't attack yet. + // Used by player to freeze a bit after teleporting. + int reactiontime; + + // If >0, the target will be chased + // no matter what (even if shot) + int threshold; + + // Additional info record for player avatars only. + // Only valid if type == MT_PLAYER + struct player_s* player; + + // Player number last looked for. + int lastlook; + + // For nightmare respawn. + mapthing_t spawnpoint; + + // Thing being chased/attacked for tracers. + struct mobj_s* tracer; +} mobj_t; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/p_plats.c b/src/DOOM/p_plats.c new file mode 100644 index 0000000..7ef9d91 --- /dev/null +++ b/src/DOOM/p_plats.c @@ -0,0 +1,298 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Plats (i.e. elevator platforms) code, raising/lowering. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "i_system.h" +#include "z_zone.h" +#include "m_random.h" +#include "doomdef.h" +#include "p_local.h" +#include "s_sound.h" +#include "doomstat.h" // State. +#include "r_state.h" // State. +#include "sounds.h" // Data. + + +plat_t* activeplats[MAXPLATS]; + + +// +// Move a plat up and down +// +void T_PlatRaise(plat_t* plat) +{ + result_e res; + + switch (plat->status) + { + case up: + res = T_MovePlane(plat->sector, + plat->speed, + plat->high, + plat->crush, 0, 1); + + if (plat->type == raiseAndChange + || plat->type == raiseToNearestAndChange) + { + if (!(leveltime & 7)) + S_StartSound((mobj_t*)&plat->sector->soundorg, + sfx_stnmov); + } + + if (res == crushed && (!plat->crush)) + { + plat->count = plat->wait; + plat->status = down; + S_StartSound((mobj_t*)&plat->sector->soundorg, + sfx_pstart); + } + else + { + if (res == pastdest) + { + plat->count = plat->wait; + plat->status = waiting; + S_StartSound((mobj_t*)&plat->sector->soundorg, + sfx_pstop); + + switch (plat->type) + { + case blazeDWUS: + case downWaitUpStay: + P_RemoveActivePlat(plat); + break; + + case raiseAndChange: + case raiseToNearestAndChange: + P_RemoveActivePlat(plat); + break; + + default: + break; + } + } + } + break; + + case down: + res = T_MovePlane(plat->sector, plat->speed, plat->low, false, 0, -1); + + if (res == pastdest) + { + plat->count = plat->wait; + plat->status = waiting; + S_StartSound((mobj_t*)&plat->sector->soundorg, sfx_pstop); + } + break; + + case waiting: + if (!--plat->count) + { + if (plat->sector->floorheight == plat->low) + plat->status = up; + else + plat->status = down; + S_StartSound((mobj_t*)&plat->sector->soundorg, sfx_pstart); + } + + case in_stasis: + break; + } +} + + +// +// Do Platforms +// "amount" is only used for SOME platforms. +// +int EV_DoPlat(line_t* line, plattype_e type, int amount) +{ + plat_t* plat; + int secnum; + int rtn; + sector_t* sec; + + secnum = -1; + rtn = 0; + + // Activate all plats that are in_stasis + switch (type) + { + case perpetualRaise: + P_ActivateInStasis(line->tag); + break; + default: + break; + } + + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + + if (sec->specialdata) + continue; + + // Find lowest & highest floors around sector + rtn = 1; + plat = Z_Malloc(sizeof(*plat), PU_LEVSPEC, 0); + P_AddThinker(&plat->thinker); + + plat->type = type; + plat->sector = sec; + plat->sector->specialdata = plat; + plat->thinker.function.acp1 = (actionf_p1)T_PlatRaise; + plat->crush = false; + plat->tag = line->tag; + + switch (type) + { + case raiseToNearestAndChange: + plat->speed = PLATSPEED / 2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = P_FindNextHighestFloor(sec, sec->floorheight); + plat->wait = 0; + plat->status = up; + // NO MORE DAMAGE, IF APPLICABLE + sec->special = 0; + + S_StartSound((mobj_t*)&sec->soundorg, sfx_stnmov); + break; + + case raiseAndChange: + plat->speed = PLATSPEED / 2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = sec->floorheight + amount * FRACUNIT; + plat->wait = 0; + plat->status = up; + + S_StartSound((mobj_t*)&sec->soundorg, sfx_stnmov); + break; + + case downWaitUpStay: + plat->speed = PLATSPEED * 4; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = 35 * PLATWAIT; + plat->status = down; + S_StartSound((mobj_t*)&sec->soundorg, sfx_pstart); + break; + + case blazeDWUS: + plat->speed = PLATSPEED * 8; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = 35 * PLATWAIT; + plat->status = down; + S_StartSound((mobj_t*)&sec->soundorg, sfx_pstart); + break; + + case perpetualRaise: + plat->speed = PLATSPEED; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = P_FindHighestFloorSurrounding(sec); + + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + + plat->wait = 35 * PLATWAIT; + plat->status = P_Random() & 1; + + S_StartSound((mobj_t*)&sec->soundorg, sfx_pstart); + break; + } + P_AddActivePlat(plat); + } + + return rtn; +} + +void P_ActivateInStasis(int tag) +{ + int i; + + for (i = 0; i < MAXPLATS; i++) + if (activeplats[i] + && (activeplats[i])->tag == tag + && (activeplats[i])->status == in_stasis) + { + (activeplats[i])->status = (activeplats[i])->oldstatus; + (activeplats[i])->thinker.function.acp1 = (actionf_p1)T_PlatRaise; + } +} + +void EV_StopPlat(line_t* line) +{ + int j; + + for (j = 0; j < MAXPLATS; j++) + if (activeplats[j] + && ((activeplats[j])->status != in_stasis) + && ((activeplats[j])->tag == line->tag)) + { + (activeplats[j])->oldstatus = (activeplats[j])->status; + (activeplats[j])->status = in_stasis; + (activeplats[j])->thinker.function.acv = (actionf_v)0; + } +} + +void P_AddActivePlat(plat_t* plat) +{ + int i; + + for (i = 0; i < MAXPLATS; i++) + if (activeplats[i] == 0) + { + activeplats[i] = plat; + return; + } + I_Error("Error: P_AddActivePlat: no more plats!"); +} + +void P_RemoveActivePlat(plat_t* plat) +{ + int i; + for (i = 0; i < MAXPLATS; i++) + if (plat == activeplats[i]) + { + (activeplats[i])->sector->specialdata = 0; + P_RemoveThinker(&(activeplats[i])->thinker); + activeplats[i] = 0; + + return; + } + I_Error("Error: P_RemoveActivePlat: can't find plat!"); +} diff --git a/src/DOOM/p_pspr.c b/src/DOOM/p_pspr.c new file mode 100644 index 0000000..09d5fe1 --- /dev/null +++ b/src/DOOM/p_pspr.c @@ -0,0 +1,778 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Weapon sprite animation, weapon objects. +// Action functions for weapons. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "doomdef.h" +#include "d_event.h" +#include "m_random.h" +#include "p_local.h" +#include "s_sound.h" +#include "doomstat.h" // State. +#include "sounds.h" // Data. +#include "p_pspr.h" + + +#define LOWERSPEED (FRACUNIT*6) +#define RAISESPEED (FRACUNIT*6) + +#define WEAPONBOTTOM (128*FRACUNIT) +#define WEAPONTOP (32*FRACUNIT) + +// plasma cells for a bfg attack +#define BFGCELLS 40 + + +fixed_t swingx; +fixed_t swingy; +fixed_t bulletslope; + + +// +// P_SetPsprite +// +void P_SetPsprite(player_t* player, int position, statenum_t stnum) +{ + pspdef_t* psp; + state_t* state; + + psp = &player->psprites[position]; + + do + { + if (!stnum) + { + // object removed itself + psp->state = 0; + break; + } + + state = &states[stnum]; + psp->state = state; + psp->tics = state->tics; // could be 0 + + if (state->misc1) + { + // coordinate set + psp->sx = state->misc1 << FRACBITS; + psp->sy = state->misc2 << FRACBITS; + } + + // Call action routine. + // Modified handling. + if (state->action.acp2) + { + state->action.acp2(player, psp); + if (!psp->state) + break; + } + + stnum = psp->state->nextstate; + + } while (!psp->tics); + // an initial state of 0 could cycle through +} + + +// +// P_BringUpWeapon +// Starts bringing the pending weapon up +// from the bottom of the screen. +// Uses player +// +void P_BringUpWeapon(player_t* player) +{ + statenum_t newstate; + + if (player->pendingweapon == wp_nochange) + player->pendingweapon = player->readyweapon; + + if (player->pendingweapon == wp_chainsaw) + S_StartSound(player->mo, sfx_sawup); + + newstate = weaponinfo[player->pendingweapon].upstate; + + player->pendingweapon = wp_nochange; + player->psprites[ps_weapon].sy = WEAPONBOTTOM; + + P_SetPsprite(player, ps_weapon, newstate); +} + + +// +// P_CheckAmmo +// Returns true if there is enough ammo to shoot. +// If not, selects the next weapon to use. +// +doom_boolean P_CheckAmmo(player_t* player) +{ + ammotype_t ammo; + int count; + + ammo = weaponinfo[player->readyweapon].ammo; + + // Minimal amount for one shot varies. + if (player->readyweapon == wp_bfg) + count = BFGCELLS; + else if (player->readyweapon == wp_supershotgun) + count = 2; // Double barrel. + else + count = 1; // Regular. + + // Some do not need ammunition anyway. + // Return if current ammunition sufficient. + if (ammo == am_noammo || player->ammo[ammo] >= count) + return true; + + // Out of ammo, pick a weapon to change to. + // Preferences are set here. + do + { + if (player->weaponowned[wp_plasma] + && player->ammo[am_cell] + && (gamemode != shareware)) + { + player->pendingweapon = wp_plasma; + } + else if (player->weaponowned[wp_supershotgun] + && player->ammo[am_shell] > 2 + && (gamemode == commercial)) + { + player->pendingweapon = wp_supershotgun; + } + else if (player->weaponowned[wp_chaingun] + && player->ammo[am_clip]) + { + player->pendingweapon = wp_chaingun; + } + else if (player->weaponowned[wp_shotgun] + && player->ammo[am_shell]) + { + player->pendingweapon = wp_shotgun; + } + else if (player->ammo[am_clip]) + { + player->pendingweapon = wp_pistol; + } + else if (player->weaponowned[wp_chainsaw]) + { + player->pendingweapon = wp_chainsaw; + } + else if (player->weaponowned[wp_missile] + && player->ammo[am_misl]) + { + player->pendingweapon = wp_missile; + } + else if (player->weaponowned[wp_bfg] + && player->ammo[am_cell] > 40 + && (gamemode != shareware)) + { + player->pendingweapon = wp_bfg; + } + else + { + // If everything fails. + player->pendingweapon = wp_fist; + } + + } while (player->pendingweapon == wp_nochange); + + // Now set appropriate weapon overlay. + P_SetPsprite(player, + ps_weapon, + weaponinfo[player->readyweapon].downstate); + + return false; +} + + +// +// P_FireWeapon. +// +void P_FireWeapon(player_t* player) +{ + statenum_t newstate; + + if (!P_CheckAmmo(player)) + return; + + P_SetMobjState(player->mo, S_PLAY_ATK1); + newstate = weaponinfo[player->readyweapon].atkstate; + P_SetPsprite(player, ps_weapon, newstate); + P_NoiseAlert(player->mo, player->mo); + + // [pd] Stop gun bobbing when shooting + pspdef_t* psp; + psp = &player->psprites[ps_weapon]; + psp->sx = FRACUNIT; + psp->sy = WEAPONTOP; +} + + +// +// P_DropWeapon +// Player died, so put the weapon away. +// +void P_DropWeapon(player_t* player) +{ + P_SetPsprite(player, + ps_weapon, + weaponinfo[player->readyweapon].downstate); +} + + +// +// A_WeaponReady +// The player can fire the weapon +// or change to another weapon at this time. +// Follows after getting weapon up, +// or after previous attack/fire sequence. +// +void A_WeaponReady(player_t* player, pspdef_t* psp) +{ + statenum_t newstate; + int angle; + + // get out of attack state + if (player->mo->state == &states[S_PLAY_ATK1] + || player->mo->state == &states[S_PLAY_ATK2]) + { + P_SetMobjState(player->mo, S_PLAY); + } + + if (player->readyweapon == wp_chainsaw + && psp->state == &states[S_SAW]) + { + S_StartSound(player->mo, sfx_sawidl); + } + + // check for change + // if player is dead, put the weapon away + if (player->pendingweapon != wp_nochange || !player->health) + { + // change weapon + // (pending weapon should allready be validated) + newstate = weaponinfo[player->readyweapon].downstate; + P_SetPsprite(player, ps_weapon, newstate); + return; + } + + // check for fire + // the missile launcher and bfg do not auto fire + if (player->cmd.buttons & BT_ATTACK) + { + if (!player->attackdown + || (player->readyweapon != wp_missile + && player->readyweapon != wp_bfg)) + { + player->attackdown = true; + P_FireWeapon(player); + return; + } + } + else + player->attackdown = false; + + // bob the weapon based on movement speed + angle = (128 * leveltime) & FINEMASK; + psp->sx = FRACUNIT + FixedMul(player->bob, finecosine[angle]); + angle &= FINEANGLES / 2 - 1; + psp->sy = WEAPONTOP + FixedMul(player->bob, finesine[angle]); +} + + +// +// A_ReFire +// The player can re-fire the weapon +// without lowering it entirely. +// +void A_ReFire(player_t* player, pspdef_t* psp) +{ + // check for fire + // (if a weaponchange is pending, let it go through instead) + if ((player->cmd.buttons & BT_ATTACK) + && player->pendingweapon == wp_nochange + && player->health) + { + player->refire++; + P_FireWeapon(player); + } + else + { + player->refire = 0; + P_CheckAmmo(player); + } +} + + +void A_CheckReload(player_t* player, pspdef_t* psp) +{ + P_CheckAmmo(player); +} + + +// +// A_Lower +// Lowers current weapon, +// and changes weapon at bottom. +// +void A_Lower(player_t* player, pspdef_t* psp) +{ + psp->sy += LOWERSPEED; + + // Is already down. + if (psp->sy < WEAPONBOTTOM) + return; + + // Player is dead. + if (player->playerstate == PST_DEAD) + { + psp->sy = WEAPONBOTTOM; + + // don't bring weapon back up + return; + } + + // The old weapon has been lowered off the screen, + // so change the weapon and start raising it + if (!player->health) + { + // Player is dead, so keep the weapon off screen. + P_SetPsprite(player, ps_weapon, S_NULL); + return; + } + + player->readyweapon = player->pendingweapon; + + P_BringUpWeapon(player); +} + + +// +// A_Raise +// +void A_Raise(player_t* player, pspdef_t* psp) +{ + statenum_t newstate; + + 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. + newstate = weaponinfo[player->readyweapon].readystate; + + P_SetPsprite(player, ps_weapon, newstate); +} + + +// +// A_GunFlash +// +void A_GunFlash(player_t* player, pspdef_t* psp) +{ + P_SetMobjState(player->mo, S_PLAY_ATK2); + P_SetPsprite(player, ps_flash, weaponinfo[player->readyweapon].flashstate); +} + + +// +// WEAPON ATTACKS +// + +// +// A_Punch +// +void A_Punch(player_t* player, pspdef_t* psp) +{ + angle_t angle; + int damage; + int slope; + + damage = (P_Random() % 10 + 1) << 1; + + if (player->powers[pw_strength]) + damage *= 10; + + angle = player->mo->angle; + angle += (P_Random() - P_Random()) << 18; + slope = P_AimLineAttack(player->mo, angle, MELEERANGE); + P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); + + // turn to face target + if (linetarget) + { + S_StartSound(player->mo, sfx_punch); + player->mo->angle = R_PointToAngle2(player->mo->x, + player->mo->y, + linetarget->x, + linetarget->y); + } +} + + +// +// A_Saw +// +void A_Saw(player_t* player, pspdef_t* psp) +{ + angle_t angle; + int damage; + int slope; + + damage = 2 * (P_Random() % 10 + 1); + angle = player->mo->angle; + angle += (P_Random() - P_Random()) << 18; + + // use meleerange + 1 se the puff doesn't skip the flash + slope = P_AimLineAttack(player->mo, angle, MELEERANGE + 1); + P_LineAttack(player->mo, angle, MELEERANGE + 1, slope, damage); + + if (!linetarget) + { + S_StartSound(player->mo, sfx_sawful); + return; + } + S_StartSound(player->mo, sfx_sawhit); + + // turn to face target + angle = R_PointToAngle2(player->mo->x, player->mo->y, + linetarget->x, linetarget->y); + if (angle - player->mo->angle > ANG180) + { + if (angle - player->mo->angle < -ANG90 / 20) + player->mo->angle = angle + ANG90 / 21; + else + player->mo->angle -= ANG90 / 20; + } + else + { + if (angle - player->mo->angle > ANG90 / 20) + player->mo->angle = angle - ANG90 / 21; + else + player->mo->angle += ANG90 / 20; + } + player->mo->flags |= MF_JUSTATTACKED; +} + + +// +// A_FireMissile +// +void A_FireMissile(player_t* player, pspdef_t* psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo]--; + P_SpawnPlayerMissile(player->mo, MT_ROCKET); +} + + +// +// A_FireBFG +// +void A_FireBFG(player_t* player, pspdef_t* psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS; + P_SpawnPlayerMissile(player->mo, MT_BFG); +} + + +// +// A_FirePlasma +// +void A_FirePlasma(player_t* player, pspdef_t* psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + P_SetPsprite(player, + ps_flash, + weaponinfo[player->readyweapon].flashstate + (P_Random() & 1)); + + P_SpawnPlayerMissile(player->mo, MT_PLASMA); +} + + +// +// P_BulletSlope +// Sets a slope so a near miss is at aproximately +// the height of the intended target +// +void P_BulletSlope(mobj_t* mo) +{ + angle_t an; + + // see which target is to be aimed at + an = mo->angle; + bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an += 1 << 26; + bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT); + if (!linetarget) + { + an -= 2 << 26; + bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT); + } + } +} + + +// +// P_GunShot +// +void P_GunShot(mobj_t* mo, doom_boolean accurate) +{ + angle_t angle; + int damage; + + damage = 5 * (P_Random() % 3 + 1); + angle = mo->angle; + + if (!accurate) + angle += (P_Random() - P_Random()) << 18; + + P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage); +} + + +// +// A_FirePistol +// +void A_FirePistol(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_pistol); + + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + P_SetPsprite(player, + ps_flash, + weaponinfo[player->readyweapon].flashstate); + + P_BulletSlope(player->mo); + P_GunShot(player->mo, !player->refire); +} + + +// +// A_FireShotgun +// +void A_FireShotgun(player_t* player, pspdef_t* psp) +{ + int i; + + S_StartSound(player->mo, sfx_shotgn); + P_SetMobjState(player->mo, S_PLAY_ATK2); + + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + P_SetPsprite(player, + ps_flash, + weaponinfo[player->readyweapon].flashstate); + + P_BulletSlope(player->mo); + + for (i = 0; i < 7; i++) + P_GunShot(player->mo, false); +} + + +// +// A_FireShotgun2 +// +void A_FireShotgun2(player_t* player, pspdef_t* psp) +{ + int i; + angle_t angle; + int damage; + + + S_StartSound(player->mo, sfx_dshtgn); + P_SetMobjState(player->mo, S_PLAY_ATK2); + + player->ammo[weaponinfo[player->readyweapon].ammo] -= 2; + + P_SetPsprite(player, + ps_flash, + weaponinfo[player->readyweapon].flashstate); + + P_BulletSlope(player->mo); + + for (i = 0; i < 20; i++) + { + damage = 5 * (P_Random() % 3 + 1); + angle = player->mo->angle; + angle += (P_Random() - P_Random()) << 19; + P_LineAttack(player->mo, + angle, + MISSILERANGE, + bulletslope + ((P_Random() - P_Random()) << 5), damage); + } +} + + +// +// A_FireCGun +// +void A_FireCGun(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_pistol); + + if (!player->ammo[weaponinfo[player->readyweapon].ammo]) + return; + + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + P_SetPsprite(player, + ps_flash, + weaponinfo[player->readyweapon].flashstate + + psp->state + - &states[S_CHAIN1]); + + P_BulletSlope(player->mo); + + P_GunShot(player->mo, !player->refire); +} + + +// +// ? +// +void A_Light0(player_t* player, pspdef_t* psp) +{ + player->extralight = 0; +} + +void A_Light1(player_t* player, pspdef_t* psp) +{ + player->extralight = 1; +} + +void A_Light2(player_t* player, pspdef_t* psp) +{ + player->extralight = 2; +} + + +// +// A_BFGSpray +// Spawn a BFG explosion on every monster in view +// +void A_BFGSpray(mobj_t* mo) +{ + int i; + int j; + int damage; + angle_t an; + + // offset angles from its attack angle + for (i = 0; i < 40; i++) + { + an = mo->angle - ANG90 / 2 + ANG90 / 40 * i; + + // mo->target is the originator (player) + // of the missile + P_AimLineAttack(mo->target, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + continue; + + P_SpawnMobj(linetarget->x, + linetarget->y, + linetarget->z + (linetarget->height >> 2), + MT_EXTRABFG); + + damage = 0; + for (j = 0; j < 15; j++) + damage += (P_Random() & 7) + 1; + + P_DamageMobj(linetarget, mo->target, mo->target, damage); + } +} + + +// +// A_BFGsound +// +void A_BFGsound(player_t* player, pspdef_t* psp) +{ + S_StartSound(player->mo, sfx_bfg); +} + + +// +// P_SetupPsprites +// Called at start of level for each player. +// +void P_SetupPsprites(player_t* player) +{ + int i; + + // remove all psprites + for (i = 0; i < NUMPSPRITES; i++) + player->psprites[i].state = 0; + + // spawn the gun + player->pendingweapon = player->readyweapon; + P_BringUpWeapon(player); +} + + +// +// P_MovePsprites +// Called every tic by player thinking routine. +// +void P_MovePsprites(player_t* player) +{ + int i; + pspdef_t* psp; + state_t* state; + + psp = &player->psprites[0]; + for (i = 0; i < NUMPSPRITES; i++, psp++) + { + // a null state means not active + if ((state = psp->state)) + { + // drop tic count and possibly change state + + // a -1 tic count never changes + if (psp->tics != -1) + { + psp->tics--; + if (!psp->tics) + P_SetPsprite(player, i, psp->state->nextstate); + } + } + } + + player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; + player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; +} diff --git a/src/DOOM/p_pspr.h b/src/DOOM/p_pspr.h new file mode 100644 index 0000000..83673d4 --- /dev/null +++ b/src/DOOM/p_pspr.h @@ -0,0 +1,75 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Sprite animation. +// +//----------------------------------------------------------------------------- + + +#ifndef __P_PSPR__ +#define __P_PSPR__ + +// Basic data types. +// Needs fixed point, and BAM angles. +#include "m_fixed.h" +#include "tables.h" + +// Needs to include the precompiled +// sprite animation tables. +// Header generated by multigen utility. +// This includes all the data for thing animation, +// i.e. the Thing Atrributes table +// and the Frame Sequence table. +#include "info.h" + +// +// Frame flags: +// handles maximum brightness (torches, muzzle flare, light sources) +// +#define FF_FULLBRIGHT 0x8000 // flag in thing->frame +#define FF_FRAMEMASK 0x7fff + + +// +// Overlay psprites are scaled shapes +// drawn directly on the view screen, +// coordinates are given for a 320*200 view screen. +// +typedef enum +{ + ps_weapon, + ps_flash, + NUMPSPRITES +} psprnum_t; + + +typedef struct +{ + state_t* state; // a 0 state means not active + int tics; + fixed_t sx; + fixed_t sy; +} pspdef_t; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/p_saveg.c b/src/DOOM/p_saveg.c new file mode 100644 index 0000000..cb0b88b --- /dev/null +++ b/src/DOOM/p_saveg.c @@ -0,0 +1,584 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Archiving: SaveGame I/O. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "i_system.h" +#include "z_zone.h" +#include "p_local.h" +#include "doomstat.h" // State. +#include "r_state.h" // State. + + +// Pads save_p to a 4-byte boundary +// so that the load/save works on SGI&Gecko. +#define PADSAVEP() save_p += (4 - ((long long)save_p & 3)) & 3 + + +byte* save_p; + + +// TODO: [pd] We are loading/saving raw pointers. It will not work with saves from 32bits system. We need to rewrite those functions. +// +// P_ArchivePlayers +// +void P_ArchivePlayers(void) +{ + int i; + int j; + player_t* dest; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + PADSAVEP(); + + dest = (player_t*)save_p; + doom_memcpy(dest, &players[i], sizeof(player_t)); + save_p += sizeof(player_t); + for (j = 0; j < NUMPSPRITES; j++) + { + if (dest->psprites[j].state) + { + dest->psprites[j].state + = (state_t*)(dest->psprites[j].state - states); + } + } + } +} + + +// +// P_UnArchivePlayers +// +void P_UnArchivePlayers(void) +{ + int i; + int j; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + PADSAVEP(); + + doom_memcpy(&players[i], save_p, sizeof(player_t)); + save_p += sizeof(player_t); + + // will be set when unarc thinker + players[i].mo = 0; + players[i].message = 0; + players[i].attacker = 0; + + for (j = 0; j < NUMPSPRITES; j++) + { + if (players[i].psprites[j].state) + { + players[i].psprites[j].state + = &states[(long long)players[i].psprites[j].state]; + } + } + } +} + + +// +// P_ArchiveWorld +// +void P_ArchiveWorld(void) +{ + int i; + int j; + sector_t* sec; + line_t* li; + side_t* si; + short* put; + + put = (short*)save_p; + + // do sectors + for (i = 0, sec = sectors; i < numsectors; i++, sec++) + { + *put++ = sec->floorheight >> FRACBITS; + *put++ = sec->ceilingheight >> FRACBITS; + *put++ = sec->floorpic; + *put++ = sec->ceilingpic; + *put++ = sec->lightlevel; + *put++ = sec->special; // needed? + *put++ = sec->tag; // needed? + } + + + // do lines + for (i = 0, li = lines; i < numlines; i++, li++) + { + *put++ = li->flags; + *put++ = li->special; + *put++ = li->tag; + for (j = 0; j < 2; j++) + { + if (li->sidenum[j] == -1) + continue; + + si = &sides[li->sidenum[j]]; + + *put++ = si->textureoffset >> FRACBITS; + *put++ = si->rowoffset >> FRACBITS; + *put++ = si->toptexture; + *put++ = si->bottomtexture; + *put++ = si->midtexture; + } + } + + save_p = (byte*)put; +} + + +// +// P_UnArchiveWorld +// +void P_UnArchiveWorld(void) +{ + int i; + int j; + sector_t* sec; + line_t* li; + side_t* si; + short* get; + + get = (short*)save_p; + + // do sectors + for (i = 0, sec = sectors; i < numsectors; i++, sec++) + { + sec->floorheight = *get++ << FRACBITS; + sec->ceilingheight = *get++ << FRACBITS; + sec->floorpic = *get++; + sec->ceilingpic = *get++; + sec->lightlevel = *get++; + sec->special = *get++; // needed? + sec->tag = *get++; // needed? + sec->specialdata = 0; + sec->soundtarget = 0; + } + + // do lines + for (i = 0, li = lines; i < numlines; i++, li++) + { + li->flags = *get++; + li->special = *get++; + li->tag = *get++; + for (j = 0; j < 2; j++) + { + if (li->sidenum[j] == -1) + continue; + si = &sides[li->sidenum[j]]; + si->textureoffset = *get++ << FRACBITS; + si->rowoffset = *get++ << FRACBITS; + si->toptexture = *get++; + si->bottomtexture = *get++; + si->midtexture = *get++; + } + } + + save_p = (byte*)get; +} + + +// +// Thinkers +// +typedef enum +{ + tc_end, + tc_mobj +} thinkerclass_t; + + +// +// P_ArchiveThinkers +// +void P_ArchiveThinkers(void) +{ + thinker_t* th; + mobj_t* mobj; + + // save off the current thinkers + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_MobjThinker) + { + *save_p++ = tc_mobj; + PADSAVEP(); + mobj = (mobj_t*)save_p; + doom_memcpy(mobj, th, sizeof(*mobj)); + save_p += sizeof(*mobj); + mobj->state = (state_t*)(mobj->state - states); + + if (mobj->player) + mobj->player = (player_t*)((mobj->player - players) + 1); + continue; + } + + // I_Error ("P_ArchiveThinkers: Unknown thinker function"); + } + + // add a terminating marker + *save_p++ = tc_end; +} + + +// +// P_UnArchiveThinkers +// +void P_UnArchiveThinkers(void) +{ + byte tclass; + thinker_t* currentthinker; + thinker_t* next; + mobj_t* mobj; + + // remove all the current thinkers + currentthinker = thinkercap.next; + while (currentthinker != &thinkercap) + { + next = currentthinker->next; + + if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) + P_RemoveMobj((mobj_t*)currentthinker); + else + Z_Free(currentthinker); + + currentthinker = next; + } + P_InitThinkers(); + + // read in saved thinkers + while (1) + { + tclass = *save_p++; + switch (tclass) + { + case tc_end: + return; // end of list + + case tc_mobj: + PADSAVEP(); + mobj = Z_Malloc(sizeof(*mobj), PU_LEVEL, 0); + doom_memcpy(mobj, save_p, sizeof(*mobj)); + save_p += sizeof(*mobj); + mobj->state = &states[(long long)mobj->state]; + mobj->target = 0; + if (mobj->player) + { + mobj->player = &players[(long long)mobj->player - 1]; + mobj->player->mo = mobj; + } + P_SetThingPosition(mobj); + mobj->info = &mobjinfo[mobj->type]; + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; + P_AddThinker(&mobj->thinker); + break; + + default: + { + //I_Error("Error: Unknown tclass %i in savegame", tclass); + + doom_strcpy(error_buf, "Error: Unknown tclass "); + doom_concat(error_buf, doom_itoa(tclass, 10)); + doom_concat(error_buf, " in savegame"); + I_Error(error_buf); + } + } + } +} + + +// +// P_ArchiveSpecials +// +enum +{ + tc_ceiling, + tc_door, + tc_floor, + tc_plat, + tc_flash, + tc_strobe, + tc_glow, + tc_endspecials +} specials_e; + + +// +// Things to handle: +// +// T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list +// T_VerticalDoor, (vldoor_t: sector_t * swizzle), +// T_MoveFloor, (floormove_t: sector_t * swizzle), +// T_LightFlash, (lightflash_t: sector_t * swizzle), +// T_StrobeFlash, (strobe_t: sector_t *), +// T_Glow, (glow_t: sector_t *), +// T_PlatRaise, (plat_t: sector_t *), - active list +// +void P_ArchiveSpecials(void) +{ + thinker_t* th; + ceiling_t* ceiling; + vldoor_t* door; + floormove_t* floor; + plat_t* plat; + lightflash_t* flash; + strobe_t* strobe; + glow_t* glow; + int i; + + // save off the current thinkers + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acv == (actionf_v)0) + { + for (i = 0; i < MAXCEILINGS; i++) + if (activeceilings[i] == (ceiling_t*)th) + break; + + if (i < MAXCEILINGS) + { + *save_p++ = tc_ceiling; + PADSAVEP(); + ceiling = (ceiling_t*)save_p; + doom_memcpy(ceiling, th, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = (sector_t*)(ceiling->sector - sectors); + } + continue; + } + + if (th->function.acp1 == (actionf_p1)T_MoveCeiling) + { + *save_p++ = tc_ceiling; + PADSAVEP(); + ceiling = (ceiling_t*)save_p; + doom_memcpy(ceiling, th, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = (sector_t*)(ceiling->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_VerticalDoor) + { + *save_p++ = tc_door; + PADSAVEP(); + door = (vldoor_t*)save_p; + doom_memcpy(door, th, sizeof(*door)); + save_p += sizeof(*door); + door->sector = (sector_t*)(door->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_MoveFloor) + { + *save_p++ = tc_floor; + PADSAVEP(); + floor = (floormove_t*)save_p; + doom_memcpy(floor, th, sizeof(*floor)); + save_p += sizeof(*floor); + floor->sector = (sector_t*)(floor->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_PlatRaise) + { + *save_p++ = tc_plat; + PADSAVEP(); + plat = (plat_t*)save_p; + doom_memcpy(plat, th, sizeof(*plat)); + save_p += sizeof(*plat); + plat->sector = (sector_t*)(plat->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_LightFlash) + { + *save_p++ = tc_flash; + PADSAVEP(); + flash = (lightflash_t*)save_p; + doom_memcpy(flash, th, sizeof(*flash)); + save_p += sizeof(*flash); + flash->sector = (sector_t*)(flash->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_StrobeFlash) + { + *save_p++ = tc_strobe; + PADSAVEP(); + strobe = (strobe_t*)save_p; + doom_memcpy(strobe, th, sizeof(*strobe)); + save_p += sizeof(*strobe); + strobe->sector = (sector_t*)(strobe->sector - sectors); + continue; + } + + if (th->function.acp1 == (actionf_p1)T_Glow) + { + *save_p++ = tc_glow; + PADSAVEP(); + glow = (glow_t*)save_p; + doom_memcpy(glow, th, sizeof(*glow)); + save_p += sizeof(*glow); + glow->sector = (sector_t*)(glow->sector - sectors); + continue; + } + } + + // add a terminating marker + *save_p++ = tc_endspecials; +} + + +// +// P_UnArchiveSpecials +// +void P_UnArchiveSpecials(void) +{ + byte tclass; + ceiling_t* ceiling; + vldoor_t* door; + floormove_t* floor; + plat_t* plat; + lightflash_t* flash; + strobe_t* strobe; + glow_t* glow; + + + // read in saved thinkers + while (1) + { + tclass = *save_p++; + switch (tclass) + { + case tc_endspecials: + return; // end of list + + case tc_ceiling: + PADSAVEP(); + ceiling = Z_Malloc(sizeof(*ceiling), PU_LEVEL, 0); + doom_memcpy(ceiling, save_p, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = §ors[(long long)ceiling->sector]; + ceiling->sector->specialdata = ceiling; + + if (ceiling->thinker.function.acp1) + ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; + + P_AddThinker(&ceiling->thinker); + P_AddActiveCeiling(ceiling); + break; + + case tc_door: + PADSAVEP(); + door = Z_Malloc(sizeof(*door), PU_LEVEL, 0); + doom_memcpy(door, save_p, sizeof(*door)); + save_p += sizeof(*door); + door->sector = §ors[(long long)door->sector]; + door->sector->specialdata = door; + door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; + P_AddThinker(&door->thinker); + break; + + case tc_floor: + PADSAVEP(); + floor = Z_Malloc(sizeof(*floor), PU_LEVEL, 0); + doom_memcpy(floor, save_p, sizeof(*floor)); + save_p += sizeof(*floor); + floor->sector = §ors[(long long)floor->sector]; + floor->sector->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + P_AddThinker(&floor->thinker); + break; + + case tc_plat: + PADSAVEP(); + plat = Z_Malloc(sizeof(*plat), PU_LEVEL, 0); + doom_memcpy(plat, save_p, sizeof(*plat)); + save_p += sizeof(*plat); + plat->sector = §ors[(long long)plat->sector]; + plat->sector->specialdata = plat; + + if (plat->thinker.function.acp1) + plat->thinker.function.acp1 = (actionf_p1)T_PlatRaise; + + P_AddThinker(&plat->thinker); + P_AddActivePlat(plat); + break; + + case tc_flash: + PADSAVEP(); + flash = Z_Malloc(sizeof(*flash), PU_LEVEL, 0); + doom_memcpy(flash, save_p, sizeof(*flash)); + save_p += sizeof(*flash); + flash->sector = §ors[(long long)flash->sector]; + flash->thinker.function.acp1 = (actionf_p1)T_LightFlash; + P_AddThinker(&flash->thinker); + break; + + case tc_strobe: + PADSAVEP(); + strobe = Z_Malloc(sizeof(*strobe), PU_LEVEL, 0); + doom_memcpy(strobe, save_p, sizeof(*strobe)); + save_p += sizeof(*strobe); + strobe->sector = §ors[(long long)strobe->sector]; + strobe->thinker.function.acp1 = (actionf_p1)T_StrobeFlash; + P_AddThinker(&strobe->thinker); + break; + + case tc_glow: + PADSAVEP(); + glow = Z_Malloc(sizeof(*glow), PU_LEVEL, 0); + doom_memcpy(glow, save_p, sizeof(*glow)); + save_p += sizeof(*glow); + glow->sector = §ors[(long long)glow->sector]; + glow->thinker.function.acp1 = (actionf_p1)T_Glow; + P_AddThinker(&glow->thinker); + break; + + default: + { + //I_Error("Error: P_UnarchiveSpecials:Unknown tclass %i " + // "in savegame", tclass); + + doom_strcpy(error_buf, "Error: P_UnarchiveSpecials:Unknown tclass "); + doom_concat(error_buf, doom_itoa(tclass, 10)); + doom_concat(error_buf, " in savegame"); + I_Error(error_buf); + } + } + } +} diff --git a/src/DOOM/p_saveg.h b/src/DOOM/p_saveg.h new file mode 100644 index 0000000..27182c9 --- /dev/null +++ b/src/DOOM/p_saveg.h @@ -0,0 +1,49 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Savegame I/O, archiving, persistence. +// +//----------------------------------------------------------------------------- + +#ifndef __P_SAVEG__ +#define __P_SAVEG__ + + +#include "doomtype.h" + + +// Persistent storage/archiving. +// These are the load / save game routines. +void P_ArchivePlayers(void); +void P_UnArchivePlayers(void); +void P_ArchiveWorld(void); +void P_UnArchiveWorld(void); +void P_ArchiveThinkers(void); +void P_UnArchiveThinkers(void); +void P_ArchiveSpecials(void); +void P_UnArchiveSpecials(void); + +extern byte* save_p; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/p_setup.c b/src/DOOM/p_setup.c new file mode 100644 index 0000000..9107e4f --- /dev/null +++ b/src/DOOM/p_setup.c @@ -0,0 +1,674 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Do all the WAD I/O, get map description, +// set up initial state and misc. LUTs. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "z_zone.h" +#include "m_swap.h" +#include "m_bbox.h" +#include "g_game.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "p_local.h" +#include "s_sound.h" +#include "doomstat.h" + + +// Maintain single and multi player starting spots. +#define MAX_DEATHMATCH_STARTS 10 + + +// +// MAP related Lookup tables. +// Store VERTEXES, LINEDEFS, SIDEDEFS, etc. +// +int numvertexes; +vertex_t* vertexes; + +int numsegs; +seg_t* segs; + +int numsectors; +sector_t* sectors; + +int numsubsectors; +subsector_t* subsectors; + +int numnodes; +node_t* nodes; + +int numlines; +line_t* lines; + +int numsides; +side_t* sides; + +// BLOCKMAP +// Created from axis aligned bounding box +// of the map, a rectangular array of +// blocks of size ... +// Used to speed up collision detection +// by spatial subdivision in 2D. +// +// Blockmap size. +int bmapwidth; +int bmapheight; // size in mapblocks +short* blockmap; // int for larger maps +// offsets in blockmap are from here +short* blockmaplump; +// origin of block map +fixed_t bmaporgx; +fixed_t bmaporgy; +// for thing chains +mobj_t** blocklinks; + +// REJECT +// For fast sight rejection. +// Speeds up enemy AI by skipping detailed +// LineOf Sight calculation. +// Without special effect, this could be +// used as a PVS lookup as well. +// +byte* rejectmatrix; + +mapthing_t deathmatchstarts[MAX_DEATHMATCH_STARTS]; +mapthing_t* deathmatch_p; +mapthing_t playerstarts[MAXPLAYERS]; + + +void P_SpawnMapThing(mapthing_t* mthing); + + +// +// P_LoadVertexes +// +void P_LoadVertexes(int lump) +{ + byte* data; + int i; + mapvertex_t* ml; + vertex_t* li; + + // Determine number of lumps: + // total lump length / vertex record length. + numvertexes = W_LumpLength(lump) / sizeof(mapvertex_t); + + // Allocate zone memory for buffer. + vertexes = Z_Malloc(numvertexes * sizeof(vertex_t), PU_LEVEL, 0); + + // Load data into cache. + data = W_CacheLumpNum(lump, PU_STATIC); + + ml = (mapvertex_t*)data; + li = vertexes; + + // Copy and convert vertex coordinates, + // internal representation as fixed. + for (i = 0; i < numvertexes; i++, li++, ml++) + { + li->x = SHORT(ml->x) << FRACBITS; + li->y = SHORT(ml->y) << FRACBITS; + } + + // Free buffer memory. + Z_Free(data); +} + + +// +// P_LoadSegs +// +void P_LoadSegs(int lump) +{ + byte* data; + int i; + mapseg_t* ml; + seg_t* li; + line_t* ldef; + int linedef; + int side; + + numsegs = W_LumpLength(lump) / sizeof(mapseg_t); + segs = Z_Malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0); + doom_memset(segs, 0, numsegs * sizeof(seg_t)); + data = W_CacheLumpNum(lump, PU_STATIC); + + ml = (mapseg_t*)data; + li = segs; + for (i = 0; i < numsegs; i++, li++, ml++) + { + li->v1 = &vertexes[SHORT(ml->v1)]; + li->v2 = &vertexes[SHORT(ml->v2)]; + + li->angle = (SHORT(ml->angle)) << 16; + li->offset = (SHORT(ml->offset)) << 16; + linedef = SHORT(ml->linedef); + ldef = &lines[linedef]; + li->linedef = ldef; + side = SHORT(ml->side); + li->sidedef = &sides[ldef->sidenum[side]]; + li->frontsector = sides[ldef->sidenum[side]].sector; + if (ldef->flags & ML_TWOSIDED) + li->backsector = sides[ldef->sidenum[side ^ 1]].sector; + else + li->backsector = 0; + } + + Z_Free(data); +} + + +// +// P_LoadSubsectors +// +void P_LoadSubsectors(int lump) +{ + byte* data; + int i; + mapsubsector_t* ms; + subsector_t* ss; + + numsubsectors = W_LumpLength(lump) / sizeof(mapsubsector_t); + subsectors = Z_Malloc(numsubsectors * sizeof(subsector_t), PU_LEVEL, 0); + data = W_CacheLumpNum(lump, PU_STATIC); + + ms = (mapsubsector_t*)data; + doom_memset(subsectors, 0, numsubsectors * sizeof(subsector_t)); + ss = subsectors; + + for (i = 0; i < numsubsectors; i++, ss++, ms++) + { + ss->numlines = SHORT(ms->numsegs); + ss->firstline = SHORT(ms->firstseg); + } + + Z_Free(data); +} + + +// +// P_LoadSectors +// +void P_LoadSectors(int lump) +{ + byte* data; + int i; + mapsector_t* ms; + sector_t* ss; + + numsectors = W_LumpLength(lump) / sizeof(mapsector_t); + sectors = Z_Malloc(numsectors * sizeof(sector_t), PU_LEVEL, 0); + doom_memset(sectors, 0, numsectors * sizeof(sector_t)); + data = W_CacheLumpNum(lump, PU_STATIC); + + ms = (mapsector_t*)data; + ss = sectors; + for (i = 0; i < numsectors; i++, ss++, ms++) + { + ss->floorheight = SHORT(ms->floorheight) << FRACBITS; + ss->ceilingheight = SHORT(ms->ceilingheight) << FRACBITS; + ss->floorpic = R_FlatNumForName(ms->floorpic); + ss->ceilingpic = R_FlatNumForName(ms->ceilingpic); + ss->lightlevel = SHORT(ms->lightlevel); + ss->special = SHORT(ms->special); + ss->tag = SHORT(ms->tag); + ss->thinglist = 0; + } + + Z_Free(data); +} + + +// +// P_LoadNodes +// +void P_LoadNodes(int lump) +{ + byte* data; + int i; + int j; + int k; + mapnode_t* mn; + node_t* no; + + numnodes = W_LumpLength(lump) / sizeof(mapnode_t); + nodes = Z_Malloc(numnodes * sizeof(node_t), PU_LEVEL, 0); + data = W_CacheLumpNum(lump, PU_STATIC); + + mn = (mapnode_t*)data; + no = nodes; + + for (i = 0; i < numnodes; i++, no++, mn++) + { + no->x = SHORT(mn->x) << FRACBITS; + no->y = SHORT(mn->y) << FRACBITS; + no->dx = SHORT(mn->dx) << FRACBITS; + no->dy = SHORT(mn->dy) << FRACBITS; + for (j = 0; j < 2; j++) + { + no->children[j] = SHORT(mn->children[j]); + for (k = 0; k < 4; k++) + no->bbox[j][k] = SHORT(mn->bbox[j][k]) << FRACBITS; + } + } + + Z_Free(data); +} + + +// +// P_LoadThings +// +void P_LoadThings(int lump) +{ + byte* data; + int i; + mapthing_t* mt; + int numthings; + doom_boolean spawn; + + data = W_CacheLumpNum(lump, PU_STATIC); + numthings = W_LumpLength(lump) / sizeof(mapthing_t); + + mt = (mapthing_t*)data; + for (i = 0; i < numthings; i++, mt++) + { + spawn = true; + + // Do not spawn cool, new monsters if !commercial + if (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 == false) + break; + + // Do spawn all other stuff. + mt->x = SHORT(mt->x); + mt->y = SHORT(mt->y); + mt->angle = SHORT(mt->angle); + mt->type = SHORT(mt->type); + mt->options = SHORT(mt->options); + + P_SpawnMapThing(mt); + } + + Z_Free(data); +} + + +// +// P_LoadLineDefs +// Also counts secret lines for intermissions. +// +void P_LoadLineDefs(int lump) +{ + byte* data; + int i; + maplinedef_t* mld; + line_t* ld; + vertex_t* v1; + vertex_t* v2; + + numlines = W_LumpLength(lump) / sizeof(maplinedef_t); + lines = Z_Malloc(numlines * sizeof(line_t), PU_LEVEL, 0); + doom_memset(lines, 0, numlines * sizeof(line_t)); + data = W_CacheLumpNum(lump, PU_STATIC); + + mld = (maplinedef_t*)data; + ld = lines; + for (i = 0; i < numlines; i++, mld++, ld++) + { + ld->flags = SHORT(mld->flags); + ld->special = SHORT(mld->special); + ld->tag = SHORT(mld->tag); + v1 = ld->v1 = &vertexes[SHORT(mld->v1)]; + v2 = ld->v2 = &vertexes[SHORT(mld->v2)]; + ld->dx = v2->x - v1->x; + ld->dy = v2->y - v1->y; + + if (!ld->dx) + ld->slopetype = ST_VERTICAL; + else if (!ld->dy) + ld->slopetype = ST_HORIZONTAL; + else + { + if (FixedDiv(ld->dy, ld->dx) > 0) + ld->slopetype = ST_POSITIVE; + else + ld->slopetype = ST_NEGATIVE; + } + + if (v1->x < v2->x) + { + ld->bbox[BOXLEFT] = v1->x; + ld->bbox[BOXRIGHT] = v2->x; + } + else + { + ld->bbox[BOXLEFT] = v2->x; + ld->bbox[BOXRIGHT] = v1->x; + } + + if (v1->y < v2->y) + { + ld->bbox[BOXBOTTOM] = v1->y; + ld->bbox[BOXTOP] = v2->y; + } + else + { + ld->bbox[BOXBOTTOM] = v2->y; + ld->bbox[BOXTOP] = v1->y; + } + + ld->sidenum[0] = SHORT(mld->sidenum[0]); + ld->sidenum[1] = SHORT(mld->sidenum[1]); + + if (ld->sidenum[0] != -1) + ld->frontsector = sides[ld->sidenum[0]].sector; + else + ld->frontsector = 0; + + if (ld->sidenum[1] != -1) + ld->backsector = sides[ld->sidenum[1]].sector; + else + ld->backsector = 0; + } + + Z_Free(data); +} + + +// +// P_LoadSideDefs +// +void P_LoadSideDefs(int lump) +{ + byte* data; + int i; + mapsidedef_t* msd; + side_t* sd; + + numsides = W_LumpLength(lump) / sizeof(mapsidedef_t); + sides = Z_Malloc(numsides * sizeof(side_t), PU_LEVEL, 0); + doom_memset(sides, 0, numsides * sizeof(side_t)); + data = W_CacheLumpNum(lump, PU_STATIC); + + msd = (mapsidedef_t*)data; + sd = sides; + for (i = 0; i < numsides; i++, msd++, sd++) + { + sd->textureoffset = SHORT(msd->textureoffset) << FRACBITS; + sd->rowoffset = SHORT(msd->rowoffset) << FRACBITS; + sd->toptexture = R_TextureNumForName(msd->toptexture); + sd->bottomtexture = R_TextureNumForName(msd->bottomtexture); + sd->midtexture = R_TextureNumForName(msd->midtexture); + sd->sector = §ors[SHORT(msd->sector)]; + } + + Z_Free(data); +} + + +// +// P_LoadBlockMap +// +void P_LoadBlockMap(int lump) +{ + int i; + int count; + + blockmaplump = W_CacheLumpNum(lump, PU_LEVEL); + blockmap = blockmaplump + 4; + count = W_LumpLength(lump) / 2; + + for (i = 0; i < count; i++) + blockmaplump[i] = SHORT(blockmaplump[i]); + + bmaporgx = blockmaplump[0] << FRACBITS; + bmaporgy = blockmaplump[1] << FRACBITS; + bmapwidth = blockmaplump[2]; + bmapheight = blockmaplump[3]; + + // clear out mobj chains + count = sizeof(*blocklinks) * bmapwidth * bmapheight; + blocklinks = Z_Malloc(count, PU_LEVEL, 0); + doom_memset(blocklinks, 0, count); +} + + +// +// P_GroupLines +// Builds sector line lists and subsector sector numbers. +// Finds block bounding boxes for sectors. +// +void P_GroupLines(void) +{ + line_t** linebuffer; + int i; + int j; + int total; + line_t* li; + sector_t* sector; + subsector_t* ss; + seg_t* seg; + fixed_t bbox[4]; + int block; + + // look up sector number for each subsector + ss = subsectors; + for (i = 0; i < numsubsectors; i++, ss++) + { + seg = &segs[ss->firstline]; + ss->sector = seg->sidedef->sector; + } + + // count number of lines in each sector + li = lines; + total = 0; + for (i = 0; i < numlines; i++, li++) + { + total++; + li->frontsector->linecount++; + + if (li->backsector && li->backsector != li->frontsector) + { + li->backsector->linecount++; + total++; + } + } + + // build line tables for each sector + linebuffer = Z_Malloc(total * sizeof(line_t*), PU_LEVEL, 0); + sector = sectors; + for (i = 0; i < numsectors; i++, sector++) + { + M_ClearBox(bbox); + sector->lines = linebuffer; + li = lines; + for (j = 0; j < numlines; j++, li++) + { + if (li->frontsector == sector || li->backsector == sector) + { + *linebuffer++ = li; + M_AddToBox(bbox, li->v1->x, li->v1->y); + M_AddToBox(bbox, li->v2->x, li->v2->y); + } + } + if (linebuffer - sector->lines != sector->linecount) + I_Error("Error: P_GroupLines: miscounted"); + + // set the degenmobj_t to the middle of the bounding box + sector->soundorg.x = (bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2; + sector->soundorg.y = (bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2; + + // adjust bounding box to map blocks + block = (bbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + block = block >= bmapheight ? bmapheight - 1 : block; + sector->blockbox[BOXTOP] = block; + + block = (bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXBOTTOM] = block; + + block = (bbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + block = block >= bmapwidth ? bmapwidth - 1 : block; + sector->blockbox[BOXRIGHT] = block; + + block = (bbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXLEFT] = block; + } +} + + +// +// P_SetupLevel +// +void P_SetupLevel(int episode, int map, int playermask, skill_t skill) +{ + int i; + char lumpname[9]; + int lumpnum; + + totalkills = totalitems = totalsecret = wminfo.maxfrags = 0; + wminfo.partime = 180; + for (i = 0; i < MAXPLAYERS; i++) + { + players[i].killcount = players[i].secretcount + = players[i].itemcount = 0; + } + + // Initial height of PointOfView + // will be set by player think. + players[consoleplayer].viewz = 1; + + // Make sure all sounds are stopped before Z_FreeTags. + S_Start(); + + Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1); + + // UNUSED W_Profile (); + P_InitThinkers(); + + // if working with a devlopment map, reload it + W_Reload(); + + // find map name + if (gamemode == commercial) + { + if (map < 10) + { + //doom_sprintf(lumpname, "map0%i", map); + doom_strcpy(lumpname, "map0"); + doom_concat(lumpname, doom_itoa(map, 10)); + } + else + { + //doom_sprintf(lumpname, "map%i", map); + doom_strcpy(lumpname, "map"); + doom_concat(lumpname, doom_itoa(map, 10)); + } + } + else + { + lumpname[0] = 'E'; + lumpname[1] = '0' + episode; + lumpname[2] = 'M'; + lumpname[3] = '0' + map; + lumpname[4] = 0; + } + + lumpnum = W_GetNumForName(lumpname); + + leveltime = 0; + + // note: most of this ordering is important + P_LoadBlockMap(lumpnum + ML_BLOCKMAP); + P_LoadVertexes(lumpnum + ML_VERTEXES); + P_LoadSectors(lumpnum + ML_SECTORS); + P_LoadSideDefs(lumpnum + ML_SIDEDEFS); + + P_LoadLineDefs(lumpnum + ML_LINEDEFS); + P_LoadSubsectors(lumpnum + ML_SSECTORS); + P_LoadNodes(lumpnum + ML_NODES); + P_LoadSegs(lumpnum + ML_SEGS); + + rejectmatrix = W_CacheLumpNum(lumpnum + ML_REJECT, PU_LEVEL); + P_GroupLines(); + + bodyqueslot = 0; + deathmatch_p = deathmatchstarts; + P_LoadThings(lumpnum + ML_THINGS); + + // if deathmatch, randomly spawn the active players + if (deathmatch) + { + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + { + players[i].mo = 0; + G_DeathMatchSpawnPlayer(i); + } + } + + // clear special respawning que + iquehead = iquetail = 0; + + // set up world state + P_SpawnSpecials(); + + // preload graphics + if (precache) + R_PrecacheLevel(); +} + + +// +// P_Init +// +void P_Init(void) +{ + P_InitSwitchList(); + P_InitPicAnims(); + R_InitSprites(sprnames); +} diff --git a/src/DOOM/p_setup.h b/src/DOOM/p_setup.h new file mode 100644 index 0000000..a1f6b2f --- /dev/null +++ b/src/DOOM/p_setup.h @@ -0,0 +1,39 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Setup a game, startup stuff. +// +//----------------------------------------------------------------------------- + +#ifndef __P_SETUP__ +#define __P_SETUP__ + + +// NOT called by W_Ticker. Fixme. +void P_SetupLevel(int episode, int map, int playermask, skill_t skill); + +// Called by startup code. +void P_Init(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/p_sight.c b/src/DOOM/p_sight.c new file mode 100644 index 0000000..a09764f --- /dev/null +++ b/src/DOOM/p_sight.c @@ -0,0 +1,341 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// LineOfSight/Visibility checks, uses REJECT Lookup Table. +// +//----------------------------------------------------------------------------- + +#include "doom_config.h" + +#include "doomdef.h" +#include "i_system.h" +#include "p_local.h" +#include "r_state.h" // State. + + +// +// P_CheckSight +// +fixed_t sightzstart; // eye z of looker +fixed_t topslope; +fixed_t bottomslope; // slopes to top and bottom of target + +divline_t strace; // from t1 to t2 +fixed_t t2x; +fixed_t t2y; + +int sightcounts[2]; + + +// +// P_DivlineSide +// Returns side 0 (front), 1 (back), or 2 (on). +// +int P_DivlineSide(fixed_t x, fixed_t y, divline_t* node) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!node->dx) + { + if (x == node->x) + return 2; + + if (x <= node->x) + return node->dy > 0; + + return node->dy < 0; + } + + if (!node->dy) + { + if (x == node->y) + return 2; + + if (y <= node->y) + return node->dx < 0; + + return node->dx > 0; + } + + dx = (x - node->x); + dy = (y - node->y); + + left = (node->dy >> FRACBITS) * (dx >> FRACBITS); + right = (dy >> FRACBITS) * (node->dx >> FRACBITS); + + if (right < left) + return 0; // front side + + if (left == right) + return 2; + return 1; // back side +} + + +// +// P_InterceptVector2 +// Returns the fractional intercept point +// along the first divline. +// This is only called by the addthings and addlines traversers. +// +fixed_t P_InterceptVector2(divline_t* v2, divline_t* v1) +{ + fixed_t frac; + fixed_t num; + fixed_t den; + + den = FixedMul(v1->dy >> 8, v2->dx) - FixedMul(v1->dx >> 8, v2->dy); + + if (den == 0) + return 0; + + num = FixedMul((v1->x - v2->x) >> 8, v1->dy) + + FixedMul((v2->y - v1->y) >> 8, v1->dx); + frac = FixedDiv(num, den); + + return frac; +} + + +// +// P_CrossSubsector +// Returns true +// if strace crosses the given subsector successfully. +// +doom_boolean P_CrossSubsector(int num) +{ + seg_t* seg; + line_t* line; + int s1; + int s2; + int count; + subsector_t* sub; + sector_t* front; + sector_t* back; + fixed_t opentop; + fixed_t openbottom; + divline_t divl; + vertex_t* v1; + vertex_t* v2; + fixed_t frac; + fixed_t slope; + +#ifdef RANGECHECK + if (num >= numsubsectors) + { + //I_Error("Error: P_CrossSubsector: ss %i with numss = %i", + // num, + // numsubsectors); + + doom_strcpy(error_buf, "Error: P_CrossSubsector: ss "); + doom_concat(error_buf, doom_itoa(num, 10)); + doom_concat(error_buf, " with numss = "); + doom_concat(error_buf, doom_itoa(numsubsectors, 10)); + I_Error(error_buf); + } +#endif + + sub = &subsectors[num]; + + // check lines + count = sub->numlines; + seg = &segs[sub->firstline]; + + for (; count; seg++, count--) + { + line = seg->linedef; + + // allready checked other side? + if (line->validcount == validcount) + continue; + + line->validcount = validcount; + + v1 = line->v1; + v2 = line->v2; + s1 = P_DivlineSide(v1->x, v1->y, &strace); + s2 = P_DivlineSide(v2->x, v2->y, &strace); + + // line isn't crossed? + if (s1 == s2) + continue; + + divl.x = v1->x; + divl.y = v1->y; + divl.dx = v2->x - v1->x; + divl.dy = v2->y - v1->y; + s1 = P_DivlineSide(strace.x, strace.y, &divl); + s2 = P_DivlineSide(t2x, t2y, &divl); + + // line isn't crossed? + if (s1 == s2) + continue; + + // stop because it is not two sided anyway + // might do this after updating validcount? + if (!(line->flags & ML_TWOSIDED)) + return false; + + // crosses a two sided line + front = seg->frontsector; + 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 + if (front->ceilingheight < back->ceilingheight) + opentop = front->ceilingheight; + else + opentop = back->ceilingheight; + + // because of ceiling height differences + if (front->floorheight > back->floorheight) + openbottom = front->floorheight; + else + openbottom = back->floorheight; + + // quick test for totally closed doors + if (openbottom >= opentop) + return false; // stop + + frac = P_InterceptVector2(&strace, &divl); + + if (front->floorheight != back->floorheight) + { + slope = FixedDiv(openbottom - sightzstart, frac); + if (slope > bottomslope) + bottomslope = slope; + } + + if (front->ceilingheight != back->ceilingheight) + { + slope = FixedDiv(opentop - sightzstart, frac); + if (slope < topslope) + topslope = slope; + } + + if (topslope <= bottomslope) + return false; // stop + } + + // passed the subsector ok + return true; +} + + +// +// P_CrossBSPNode +// Returns true +// if strace crosses the given node successfully. +// +doom_boolean P_CrossBSPNode(int bspnum) +{ + node_t* bsp; + int side; + + if (bspnum & NF_SUBSECTOR) + { + if (bspnum == -1) + return P_CrossSubsector(0); + else + return P_CrossSubsector(bspnum & (~NF_SUBSECTOR)); + } + + bsp = &nodes[bspnum]; + + // decide which side the start point is on + side = P_DivlineSide(strace.x, strace.y, (divline_t*)bsp); + if (side == 2) + side = 0; // an "on" should cross both sides + + // cross the starting side + if (!P_CrossBSPNode(bsp->children[side])) + return false; + + // the partition plane is crossed here + if (side == P_DivlineSide(t2x, t2y, (divline_t*)bsp)) + { + // the line doesn't touch the other side + return true; + } + + // cross the ending side + return P_CrossBSPNode(bsp->children[side ^ 1]); +} + + +// +// P_CheckSight +// Returns true +// if a straight line between t1 and t2 is unobstructed. +// Uses REJECT. +// +doom_boolean P_CheckSight(mobj_t* t1, mobj_t* t2) +{ + int s1; + int s2; + int pnum; + int bytenum; + int bitnum; + + // First check for trivial rejection. + + // Determine subsector entries in REJECT table. + s1 = (int)(t1->subsector->sector - sectors); + s2 = (int)(t2->subsector->sector - sectors); + pnum = s1 * numsectors + s2; + bytenum = pnum >> 3; + bitnum = 1 << (pnum & 7); + + // Check in REJECT table. + if (rejectmatrix[bytenum] & bitnum) + { + sightcounts[0]++; + + // can't possibly be connected + return false; + } + + // An unobstructed LOS is possible. + // Now look from eyes of t1 to any part of t2. + sightcounts[1]++; + + validcount++; + + sightzstart = t1->z + t1->height - (t1->height >> 2); + topslope = (t2->z + t2->height) - sightzstart; + bottomslope = (t2->z) - sightzstart; + + strace.x = t1->x; + strace.y = t1->y; + t2x = t2->x; + t2y = t2->y; + strace.dx = t2->x - t1->x; + strace.dy = t2->y - t1->y; + + // the head node is the last node output + return P_CrossBSPNode(numnodes - 1); +} diff --git a/src/DOOM/p_spec.c b/src/DOOM/p_spec.c new file mode 100644 index 0000000..12cf838 --- /dev/null +++ b/src/DOOM/p_spec.c @@ -0,0 +1,1313 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Implements special effects: +// Texture animation, height or lighting changes +// according to adjacent sectors, respective +// utility functions, etc. +// Line Tag handling. Line and Sector triggers. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "doomdef.h" +#include "doomstat.h" +#include "i_system.h" +#include "z_zone.h" +#include "m_argv.h" +#include "m_random.h" +#include "w_wad.h" +#include "r_local.h" +#include "p_local.h" +#include "g_game.h" +#include "s_sound.h" +#include "r_state.h" // State. +#include "sounds.h" // Data. + + +#define MAXANIMS 32 +#define MAXLINEANIMS 64 + +// 20 adjoining sectors max! [dsl] Useless comment is useless +#define MAX_ADJOINING_SECTORS 20 + + +// +// Animating textures and planes +// There is another anim_t used in wi_stuff, unrelated. +// +typedef struct +{ + doom_boolean istexture; + int picnum; + int basepic; + int numpics; + int speed; +} anim_t; + +// +// source animation definition +// +typedef struct +{ + doom_boolean istexture; // if false, it is a flat + char endname[9]; + char startname[9]; + int speed; +} animdef_t; + + +doom_boolean levelTimer; +int levelTimeCount; +short numlinespecials; +line_t* linespeciallist[MAXLINEANIMS]; + + +extern anim_t anims[MAXANIMS]; +extern anim_t* lastanim; + + +// +// P_InitPicAnims +// + +// Floor/ceiling animation sequences, +// defined by first and last frame, +// i.e. the flat (64x64 tile) name to +// be used. +// The full animation sequence is given +// using all the flats between the start +// and end entry, in the order found in +// the WAD file. +// +animdef_t animdefs[] = +{ + {false, "NUKAGE3", "NUKAGE1", 8}, + {false, "FWATER4", "FWATER1", 8}, + {false, "SWATER4", "SWATER1", 8}, + {false, "LAVA4", "LAVA1", 8}, + {false, "BLOOD3", "BLOOD1", 8}, + + // DOOM II flat animations. + {false, "RROCK08", "RROCK05", 8}, + {false, "SLIME04", "SLIME01", 8}, + {false, "SLIME08", "SLIME05", 8}, + {false, "SLIME12", "SLIME09", 8}, + + {true, "BLODGR4", "BLODGR1", 8}, + {true, "SLADRIP3", "SLADRIP1", 8}, + + {true, "BLODRIP4", "BLODRIP1", 8}, + {true, "FIREWALL", "FIREWALA", 8}, + {true, "GSTFONT3", "GSTFONT1", 8}, + {true, "FIRELAVA", "FIRELAV3", 8}, + {true, "FIREMAG3", "FIREMAG1", 8}, + {true, "FIREBLU2", "FIREBLU1", 8}, + {true, "ROCKRED3", "ROCKRED1", 8}, + + {true, "BFALL4", "BFALL1", 8}, + {true, "SFALL4", "SFALL1", 8}, + {true, "WFALL4", "WFALL1", 8}, + {true, "DBRAIN4", "DBRAIN1", 8}, + + {-1} +}; + +anim_t anims[MAXANIMS]; +anim_t* lastanim; + + +// +// Animating line specials +// + +extern short numlinespecials; +extern line_t* linespeciallist[MAXLINEANIMS]; + + +void P_InitPicAnims(void) +{ + int i; + + // Init animation + lastanim = anims; + for (i = 0; animdefs[i].istexture != (doom_boolean)-1; i++) + { + if (animdefs[i].istexture) + { + // different episode ? + if (R_CheckTextureNumForName(animdefs[i].startname) == -1) + continue; + + lastanim->picnum = R_TextureNumForName(animdefs[i].endname); + lastanim->basepic = R_TextureNumForName(animdefs[i].startname); + } + else + { + if (W_CheckNumForName(animdefs[i].startname) == -1) + continue; + + lastanim->picnum = R_FlatNumForName(animdefs[i].endname); + lastanim->basepic = R_FlatNumForName(animdefs[i].startname); + } + + lastanim->istexture = animdefs[i].istexture; + lastanim->numpics = lastanim->picnum - lastanim->basepic + 1; + + if (lastanim->numpics < 2) + { + //I_Error("Error: P_InitPicAnims: bad cycle from %s to %s", + // animdefs[i].startname, + // animdefs[i].endname); + + doom_strcpy(error_buf, "Error: P_InitPicAnims: bad cycle from "); + doom_concat(error_buf, animdefs[i].startname); + doom_concat(error_buf, " to "); + doom_concat(error_buf, animdefs[i].endname); + I_Error(error_buf); + } + + lastanim->speed = animdefs[i].speed; + lastanim++; + } +} + + +// +// UTILITIES +// + +// +// getSide() +// Will return a side_t* +// given the number of the current sector, +// the line number, and the side (0/1) that you want. +// +side_t* getSide(int currentSector, int line, int side) +{ + return &sides[(sectors[currentSector].lines[line])->sidenum[side]]; +} + + +// +// getSector() +// Will return a sector_t* +// given the number of the current sector, +// the line number and the side (0/1) that you want. +// +sector_t* getSector(int currentSector, int line, int side) +{ + return sides[(sectors[currentSector].lines[line])->sidenum[side]].sector; +} + + +// +// twoSided() +// Given the sector number and the line number, +// it will tell you whether the line is two-sided or not. +// +int twoSided(int sector, int line) +{ + return (sectors[sector].lines[line])->flags & ML_TWOSIDED; +} + + +// +// getNextSector() +// Return sector_t * of sector next to current. +// 0 if not two-sided line +// +sector_t* getNextSector(line_t* line, sector_t* sec) +{ + if (!(line->flags & ML_TWOSIDED)) + return 0; + + if (line->frontsector == sec) + return line->backsector; + + return line->frontsector; +} + + +// +// P_FindLowestFloorSurrounding() +// FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS +// +fixed_t P_FindLowestFloorSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = sec->floorheight; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = getNextSector(check, sec); + + if (!other) + continue; + + if (other->floorheight < floor) + floor = other->floorheight; + } + + return floor; +} + + +// +// P_FindHighestFloorSurrounding() +// FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS +// +fixed_t P_FindHighestFloorSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = -500 * FRACUNIT; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = getNextSector(check, sec); + + if (!other) + continue; + + if (other->floorheight > floor) + floor = other->floorheight; + } + + return floor; +} + + +// +// P_FindNextHighestFloor +// FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS +// Note: this should be doable w/o a fixed array. +fixed_t P_FindNextHighestFloor(sector_t* sec, int currentheight) +{ + int i; + int h; + int min; + line_t* check; + sector_t* other; + fixed_t height = currentheight; + + fixed_t heightlist[MAX_ADJOINING_SECTORS]; + + for (i = 0, h = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = getNextSector(check, sec); + + if (!other) + continue; + + if (other->floorheight > height) + heightlist[h++] = other->floorheight; + + // Check for overflow. Exit. + if (h >= MAX_ADJOINING_SECTORS) + { + doom_print( + "Sector with more than 20 adjoining sectors\n"); + break; + } + } + + // Find lowest height in list + if (!h) + return currentheight; + + min = heightlist[0]; + + // Range checking? + for (i = 1; i < h; i++) + if (heightlist[i] < min) + min = heightlist[i]; + + return min; +} + + +// +// FIND LOWEST CEILING IN THE SURROUNDING SECTORS +// +fixed_t P_FindLowestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = DOOM_MAXINT; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = getNextSector(check, sec); + + if (!other) + continue; + + if (other->ceilingheight < height) + height = other->ceilingheight; + } + + return height; +} + + +// +// FIND HIGHEST CEILING IN THE SURROUNDING SECTORS +// +fixed_t P_FindHighestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = 0; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = getNextSector(check, sec); + + if (!other) + continue; + + if (other->ceilingheight > height) + height = other->ceilingheight; + } + + return height; +} + + +// +// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO +// +int P_FindSectorFromLineTag(line_t* line, int start) +{ + int i; + + for (i = start + 1; i < numsectors; i++) + if (sectors[i].tag == line->tag) + return i; + + return -1; +} + + +// +// Find minimum light from an adjacent sector +// +int P_FindMinSurroundingLight(sector_t* sector, int max) +{ + int i; + int min; + line_t* line; + sector_t* check; + + min = max; + for (i = 0; i < sector->linecount; i++) + { + line = sector->lines[i]; + check = getNextSector(line, sector); + + if (!check) + continue; + + if (check->lightlevel < min) + min = check->lightlevel; + } + + return min; +} + + +// +// EVENTS +// Events are operations triggered by using, crossing, +// or shooting special lines, or by timed thinkers. +// + +// +// P_CrossSpecialLine - TRIGGER +// Called every time a thing origin is about +// to cross a line with a non 0 special. +// +void P_CrossSpecialLine(int linenum, int side, mobj_t* thing) +{ + line_t* line; + int ok; + + line = &lines[linenum]; + + // Triggers that other things can activate + if (!thing->player) + { + // Things that should NOT trigger specials... + switch (thing->type) + { + case MT_ROCKET: + case MT_PLASMA: + case MT_BFG: + case MT_TROOPSHOT: + case MT_HEADSHOT: + case MT_BRUISERSHOT: + return; + break; + + default: break; + } + + ok = 0; + switch (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 = 1; + break; + } + if (!ok) + return; + } + + // Note: could use some const's here. + switch (line->special) + { + // TRIGGERS. + // All from here to RETRIGGERS. + case 2: + // Open Door + EV_DoDoor(line, door_open); + line->special = 0; + break; + + case 3: + // Close Door + EV_DoDoor(line, door_close); + line->special = 0; + break; + + case 4: + // Raise Door + EV_DoDoor(line, door_normal); + line->special = 0; + break; + + case 5: + // Raise Floor + EV_DoFloor(line, raiseFloor); + line->special = 0; + break; + + case 6: + // Fast Ceiling Crush & Raise + EV_DoCeiling(line, fastCrushAndRaise); + line->special = 0; + break; + + case 8: + // Build Stairs + EV_BuildStairs(line, build8); + line->special = 0; + break; + + case 10: + // PlatDownWaitUp + EV_DoPlat(line, downWaitUpStay, 0); + line->special = 0; + break; + + case 12: + // Light Turn On - brightest near + EV_LightTurnOn(line, 0); + line->special = 0; + break; + + case 13: + // Light Turn On 255 + EV_LightTurnOn(line, 255); + line->special = 0; + break; + + case 16: + // Close Door 30 + EV_DoDoor(line, close30ThenOpen); + line->special = 0; + break; + + case 17: + // Start Light Strobing + EV_StartLightStrobing(line); + line->special = 0; + break; + + case 19: + // Lower Floor + EV_DoFloor(line, lowerFloor); + line->special = 0; + break; + + case 22: + // Raise floor to nearest height and change texture + EV_DoPlat(line, raiseToNearestAndChange, 0); + line->special = 0; + break; + + case 25: + // Ceiling Crush and Raise + EV_DoCeiling(line, crushAndRaise); + line->special = 0; + break; + + case 30: + // Raise floor to shortest texture height + // on either side of lines. + EV_DoFloor(line, raiseToTexture); + line->special = 0; + break; + + case 35: + // Lights Very Dark + EV_LightTurnOn(line, 35); + line->special = 0; + break; + + case 36: + // Lower Floor (TURBO) + EV_DoFloor(line, turboLower); + line->special = 0; + break; + + case 37: + // LowerAndChange + EV_DoFloor(line, lowerAndChange); + line->special = 0; + break; + + case 38: + // Lower Floor To Lowest + EV_DoFloor(line, lowerFloorToLowest); + line->special = 0; + break; + + case 39: + // TELEPORT! + EV_Teleport(line, side, thing); + line->special = 0; + break; + + case 40: + // RaiseCeilingLowerFloor + EV_DoCeiling(line, raiseToHighest); + EV_DoFloor(line, lowerFloorToLowest); + line->special = 0; + break; + + case 44: + // Ceiling Crush + EV_DoCeiling(line, lowerAndCrush); + line->special = 0; + break; + + case 52: + // EXIT! + G_ExitLevel(); + break; + + case 53: + // Perpetual Platform Raise + EV_DoPlat(line, perpetualRaise, 0); + line->special = 0; + break; + + case 54: + // Platform Stop + EV_StopPlat(line); + line->special = 0; + break; + + case 56: + // Raise Floor Crush + EV_DoFloor(line, raiseFloorCrush); + line->special = 0; + break; + + case 57: + // Ceiling Crush Stop + EV_CeilingCrushStop(line); + line->special = 0; + break; + + case 58: + // Raise Floor 24 + EV_DoFloor(line, raiseFloor24); + line->special = 0; + break; + + case 59: + // Raise Floor 24 And Change + EV_DoFloor(line, raiseFloor24AndChange); + line->special = 0; + break; + + case 104: + // Turn lights off in sector(tag) + EV_TurnTagLightsOff(line); + line->special = 0; + break; + + case 108: + // Blazing Door Raise (faster than TURBO!) + EV_DoDoor(line, blazeRaise); + line->special = 0; + break; + + case 109: + // Blazing Door Open (faster than TURBO!) + EV_DoDoor(line, blazeOpen); + line->special = 0; + break; + + case 100: + // Build Stairs Turbo 16 + EV_BuildStairs(line, turbo16); + line->special = 0; + break; + + case 110: + // Blazing Door Close (faster than TURBO!) + EV_DoDoor(line, blazeClose); + line->special = 0; + break; + + case 119: + // Raise floor to nearest surr. floor + EV_DoFloor(line, raiseFloorToNearest); + line->special = 0; + break; + + case 121: + // Blazing PlatDownWaitUpStay + EV_DoPlat(line, blazeDWUS, 0); + line->special = 0; + break; + + case 124: + // Secret EXIT + G_SecretExitLevel(); + break; + + case 125: + // TELEPORT MonsterONLY + if (!thing->player) + { + EV_Teleport(line, side, thing); + line->special = 0; + } + break; + + case 130: + // Raise Floor Turbo + EV_DoFloor(line, raiseFloorTurbo); + line->special = 0; + break; + + case 141: + // Silent Ceiling Crush & Raise + EV_DoCeiling(line, silentCrushAndRaise); + line->special = 0; + break; + + // RETRIGGERS. All from here till end. + case 72: + // Ceiling Crush + EV_DoCeiling(line, lowerAndCrush); + break; + + case 73: + // Ceiling Crush and Raise + EV_DoCeiling(line, crushAndRaise); + break; + + case 74: + // Ceiling Crush Stop + EV_CeilingCrushStop(line); + break; + + case 75: + // Close Door + EV_DoDoor(line, door_close); + break; + + case 76: + // Close Door 30 + EV_DoDoor(line, close30ThenOpen); + break; + + case 77: + // Fast Ceiling Crush & Raise + EV_DoCeiling(line, fastCrushAndRaise); + break; + + case 79: + // Lights Very Dark + EV_LightTurnOn(line, 35); + break; + + case 80: + // Light Turn On - brightest near + EV_LightTurnOn(line, 0); + break; + + case 81: + // Light Turn On 255 + EV_LightTurnOn(line, 255); + break; + + case 82: + // Lower Floor To Lowest + EV_DoFloor(line, lowerFloorToLowest); + break; + + case 83: + // Lower Floor + EV_DoFloor(line, lowerFloor); + break; + + case 84: + // LowerAndChange + EV_DoFloor(line, lowerAndChange); + break; + + case 86: + // Open Door + EV_DoDoor(line, door_open); + break; + + case 87: + // Perpetual Platform Raise + EV_DoPlat(line, perpetualRaise, 0); + break; + + case 88: + // PlatDownWaitUp + EV_DoPlat(line, downWaitUpStay, 0); + break; + + case 89: + // Platform Stop + EV_StopPlat(line); + break; + + case 90: + // Raise Door + EV_DoDoor(line, door_normal); + break; + + case 91: + // Raise Floor + EV_DoFloor(line, raiseFloor); + break; + + case 92: + // Raise Floor 24 + EV_DoFloor(line, raiseFloor24); + break; + + case 93: + // Raise Floor 24 And Change + EV_DoFloor(line, raiseFloor24AndChange); + break; + + case 94: + // Raise Floor Crush + EV_DoFloor(line, raiseFloorCrush); + break; + + case 95: + // Raise floor to nearest height + // and change texture. + EV_DoPlat(line, raiseToNearestAndChange, 0); + break; + + case 96: + // Raise floor to shortest texture height + // on either side of lines. + EV_DoFloor(line, raiseToTexture); + break; + + case 97: + // TELEPORT! + EV_Teleport(line, side, thing); + break; + + case 98: + // Lower Floor (TURBO) + EV_DoFloor(line, turboLower); + break; + + case 105: + // Blazing Door Raise (faster than TURBO!) + EV_DoDoor(line, blazeRaise); + break; + + case 106: + // Blazing Door Open (faster than TURBO!) + EV_DoDoor(line, blazeOpen); + break; + + case 107: + // Blazing Door Close (faster than TURBO!) + EV_DoDoor(line, blazeClose); + break; + + case 120: + // Blazing PlatDownWaitUpStay. + EV_DoPlat(line, blazeDWUS, 0); + break; + + case 126: + // TELEPORT MonsterONLY. + if (!thing->player) + EV_Teleport(line, side, thing); + break; + + case 128: + // Raise To Nearest Floor + EV_DoFloor(line, raiseFloorToNearest); + break; + + case 129: + // Raise Floor Turbo + EV_DoFloor(line, raiseFloorTurbo); + break; + } +} + + +// +// P_ShootSpecialLine - IMPACT SPECIALS +// Called when a thing shoots a special line. +// +void P_ShootSpecialLine(mobj_t* thing, line_t* line) +{ + int ok; + + // Impacts that other things can activate. + if (!thing->player) + { + ok = 0; + switch (line->special) + { + case 46: + // OPEN DOOR IMPACT + ok = 1; + break; + } + if (!ok) + return; + } + + switch (line->special) + { + case 24: + // RAISE FLOOR + EV_DoFloor(line, raiseFloor); + P_ChangeSwitchTexture(line, 0); + break; + + case 46: + // OPEN DOOR + EV_DoDoor(line, door_open); + P_ChangeSwitchTexture(line, 1); + break; + + case 47: + // RAISE FLOOR NEAR AND CHANGE + EV_DoPlat(line, raiseToNearestAndChange, 0); + P_ChangeSwitchTexture(line, 0); + break; + } +} + + +// +// P_PlayerInSpecialSector +// Called every tic frame +// that the player origin is in a special sector +// +void P_PlayerInSpecialSector(player_t* player) +{ + sector_t* sector; + + sector = player->mo->subsector->sector; + + // Falling, not all the way down yet? + if (player->mo->z != sector->floorheight) + return; + + // Has hitten ground. + switch (sector->special) + { + case 5: + // HELLSLIME DAMAGE + if (!player->powers[pw_ironfeet]) + if (!(leveltime & 0x1f)) + P_DamageMobj(player->mo, 0, 0, 10); + break; + + case 7: + // NUKAGE DAMAGE + if (!player->powers[pw_ironfeet]) + if (!(leveltime & 0x1f)) + P_DamageMobj(player->mo, 0, 0, 5); + break; + + case 16: + // SUPER HELLSLIME DAMAGE + case 4: + // STROBE HURT + if (!player->powers[pw_ironfeet] + || (P_Random() < 5)) + { + if (!(leveltime & 0x1f)) + P_DamageMobj(player->mo, 0, 0, 20); + } + break; + + case 9: + // SECRET SECTOR + player->secretcount++; + sector->special = 0; + break; + + case 11: + // EXIT SUPER DAMAGE! (for E1M8 finale) + player->cheats &= ~CF_GODMODE; + + if (!(leveltime & 0x1f)) + P_DamageMobj(player->mo, 0, 0, 20); + + if (player->health <= 10) + G_ExitLevel(); + break; + + default: + { + //I_Error("Error: P_PlayerInSpecialSector: " + // "unknown special %i", + // sector->special); + + + doom_strcpy(error_buf, "Error: P_PlayerInSpecialSector: unknown special "); + doom_concat(error_buf, doom_itoa(sector->special, 10)); + I_Error(error_buf); + break; + } + }; +} + + +// +// P_UpdateSpecials +// Animate planes, scroll walls, etc. +// +void P_UpdateSpecials(void) +{ + anim_t* anim; + int pic; + int i; + line_t* line; + + // LEVEL TIMER + if (levelTimer == true) + { + levelTimeCount--; + if (!levelTimeCount) + G_ExitLevel(); + } + + // ANIMATE FLATS AND TEXTURES GLOBALLY + for (anim = anims; anim < lastanim; anim++) + { + for (i = anim->basepic; i < anim->basepic + anim->numpics; i++) + { + pic = anim->basepic + ((leveltime / anim->speed + i) % anim->numpics); + if (anim->istexture) + texturetranslation[i] = pic; + else + flattranslation[i] = pic; + } + } + + // ANIMATE LINE SPECIALS + for (i = 0; i < numlinespecials; i++) + { + line = linespeciallist[i]; + switch (line->special) + { + case 48: + // EFFECT FIRSTCOL SCROLL + + sides[line->sidenum[0]].textureoffset += FRACUNIT; + break; + } + } + + // DO BUTTONS + for (i = 0; i < MAXBUTTONS; i++) + if (buttonlist[i].btimer) + { + buttonlist[i].btimer--; + if (!buttonlist[i].btimer) + { + switch (buttonlist[i].where) + { + case top: + sides[buttonlist[i].line->sidenum[0]].toptexture = + buttonlist[i].btexture; + break; + + case middle: + sides[buttonlist[i].line->sidenum[0]].midtexture = + buttonlist[i].btexture; + break; + + case bottom: + sides[buttonlist[i].line->sidenum[0]].bottomtexture = + buttonlist[i].btexture; + break; + } + S_StartSound((mobj_t*)&buttonlist[i].soundorg, sfx_swtchn); + doom_memset(&buttonlist[i], 0, sizeof(button_t)); + } + } +} + + +// +// Special Stuff that can not be categorized +// +int EV_DoDonut(line_t* line) +{ + sector_t* s1; + sector_t* s2; + sector_t* s3; + int secnum; + int rtn; + int i; + floormove_t* floor; + + secnum = -1; + rtn = 0; + while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) + { + s1 = §ors[secnum]; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (s1->specialdata) + continue; + + rtn = 1; + s2 = getNextSector(s1->lines[0], s1); + for (i = 0; i < s2->linecount; i++) + { + if ((!(s2->lines[i]->flags & ML_TWOSIDED)) || + (s2->lines[i]->backsector == s1)) + continue; + s3 = s2->lines[i]->backsector; + + // Spawn rising slime + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + P_AddThinker(&floor->thinker); + s2->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + floor->type = donutRaise; + floor->crush = false; + floor->direction = 1; + floor->sector = s2; + floor->speed = FLOORSPEED / 2; + floor->texture = s3->floorpic; + floor->newspecial = 0; + floor->floordestheight = s3->floorheight; + + // Spawn lowering donut-hole + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + P_AddThinker(&floor->thinker); + s1->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; + floor->type = lowerFloor; + floor->crush = false; + floor->direction = -1; + floor->sector = s1; + floor->speed = FLOORSPEED / 2; + floor->floordestheight = s3->floorheight; + break; + } + } + + return rtn; +} + + +// +// SPECIAL SPAWNING +// + +// +// P_SpawnSpecials +// After the map has been loaded, scan for specials +// that spawn thinkers +// + +// Parses command line parameters. +void P_SpawnSpecials(void) +{ + sector_t* sector; + int i; + int episode; + + episode = 1; + if (W_CheckNumForName("texture2") >= 0) + episode = 2; + + + // See if -TIMER needs to be used. + levelTimer = false; + + i = M_CheckParm("-avg"); + if (i && deathmatch) + { + levelTimer = true; + levelTimeCount = 20 * 60 * 35; + } + + i = M_CheckParm("-timer"); + if (i && deathmatch) + { + int time; + time = doom_atoi(myargv[i + 1]) * 60 * 35; + levelTimer = true; + levelTimeCount = time; + } + + // Init special SECTORs. + sector = sectors; + for (i = 0; i < numsectors; i++, sector++) + { + if (!sector->special) + continue; + + switch (sector->special) + { + case 1: + // FLICKERING LIGHTS + P_SpawnLightFlash(sector); + break; + + case 2: + // STROBE FAST + P_SpawnStrobeFlash(sector, FASTDARK, 0); + break; + + case 3: + // STROBE SLOW + P_SpawnStrobeFlash(sector, SLOWDARK, 0); + break; + + case 4: + // STROBE FAST/DEATH SLIME + P_SpawnStrobeFlash(sector, FASTDARK, 0); + sector->special = 4; + break; + + case 8: + // GLOWING LIGHT + P_SpawnGlowingLight(sector); + break; + case 9: + // SECRET SECTOR + totalsecret++; + break; + + case 10: + // DOOR CLOSE IN 30 SECONDS + P_SpawnDoorCloseIn30(sector); + break; + + case 12: + // SYNC STROBE SLOW + P_SpawnStrobeFlash(sector, SLOWDARK, 1); + break; + + case 13: + // SYNC STROBE FAST + P_SpawnStrobeFlash(sector, FASTDARK, 1); + break; + + case 14: + // DOOR RAISE IN 5 MINUTES + P_SpawnDoorRaiseIn5Mins(sector, i); + break; + + case 17: + P_SpawnFireFlicker(sector); + break; + } + } + + + // Init line EFFECTs + numlinespecials = 0; + for (i = 0; i < numlines; i++) + { + switch (lines[i].special) + { + case 48: + // EFFECT FIRSTCOL SCROLL+ + linespeciallist[numlinespecials] = &lines[i]; + numlinespecials++; + break; + } + } + + + // Init other misc stuff + for (i = 0; i < MAXCEILINGS; i++) + activeceilings[i] = 0; + + for (i = 0; i < MAXPLATS; i++) + activeplats[i] = 0; + + for (i = 0; i < MAXBUTTONS; i++) + doom_memset(&buttonlist[i], 0, sizeof(button_t)); +} diff --git a/src/DOOM/p_spec.h b/src/DOOM/p_spec.h new file mode 100644 index 0000000..331cfe3 --- /dev/null +++ b/src/DOOM/p_spec.h @@ -0,0 +1,429 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: none +// Implements special effects: +// Texture animation, height or lighting changes +// according to adjacent sectors, respective +// utility functions, etc. +// +//----------------------------------------------------------------------------- + +#ifndef __P_SPEC__ +#define __P_SPEC__ + + +#include "p_mobj.h" +#include "r_defs.h" + + +// +// End-level timer (-TIMER option) +// +extern doom_boolean levelTimer; +extern int levelTimeCount; + + +// Define values for map objects +#define MO_TELEPORTMAN 14 + + +// at game start +void P_InitPicAnims(void); + +// at map load +void P_SpawnSpecials(void); + +// every tic +void P_UpdateSpecials(void); + +// when needed +doom_boolean P_UseSpecialLine(mobj_t* thing, line_t* line, int side); + +void P_ShootSpecialLine(mobj_t* thing, line_t* line); +void P_CrossSpecialLine(int linenum, int side, mobj_t* thing); +void P_PlayerInSpecialSector(player_t* player); +int twoSided(int sector, int line); +sector_t* getSector(int currentSector, int line, int side); +side_t* getSide(int currentSector, int line, int side); +fixed_t P_FindLowestFloorSurrounding(sector_t* sec); +fixed_t P_FindHighestFloorSurrounding(sector_t* sec); +fixed_t P_FindNextHighestFloor(sector_t* sec, int currentheight); +fixed_t P_FindLowestCeilingSurrounding(sector_t* sec); +fixed_t P_FindHighestCeilingSurrounding(sector_t* sec); +int P_FindSectorFromLineTag(line_t* line, int start); +int P_FindMinSurroundingLight(sector_t* sector, int max); +sector_t* getNextSector(line_t* line, sector_t* sec); + +// +// SPECIAL +// +int EV_DoDonut(line_t* line); + + +// +// P_LIGHTS +// +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int maxlight; + int minlight; +} fireflicker_t; + + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int maxlight; + int minlight; + int maxtime; + int mintime; +} lightflash_t; + + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int minlight; + int maxlight; + int darktime; + int brighttime; +} strobe_t; + + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int minlight; + int maxlight; + int direction; +} glow_t; + + +#define GLOWSPEED 8 +#define STROBEBRIGHT 5 +#define FASTDARK 15 +#define SLOWDARK 35 + +void P_SpawnFireFlicker(sector_t* sector); +void T_LightFlash(lightflash_t* flash); +void P_SpawnLightFlash(sector_t* sector); +void T_StrobeFlash(strobe_t* flash); + +void P_SpawnStrobeFlash(sector_t* sector, int fastOrSlow, int inSync); +void EV_StartLightStrobing(line_t* line); +void EV_TurnTagLightsOff(line_t* line); + +void EV_LightTurnOn(line_t* line, int bright); + +void T_Glow(glow_t* g); +void P_SpawnGlowingLight(sector_t* sector); + + +// +// P_SWITCH +// +typedef struct +{ + char name1[9]; + char name2[9]; + short episode; +} switchlist_t; + + +typedef enum +{ + top, + middle, + bottom +} bwhere_e; + + +typedef struct +{ + line_t* line; + bwhere_e where; + int btexture; + int btimer; + mobj_t* soundorg; +} button_t; + + +// max # of wall switches in a level +#define MAXSWITCHES 50 + +// 4 players, 4 buttons each at once, max. +#define MAXBUTTONS 16 + +// 1 second, in ticks. +#define BUTTONTIME 35 + + +extern button_t buttonlist[MAXBUTTONS]; + + +void P_ChangeSwitchTexture(line_t* line, int useAgain); +void P_InitSwitchList(void); + + +// +// P_PLATS +// +typedef enum +{ + up, + down, + waiting, + in_stasis +} plat_e; + + +typedef enum +{ + perpetualRaise, + downWaitUpStay, + raiseAndChange, + raiseToNearestAndChange, + blazeDWUS +} plattype_e; + + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + fixed_t speed; + fixed_t low; + fixed_t high; + int wait; + int count; + plat_e status; + plat_e oldstatus; + doom_boolean crush; + int tag; + plattype_e type; +} plat_t; + + +#define PLATWAIT 3 +#define PLATSPEED FRACUNIT +#define MAXPLATS 30 + + +extern plat_t* activeplats[MAXPLATS]; + + +void T_PlatRaise(plat_t* plat); +int EV_DoPlat(line_t* line, plattype_e type, int amount); +void P_AddActivePlat(plat_t* plat); +void P_RemoveActivePlat(plat_t* plat); +void EV_StopPlat(line_t* line); +void P_ActivateInStasis(int tag); + + +// +// P_DOORS +// +typedef enum +{ + door_normal, + close30ThenOpen, + door_close, + door_open, + raiseIn5Mins, + blazeRaise, + blazeOpen, + blazeClose +} vldoor_e; + + +typedef struct +{ + thinker_t thinker; + vldoor_e type; + sector_t* sector; + fixed_t topheight; + fixed_t speed; + + // 1 = up, 0 = waiting at top, -1 = down + int direction; + + // tics to wait at the top + int topwait; + + // (keep in case a door going down is reset) + // when it reaches 0, start going down + int topcountdown; +} vldoor_t; + + +#define VDOORSPEED FRACUNIT*2 +#define VDOORWAIT 150 + + +void EV_VerticalDoor(line_t* line, mobj_t* thing); +int EV_DoDoor(line_t* line, vldoor_e type); +int EV_DoLockedDoor(line_t* line, vldoor_e type, mobj_t* thing); +void T_VerticalDoor(vldoor_t* door); +void P_SpawnDoorCloseIn30(sector_t* sec); +void P_SpawnDoorRaiseIn5Mins(sector_t* sec, int secnum); + + +// +// P_CEILNG +// +typedef enum +{ + lowerToFloor, + raiseToHighest, + lowerAndCrush, + crushAndRaise, + fastCrushAndRaise, + silentCrushAndRaise +} ceiling_e; + + +typedef struct +{ + thinker_t thinker; + ceiling_e type; + sector_t* sector; + fixed_t bottomheight; + fixed_t topheight; + fixed_t speed; + doom_boolean crush; + + // 1 = up, 0 = waiting, -1 = down + int direction; + + // ID + int tag; + int olddirection; +} ceiling_t; + + +#define CEILSPEED FRACUNIT +#define CEILWAIT 150 +#define MAXCEILINGS 30 + + +extern ceiling_t* activeceilings[MAXCEILINGS]; + + +int EV_DoCeiling(line_t* line, ceiling_e type); +void T_MoveCeiling(ceiling_t* ceiling); +void P_AddActiveCeiling(ceiling_t* c); +void P_RemoveActiveCeiling(ceiling_t* c); +int EV_CeilingCrushStop(line_t* line); +void P_ActivateInStasisCeiling(line_t* line); + + +// +// P_FLOOR +// +typedef enum +{ + // 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 floorpic + lowerAndChange, + + raiseFloor24, + raiseFloor24AndChange, + raiseFloorCrush, + + // raise to next highest floor, turbo-speed + raiseFloorTurbo, + donutRaise, + raiseFloor512 +} floor_e; + + +typedef enum +{ + build8, // slowly build by 8 + turbo16 // quickly build by 16 +} stair_e; + + +typedef struct +{ + thinker_t thinker; + floor_e type; + doom_boolean crush; + sector_t* sector; + int direction; + int newspecial; + short texture; + fixed_t floordestheight; + fixed_t speed; +} floormove_t; + + +#define FLOORSPEED FRACUNIT + + +typedef enum +{ + ok, + crushed, + pastdest +} result_e; + + +result_e T_MovePlane(sector_t* sector, fixed_t speed, fixed_t dest, doom_boolean crush, int floorOrCeiling, int direction); +int EV_BuildStairs(line_t* line, stair_e type); +int EV_DoFloor(line_t* line, floor_e floortype); +void T_MoveFloor(floormove_t* floor); + +// +// P_TELEPT +// +int EV_Teleport(line_t* line, int side, mobj_t* thing); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/p_switch.c b/src/DOOM/p_switch.c new file mode 100644 index 0000000..9c31518 --- /dev/null +++ b/src/DOOM/p_switch.c @@ -0,0 +1,612 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// +// $Log:$ +// +// DESCRIPTION: +// Switches, buttons. Two-state animation. Exits. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "i_system.h" +#include "doomdef.h" +#include "p_local.h" +#include "g_game.h" +#include "s_sound.h" +#include "sounds.h" // Data. +#include "doomstat.h" // State. +#include "r_state.h" // State. + + +// +// CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE +// +switchlist_t alphSwitchList[] = +{ + // Doom shareware episode 1 switches + {"SW1BRCOM", "SW2BRCOM", 1}, + {"SW1BRN1", "SW2BRN1", 1}, + {"SW1BRN2", "SW2BRN2", 1}, + {"SW1BRNGN", "SW2BRNGN", 1}, + {"SW1BROWN", "SW2BROWN", 1}, + {"SW1COMM", "SW2COMM", 1}, + {"SW1COMP", "SW2COMP", 1}, + {"SW1DIRT", "SW2DIRT", 1}, + {"SW1EXIT", "SW2EXIT", 1}, + {"SW1GRAY", "SW2GRAY", 1}, + {"SW1GRAY1", "SW2GRAY1", 1}, + {"SW1METAL", "SW2METAL", 1}, + {"SW1PIPE", "SW2PIPE", 1}, + {"SW1SLAD", "SW2SLAD", 1}, + {"SW1STARG", "SW2STARG", 1}, + {"SW1STON1", "SW2STON1", 1}, + {"SW1STON2", "SW2STON2", 1}, + {"SW1STONE", "SW2STONE", 1}, + {"SW1STRTN", "SW2STRTN", 1}, + + // Doom registered episodes 2&3 switches + {"SW1BLUE", "SW2BLUE", 2}, + {"SW1CMT", "SW2CMT", 2}, + {"SW1GARG", "SW2GARG", 2}, + {"SW1GSTON", "SW2GSTON", 2}, + {"SW1HOT", "SW2HOT", 2}, + {"SW1LION", "SW2LION", 2}, + {"SW1SATYR", "SW2SATYR", 2}, + {"SW1SKIN", "SW2SKIN", 2}, + {"SW1VINE", "SW2VINE", 2}, + {"SW1WOOD", "SW2WOOD", 2}, + + // Doom II switches + {"SW1PANEL", "SW2PANEL", 3}, + {"SW1ROCK", "SW2ROCK", 3}, + {"SW1MET2", "SW2MET2", 3}, + {"SW1WDMET", "SW2WDMET", 3}, + {"SW1BRIK", "SW2BRIK", 3}, + {"SW1MOD1", "SW2MOD1", 3}, + {"SW1ZIM", "SW2ZIM", 3}, + {"SW1STON6", "SW2STON6", 3}, + {"SW1TEK", "SW2TEK", 3}, + {"SW1MARB", "SW2MARB", 3}, + {"SW1SKULL", "SW2SKULL", 3}, + + {"\0", "\0", 0} +}; + +int switchlist[MAXSWITCHES * 2]; +int numswitches; +button_t buttonlist[MAXBUTTONS]; + + +// +// P_InitSwitchList +// Only called at game initialization. +// +void P_InitSwitchList(void) +{ + int i; + int index; + int episode; + + episode = 1; + + if (gamemode == registered) + episode = 2; + else + if (gamemode == commercial) + episode = 3; + + for (index = 0, i = 0; i < MAXSWITCHES; i++) + { + if (!alphSwitchList[i].episode) + { + numswitches = index / 2; + switchlist[index] = -1; + break; + } + + if (alphSwitchList[i].episode <= episode) + { + switchlist[index++] = R_TextureNumForName(alphSwitchList[i].name1); + switchlist[index++] = R_TextureNumForName(alphSwitchList[i].name2); + } + } +} + + +// +// Start a button counting down till it turns off. +// +void P_StartButton(line_t* line, bwhere_e w, int texture, int time) +{ + int i; + + // See if button is already pressed + for (i = 0; i < MAXBUTTONS; i++) + { + if (buttonlist[i].btimer + && buttonlist[i].line == line) + { + + return; + } + } + + for (i = 0; i < MAXBUTTONS; i++) + { + if (!buttonlist[i].btimer) + { + buttonlist[i].line = line; + buttonlist[i].where = w; + buttonlist[i].btexture = texture; + buttonlist[i].btimer = time; + buttonlist[i].soundorg = (mobj_t*)&line->frontsector->soundorg; + return; + } + } + + I_Error("Error: P_StartButton: no button slots left!"); +} + + +// +// Function that changes wall texture. +// Tell it if switch is ok to use again (1=yes, it's a button). +// +void P_ChangeSwitchTexture(line_t* line, int useAgain) +{ + int texTop; + int texMid; + int texBot; + int i; + int sound; + + if (!useAgain) + line->special = 0; + + texTop = sides[line->sidenum[0]].toptexture; + texMid = sides[line->sidenum[0]].midtexture; + texBot = sides[line->sidenum[0]].bottomtexture; + + sound = sfx_swtchn; + + // EXIT SWITCH? + if (line->special == 11) + sound = sfx_swtchx; + + for (i = 0; i < numswitches * 2; i++) + { + if (switchlist[i] == texTop) + { + S_StartSound(buttonlist->soundorg, sound); + sides[line->sidenum[0]].toptexture = switchlist[i ^ 1]; + + if (useAgain) + P_StartButton(line, top, switchlist[i], BUTTONTIME); + + return; + } + else + { + if (switchlist[i] == texMid) + { + S_StartSound(buttonlist->soundorg, sound); + sides[line->sidenum[0]].midtexture = switchlist[i ^ 1]; + + if (useAgain) + P_StartButton(line, middle, switchlist[i], BUTTONTIME); + + return; + } + else + { + if (switchlist[i] == texBot) + { + S_StartSound(buttonlist->soundorg, sound); + sides[line->sidenum[0]].bottomtexture = switchlist[i ^ 1]; + + if (useAgain) + P_StartButton(line, bottom, switchlist[i], BUTTONTIME); + + return; + } + } + } + } +} + + +// +// P_UseSpecialLine +// Called when a thing uses a special line. +// Only the front sides of lines are usable. +// +doom_boolean P_UseSpecialLine(mobj_t* thing, line_t* line, int side) +{ + // Err... + // Use the back sides of VERY SPECIAL lines... + if (side) + { + switch (line->special) + { + case 124: + // Sliding door open&close + // UNUSED? + break; + + default: + return false; + break; + } + } + + + // Switches that other things can activate. + if (!thing->player) + { + // never open secret doors + if (line->flags & ML_SECRET) + return false; + + switch (line->special) + { + case 1: // MANUAL DOOR RAISE + case 32: // MANUAL BLUE + case 33: // MANUAL RED + case 34: // MANUAL YELLOW + break; + + default: + return false; + break; + } + } + + + // do something + switch (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 + EV_VerticalDoor(line, thing); + break; + + //UNUSED - Door Slide Open&Close + // case 124: + // EV_SlidingDoor (line, thing); + // break; + + // SWITCHES + case 7: + // Build Stairs + if (EV_BuildStairs(line, build8)) + P_ChangeSwitchTexture(line, 0); + break; + + case 9: + // Change Donut + if (EV_DoDonut(line)) + P_ChangeSwitchTexture(line, 0); + break; + + case 11: + // Exit level + P_ChangeSwitchTexture(line, 0); + G_ExitLevel(); + break; + + case 14: + // Raise Floor 32 and change texture + if (EV_DoPlat(line, raiseAndChange, 32)) + P_ChangeSwitchTexture(line, 0); + break; + + case 15: + // Raise Floor 24 and change texture + if (EV_DoPlat(line, raiseAndChange, 24)) + P_ChangeSwitchTexture(line, 0); + break; + + case 18: + // Raise Floor to next highest floor + if (EV_DoFloor(line, raiseFloorToNearest)) + P_ChangeSwitchTexture(line, 0); + break; + + case 20: + // Raise Plat next highest floor and change texture + if (EV_DoPlat(line, raiseToNearestAndChange, 0)) + P_ChangeSwitchTexture(line, 0); + break; + + case 21: + // PlatDownWaitUpStay + if (EV_DoPlat(line, downWaitUpStay, 0)) + P_ChangeSwitchTexture(line, 0); + break; + + case 23: + // Lower Floor to Lowest + if (EV_DoFloor(line, lowerFloorToLowest)) + P_ChangeSwitchTexture(line, 0); + break; + + case 29: + // Raise Door + if (EV_DoDoor(line, door_normal)) + P_ChangeSwitchTexture(line, 0); + break; + + case 41: + // Lower Ceiling to Floor + if (EV_DoCeiling(line, lowerToFloor)) + P_ChangeSwitchTexture(line, 0); + break; + + case 71: + // Turbo Lower Floor + if (EV_DoFloor(line, turboLower)) + P_ChangeSwitchTexture(line, 0); + break; + + case 49: + // Ceiling Crush And Raise + if (EV_DoCeiling(line, crushAndRaise)) + P_ChangeSwitchTexture(line, 0); + break; + + case 50: + // Close Door + if (EV_DoDoor(line, door_close)) + P_ChangeSwitchTexture(line, 0); + break; + + case 51: + // Secret EXIT + P_ChangeSwitchTexture(line, 0); + G_SecretExitLevel(); + break; + + case 55: + // Raise Floor Crush + if (EV_DoFloor(line, raiseFloorCrush)) + P_ChangeSwitchTexture(line, 0); + break; + + case 101: + // Raise Floor + if (EV_DoFloor(line, raiseFloor)) + P_ChangeSwitchTexture(line, 0); + break; + + case 102: + // Lower Floor to Surrounding floor height + if (EV_DoFloor(line, lowerFloor)) + P_ChangeSwitchTexture(line, 0); + break; + + case 103: + // Open Door + if (EV_DoDoor(line, door_open)) + P_ChangeSwitchTexture(line, 0); + break; + + case 111: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor(line, blazeRaise)) + P_ChangeSwitchTexture(line, 0); + break; + + case 112: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor(line, blazeOpen)) + P_ChangeSwitchTexture(line, 0); + break; + + case 113: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor(line, blazeClose)) + P_ChangeSwitchTexture(line, 0); + break; + + case 122: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line, blazeDWUS, 0)) + P_ChangeSwitchTexture(line, 0); + break; + + case 127: + // Build Stairs Turbo 16 + if (EV_BuildStairs(line, turbo16)) + P_ChangeSwitchTexture(line, 0); + break; + + case 131: + // Raise Floor Turbo + if (EV_DoFloor(line, raiseFloorTurbo)) + P_ChangeSwitchTexture(line, 0); + break; + + case 133: + // BlzOpenDoor BLUE + case 135: + // BlzOpenDoor RED + case 137: + // BlzOpenDoor YELLOW + if (EV_DoLockedDoor(line, blazeOpen, thing)) + P_ChangeSwitchTexture(line, 0); + break; + + case 140: + // Raise Floor 512 + if (EV_DoFloor(line, raiseFloor512)) + P_ChangeSwitchTexture(line, 0); + break; + + // BUTTONS + case 42: + // Close Door + if (EV_DoDoor(line, door_close)) + P_ChangeSwitchTexture(line, 1); + break; + + case 43: + // Lower Ceiling to Floor + if (EV_DoCeiling(line, lowerToFloor)) + P_ChangeSwitchTexture(line, 1); + break; + + case 45: + // Lower Floor to Surrounding floor height + if (EV_DoFloor(line, lowerFloor)) + P_ChangeSwitchTexture(line, 1); + break; + + case 60: + // Lower Floor to Lowest + if (EV_DoFloor(line, lowerFloorToLowest)) + P_ChangeSwitchTexture(line, 1); + break; + + case 61: + // Open Door + if (EV_DoDoor(line, door_open)) + P_ChangeSwitchTexture(line, 1); + break; + + case 62: + // PlatDownWaitUpStay + if (EV_DoPlat(line, downWaitUpStay, 1)) + P_ChangeSwitchTexture(line, 1); + break; + + case 63: + // Raise Door + if (EV_DoDoor(line, door_normal)) + P_ChangeSwitchTexture(line, 1); + break; + + case 64: + // Raise Floor to ceiling + if (EV_DoFloor(line, raiseFloor)) + P_ChangeSwitchTexture(line, 1); + break; + + case 66: + // Raise Floor 24 and change texture + if (EV_DoPlat(line, raiseAndChange, 24)) + P_ChangeSwitchTexture(line, 1); + break; + + case 67: + // Raise Floor 32 and change texture + if (EV_DoPlat(line, raiseAndChange, 32)) + P_ChangeSwitchTexture(line, 1); + break; + + case 65: + // Raise Floor Crush + if (EV_DoFloor(line, raiseFloorCrush)) + P_ChangeSwitchTexture(line, 1); + break; + + case 68: + // Raise Plat to next highest floor and change texture + if (EV_DoPlat(line, raiseToNearestAndChange, 0)) + P_ChangeSwitchTexture(line, 1); + break; + + case 69: + // Raise Floor to next highest floor + if (EV_DoFloor(line, raiseFloorToNearest)) + P_ChangeSwitchTexture(line, 1); + break; + + case 70: + // Turbo Lower Floor + if (EV_DoFloor(line, turboLower)) + P_ChangeSwitchTexture(line, 1); + break; + + case 114: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor(line, blazeRaise)) + P_ChangeSwitchTexture(line, 1); + break; + + case 115: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor(line, blazeOpen)) + P_ChangeSwitchTexture(line, 1); + break; + + case 116: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor(line, blazeClose)) + P_ChangeSwitchTexture(line, 1); + break; + + case 123: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line, blazeDWUS, 0)) + P_ChangeSwitchTexture(line, 1); + break; + + case 132: + // Raise Floor Turbo + if (EV_DoFloor(line, raiseFloorTurbo)) + P_ChangeSwitchTexture(line, 1); + break; + + case 99: + // BlzOpenDoor BLUE + case 134: + // BlzOpenDoor RED + case 136: + // BlzOpenDoor YELLOW + if (EV_DoLockedDoor(line, blazeOpen, thing)) + P_ChangeSwitchTexture(line, 1); + break; + + case 138: + // Light Turn On + EV_LightTurnOn(line, 255); + P_ChangeSwitchTexture(line, 1); + break; + + case 139: + // Light Turn Off + EV_LightTurnOn(line, 35); + P_ChangeSwitchTexture(line, 1); + break; + } + + return true; +} diff --git a/src/DOOM/p_telept.c b/src/DOOM/p_telept.c new file mode 100644 index 0000000..92008ff --- /dev/null +++ b/src/DOOM/p_telept.c @@ -0,0 +1,117 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Teleportation. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "doomdef.h" +#include "s_sound.h" +#include "p_local.h" +#include "sounds.h" // Data. +#include "r_state.h" // State. + + +// +// TELEPORTATION +// +int EV_Teleport(line_t* line, int side, mobj_t* thing) +{ + int i; + int tag; + mobj_t* m; + mobj_t* fog; + unsigned an; + thinker_t* thinker; + sector_t* sector; + fixed_t oldx; + fixed_t oldy; + fixed_t oldz; + + // don't teleport missiles + if (thing->flags & MF_MISSILE) + return 0; + + // Don't teleport if hit back of line, + // so you can get out of teleporter. + if (side == 1) + return 0; + + tag = line->tag; + for (i = 0; i < numsectors; i++) + { + if (sectors[i].tag == tag) + { + thinker = thinkercap.next; + for (thinker = thinkercap.next; + thinker != &thinkercap; + thinker = thinker->next) + { + // not a mobj + if (thinker->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + m = (mobj_t*)thinker; + + // not a teleportman + if (m->type != MT_TELEPORTMAN) + continue; + + sector = m->subsector->sector; + // wrong sector + if (sector - sectors != i) + continue; + + oldx = thing->x; + oldy = thing->y; + oldz = thing->z; + + if (!P_TeleportMove(thing, m->x, m->y)) + return 0; + + thing->z = thing->floorz; //fixme: not needed? + if (thing->player) + thing->player->viewz = thing->z + thing->player->viewheight; + + // spawn teleport fog at source and destination + fog = P_SpawnMobj(oldx, oldy, oldz, MT_TFOG); + S_StartSound(fog, sfx_telept); + an = m->angle >> ANGLETOFINESHIFT; + fog = P_SpawnMobj(m->x + 20 * finecosine[an], m->y + 20 * finesine[an] + , thing->z, MT_TFOG); + + // emit sound, where? + S_StartSound(fog, sfx_telept); + + // don't move for a bit + if (thing->player) + thing->reactiontime = 18; + + thing->angle = m->angle; + thing->momx = thing->momy = thing->momz = 0; + return 1; + } + } + } + + return 0; +} diff --git a/src/DOOM/p_tick.c b/src/DOOM/p_tick.c new file mode 100644 index 0000000..aa7d50b --- /dev/null +++ b/src/DOOM/p_tick.c @@ -0,0 +1,138 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Archiving: SaveGame I/O. +// Thinker, Ticker. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "z_zone.h" +#include "p_local.h" +#include "doomstat.h" + + +int leveltime; + +// +// THINKERS +// All thinkers should be allocated by Z_Malloc +// so they can be operated on uniformly. +// The actual structures will vary in size, +// but the first element must be thinker_t. +// + +// Both the head and tail of the thinker list. +thinker_t thinkercap; + + +// +// P_InitThinkers +// +void P_InitThinkers(void) +{ + thinkercap.prev = thinkercap.next = &thinkercap; +} + + +// +// P_AddThinker +// Adds a new thinker at the end of the list. +// +void P_AddThinker(thinker_t* thinker) +{ + thinkercap.prev->next = thinker; + thinker->next = &thinkercap; + thinker->prev = thinkercap.prev; + thinkercap.prev = thinker; +} + + +// +// P_RemoveThinker +// Deallocation is lazy -- it will not actually be freed +// until its thinking turn comes up. +// +void P_RemoveThinker(thinker_t* thinker) +{ + // FIXME: NOP. + thinker->function.acv = (actionf_v)(-1); +} + + +// +// P_RunThinkers +// +void P_RunThinkers(void) +{ + thinker_t* currentthinker; + + currentthinker = thinkercap.next; + while (currentthinker != &thinkercap) + { + if (currentthinker->function.acv == (actionf_v)(-1)) + { + // time to remove it + currentthinker->next->prev = currentthinker->prev; + currentthinker->prev->next = currentthinker->next; + Z_Free(currentthinker); + } + else + { + if (currentthinker->function.acp1) + currentthinker->function.acp1(currentthinker); + } + currentthinker = currentthinker->next; + } +} + + +// +// P_Ticker +// +void P_Ticker(void) +{ + int i; + + // run the tic + if (paused) + return; + + // pause if in menu and at least one tic has been run + if (!netgame + && menuactive + && !demoplayback + && players[consoleplayer].viewz != 1) + { + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + P_PlayerThink(&players[i]); + + P_RunThinkers(); + P_UpdateSpecials(); + P_RespawnSpecials(); + + // for par times + leveltime++; +} diff --git a/src/DOOM/p_tick.h b/src/DOOM/p_tick.h new file mode 100644 index 0000000..a660f37 --- /dev/null +++ b/src/DOOM/p_tick.h @@ -0,0 +1,38 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// ? +// +//----------------------------------------------------------------------------- + +#ifndef __P_TICK__ +#define __P_TICK__ + + +// Called by C_Ticker, +// can call G_PlayerExited. +// Carries out all thinking of monsters and players. +void P_Ticker(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/p_user.c b/src/DOOM/p_user.c new file mode 100644 index 0000000..01acef1 --- /dev/null +++ b/src/DOOM/p_user.c @@ -0,0 +1,370 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Player related stuff. +// Bobbing POV/weapon, movement. +// Pending weapon. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "doomdef.h" +#include "d_event.h" +#include "p_local.h" +#include "doomstat.h" + + +// Index of the special effects (INVUL inverse) map. +#define INVERSECOLORMAP 32 + +// 16 pixels of bob +#define MAXBOB 0x100000 + +#define ANG5 (ANG90/18) + + +// +// Movement. +// + +doom_boolean onground; + + +// +// P_Thrust +// Moves the given origin along a given angle. +// +void P_Thrust(player_t* player, angle_t angle, fixed_t move) +{ + angle >>= ANGLETOFINESHIFT; + + player->mo->momx += FixedMul(move, finecosine[angle]); + player->mo->momy += FixedMul(move, finesine[angle]); +} + + +// +// P_CalcHeight +// Calculate the walking / running height adjustment +// +void P_CalcHeight(player_t* player) +{ + int angle; + fixed_t bob; + + // Regular movement bobbing + // (needs to be calculated for gun swing + // even if not on ground) + // OPTIMIZE: tablify angle + // Note: a LUT allows for effects + // like a ramp with low health. + player->bob = + FixedMul(player->mo->momx, player->mo->momx) + + FixedMul(player->mo->momy, player->mo->momy); + + player->bob >>= 2; + + if (player->bob > MAXBOB) + player->bob = MAXBOB; + + if ((player->cheats & CF_NOMOMENTUM) || !onground) + { + player->viewz = player->mo->z + VIEWHEIGHT; + + if (player->viewz > player->mo->ceilingz - 4 * FRACUNIT) + player->viewz = player->mo->ceilingz - 4 * FRACUNIT; + + player->viewz = player->mo->z + player->viewheight; + return; + } + + angle = (FINEANGLES / 20 * leveltime) & FINEMASK; + bob = FixedMul(player->bob / 2, finesine[angle]); + + + // move viewheight + if (player->playerstate == PST_LIVE) + { + player->viewheight += player->deltaviewheight; + + if (player->viewheight > VIEWHEIGHT) + { + player->viewheight = VIEWHEIGHT; + player->deltaviewheight = 0; + } + + if (player->viewheight < VIEWHEIGHT / 2) + { + player->viewheight = VIEWHEIGHT / 2; + if (player->deltaviewheight <= 0) + player->deltaviewheight = 1; + } + + if (player->deltaviewheight) + { + player->deltaviewheight += FRACUNIT / 4; + if (!player->deltaviewheight) + player->deltaviewheight = 1; + } + } + player->viewz = player->mo->z + player->viewheight + bob; + + if (player->viewz > player->mo->ceilingz - 4 * FRACUNIT) + player->viewz = player->mo->ceilingz - 4 * FRACUNIT; +} + + +// +// P_MovePlayer +// +void P_MovePlayer(player_t* player) +{ + ticcmd_t* cmd; + + cmd = &player->cmd; + + player->mo->angle += (cmd->angleturn << 16); + + // Do not let the player control movement + // if not onground. + onground = (player->mo->z <= player->mo->floorz); + + if (cmd->forwardmove && onground) + P_Thrust(player, player->mo->angle, cmd->forwardmove * 2048); + + if (cmd->sidemove && onground) + P_Thrust(player, player->mo->angle - ANG90, cmd->sidemove * 2048); + + if ((cmd->forwardmove || cmd->sidemove) + && player->mo->state == &states[S_PLAY]) + { + P_SetMobjState(player->mo, S_PLAY_RUN1); + } +} + + +// +// P_DeathThink +// Fall on your face when dying. +// Decrease POV height to floor height. +// +void P_DeathThink(player_t* player) +{ + angle_t angle; + angle_t delta; + + P_MovePsprites(player); + + // fall to the ground + if (player->viewheight > 6 * FRACUNIT) + player->viewheight -= FRACUNIT; + + if (player->viewheight < 6 * FRACUNIT) + player->viewheight = 6 * FRACUNIT; + + player->deltaviewheight = 0; + onground = (player->mo->z <= player->mo->floorz); + P_CalcHeight(player); + + if (player->attacker && player->attacker != player->mo) + { + angle = R_PointToAngle2(player->mo->x, + player->mo->y, + player->attacker->x, + player->attacker->y); + + delta = angle - player->mo->angle; + + if (delta < ANG5 || delta >(unsigned) - ANG5) + { + // Looking at killer, + // so fade damage flash down. + player->mo->angle = angle; + + if (player->damagecount) + player->damagecount--; + } + else if (delta < ANG180) + player->mo->angle += ANG5; + else + player->mo->angle -= ANG5; + } + else if (player->damagecount) + player->damagecount--; + + + if (player->cmd.buttons & BT_USE) + player->playerstate = PST_REBORN; +} + + +// +// P_PlayerThink +// +void P_PlayerThink(player_t* player) +{ + ticcmd_t* cmd; + weapontype_t newweapon; + + // fixme: do this in the cheat code + if (player->cheats & CF_NOCLIP) + player->mo->flags |= MF_NOCLIP; + else + player->mo->flags &= ~MF_NOCLIP; + + // chain saw run forward + cmd = &player->cmd; + if (player->mo->flags & MF_JUSTATTACKED) + { + cmd->angleturn = 0; + cmd->forwardmove = 0xc800 / 512; + cmd->sidemove = 0; + player->mo->flags &= ~MF_JUSTATTACKED; + } + + + if (player->playerstate == PST_DEAD) + { + P_DeathThink(player); + return; + } + + // Move around. + // Reactiontime is used to prevent movement + // for a bit after a teleport. + if (player->mo->reactiontime) + player->mo->reactiontime--; + else + P_MovePlayer(player); + + P_CalcHeight(player); + + if (player->mo->subsector->sector->special) + P_PlayerInSpecialSector(player); + + // Check for weapon change. + + // A special event has no other buttons. + if (cmd->buttons & BT_SPECIAL) + cmd->buttons = 0; + + if (cmd->buttons & BT_CHANGE) + { + // The actual changing of the weapon is done + // when the weapon psprite can do it + // (read: not in the middle of an attack). + newweapon = (cmd->buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT; + + if (newweapon == wp_fist + && player->weaponowned[wp_chainsaw] + && !(player->readyweapon == wp_chainsaw + && player->powers[pw_strength])) + { + newweapon = wp_chainsaw; + } + + if ((gamemode == commercial) + && newweapon == wp_shotgun + && player->weaponowned[wp_supershotgun] + && player->readyweapon != wp_supershotgun) + { + newweapon = wp_supershotgun; + } + + + if (player->weaponowned[newweapon] + && newweapon != player->readyweapon) + { + // Do not go to plasma or BFG in shareware, + // even if cheated. + if ((newweapon != wp_plasma + && newweapon != wp_bfg) + || (gamemode != shareware)) + { + player->pendingweapon = newweapon; + } + } + } + + // check for use + if (cmd->buttons & BT_USE) + { + if (!player->usedown) + { + P_UseLines(player); + player->usedown = true; + } + } + else + player->usedown = false; + + // cycle psprites + P_MovePsprites(player); + + // Counters, time dependend power ups. + + // Strength counts up to diminish fade. + if (player->powers[pw_strength]) + player->powers[pw_strength]++; + + if (player->powers[pw_invulnerability]) + player->powers[pw_invulnerability]--; + + if (player->powers[pw_invisibility]) + if (!--player->powers[pw_invisibility]) + player->mo->flags &= ~MF_SHADOW; + + if (player->powers[pw_infrared]) + player->powers[pw_infrared]--; + + if (player->powers[pw_ironfeet]) + player->powers[pw_ironfeet]--; + + if (player->damagecount) + player->damagecount--; + + if (player->bonuscount) + player->bonuscount--; + + + // Handling colormaps. + if (player->powers[pw_invulnerability]) + { + if (player->powers[pw_invulnerability] > 4 * 32 + || (player->powers[pw_invulnerability] & 8)) + player->fixedcolormap = INVERSECOLORMAP; + else + player->fixedcolormap = 0; + } + else if (player->powers[pw_infrared]) + { + if (player->powers[pw_infrared] > 4 * 32 + || (player->powers[pw_infrared] & 8)) + { + // almost full bright + player->fixedcolormap = 1; + } + else + player->fixedcolormap = 0; + } + else + player->fixedcolormap = 0; +} diff --git a/src/DOOM/r_bsp.c b/src/DOOM/r_bsp.c new file mode 100644 index 0000000..5931d7d --- /dev/null +++ b/src/DOOM/r_bsp.c @@ -0,0 +1,563 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// BSP traversal, handling of LineSegs for rendering. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "r_main.h" +#include "r_plane.h" +#include "r_things.h" +#include "doomstat.h" // State. +#include "r_state.h" // State. + + +#define MAXSEGS 32 + + +// +// ClipWallSegment +// Clips the given range of columns +// and includes it in the new clip list. +// +typedef struct +{ + int first; + int last; +} cliprange_t; + + +seg_t* curline; +side_t* sidedef; +line_t* linedef; +sector_t* frontsector; +sector_t* backsector; + +drawseg_t drawsegs[MAXDRAWSEGS]; +drawseg_t* ds_p; + +// newend is one past the last valid seg +cliprange_t* newend; +cliprange_t solidsegs[MAXSEGS]; + +int checkcoord[12][4] = +{ + {3,0,2,1}, + {3,0,2,0}, + {3,1,2,0}, + {0}, + {2,0,2,1}, + {0,0,0,0}, + {3,1,3,0}, + {0}, + {2,0,3,1}, + {2,1,3,1}, + {2,1,3,0} +}; + + +void R_StoreWallRange(int start, int stop); + + +// +// R_ClearDrawSegs +// +void R_ClearDrawSegs(void) +{ + ds_p = drawsegs; +} + + +// +// R_ClipSolidWallSegment +// Does handle solid walls, +// e.g. single sided LineDefs (middle texture) +// that entirely block the view. +// +void R_ClipSolidWallSegment(int first, int last) +{ + cliprange_t* next; + cliprange_t* start; + + // Find the first range that touches the range + // (adjacent pixels are touching). + start = solidsegs; + while (start->last < first - 1) + start++; + + if (first < start->first) + { + if (last < start->first - 1) + { + // Post is entirely visible (above start), + // so insert a new clippost. + R_StoreWallRange(first, last); + next = newend; + newend++; + + while (next != start) + { + *next = *(next - 1); + next--; + } + next->first = first; + next->last = last; + return; + } + + // There is a fragment above *start. + R_StoreWallRange(first, start->first - 1); + // Now adjust the clip size. + start->first = first; + } + + // Bottom contained in start? + if (last <= start->last) + return; + + next = start; + while (last >= (next + 1)->first - 1) + { + // There is a fragment between two posts. + R_StoreWallRange(next->last + 1, (next + 1)->first - 1); + next++; + + if (last <= next->last) + { + // Bottom is contained in next. + // Adjust the clip size. + start->last = next->last; + goto crunch; + } + } + + // There is a fragment after *next. + R_StoreWallRange(next->last + 1, last); + // Adjust the clip size. + start->last = last; + + // 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++ != newend) + { + // Remove a post. + *++start = *next; + } + + newend = start + 1; +} + + +// +// R_ClipPassWallSegment +// Clips the given range of columns, +// but does not includes it in the clip list. +// Does handle windows, +// e.g. LineDefs with upper and lower texture. +// +void R_ClipPassWallSegment(int first, int last) +{ + cliprange_t* start; + + // Find the first range that touches the range + // (adjacent pixels are touching). + start = solidsegs; + while (start->last < first - 1) + start++; + + if (first < start->first) + { + if (last < start->first - 1) + { + // Post is entirely visible (above start). + R_StoreWallRange(first, last); + return; + } + + // There is a fragment above *start. + R_StoreWallRange(first, start->first - 1); + } + + // Bottom contained in start? + if (last <= start->last) + return; + + while (last >= (start + 1)->first - 1) + { + // There is a fragment between two posts. + R_StoreWallRange(start->last + 1, (start + 1)->first - 1); + start++; + + if (last <= start->last) + return; + } + + // There is a fragment after *next. + R_StoreWallRange(start->last + 1, last); +} + + +// +// R_ClearClipSegs +// +void R_ClearClipSegs(void) +{ + solidsegs[0].first = -0x7fffffff; + solidsegs[0].last = -1; + solidsegs[1].first = viewwidth; + solidsegs[1].last = 0x7fffffff; + newend = solidsegs + 2; +} + + +// +// R_AddLine +// Clips the given segment +// and adds any visible pieces to the line list. +// +void R_AddLine(seg_t* line) +{ + int x1; + int x2; + angle_t angle1; + angle_t angle2; + angle_t span; + angle_t tspan; + + curline = line; + + // OPTIMIZE: quickly reject orthogonal back sides. + angle1 = R_PointToAngle(line->v1->x, line->v1->y); + angle2 = R_PointToAngle(line->v2->x, line->v2->y); + + // Clip to view edges. + // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW). + span = angle1 - angle2; + + // Back side? I.e. backface culling? + if (span >= ANG180) + return; + + // Global angle needed by segcalc. + rw_angle1 = angle1; + angle1 -= viewangle; + angle2 -= viewangle; + + tspan = angle1 + clipangle; + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + // Totally off the left edge? + if (tspan >= span) + return; + + angle1 = clipangle; + } + tspan = clipangle - angle2; + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + // Totally off the left edge? + if (tspan >= span) + return; +#pragma warning(push) +#pragma warning(disable : 4146) + angle2 = -clipangle; +#pragma warning(pop) + } + + // The seg is in the view range, + // but not necessarily visible. + angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT; + angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT; + x1 = viewangletox[angle1]; + x2 = viewangletox[angle2]; + + // Does not cross a pixel? + if (x1 == x2) + return; + + backsector = line->backsector; + + // Single sided line? + if (!backsector) + goto clipsolid; + + // Closed door. + if (backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight) + goto clipsolid; + + // Window. + if (backsector->ceilingheight != frontsector->ceilingheight + || backsector->floorheight != frontsector->floorheight) + goto clippass; + + // 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->ceilingpic == frontsector->ceilingpic + && backsector->floorpic == frontsector->floorpic + && backsector->lightlevel == frontsector->lightlevel + && curline->sidedef->midtexture == 0) + { + return; + } + + +clippass: + R_ClipPassWallSegment(x1, x2 - 1); + return; + +clipsolid: + R_ClipSolidWallSegment(x1, x2 - 1); +} + + +// +// R_CheckBBox +// Checks BSP node/subtree bounding box. +// Returns true +// if some part of the bbox might be visible. +// +doom_boolean R_CheckBBox(fixed_t* bspcoord) +{ + int boxx; + int boxy; + int boxpos; + + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + angle_t angle1; + angle_t angle2; + angle_t span; + angle_t tspan; + + cliprange_t* start; + + int sx1; + int sx2; + + // Find the corners of the box + // that define the edges from current viewpoint. + if (viewx <= bspcoord[BOXLEFT]) + boxx = 0; + else if (viewx < bspcoord[BOXRIGHT]) + boxx = 1; + else + boxx = 2; + + if (viewy >= bspcoord[BOXTOP]) + boxy = 0; + else if (viewy > bspcoord[BOXBOTTOM]) + boxy = 1; + else + boxy = 2; + + boxpos = (boxy << 2) + boxx; + if (boxpos == 5) + return true; + + x1 = bspcoord[checkcoord[boxpos][0]]; + y1 = bspcoord[checkcoord[boxpos][1]]; + x2 = bspcoord[checkcoord[boxpos][2]]; + y2 = bspcoord[checkcoord[boxpos][3]]; + + // check clip list for an open space + angle1 = R_PointToAngle(x1, y1) - viewangle; + angle2 = R_PointToAngle(x2, y2) - viewangle; + + span = angle1 - angle2; + + // Sitting on a line? + if (span >= ANG180) + return true; + + tspan = angle1 + clipangle; + + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + // Totally off the left edge? + if (tspan >= span) + return false; + + angle1 = clipangle; + } + tspan = clipangle - angle2; + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + // Totally off the left edge? + if (tspan >= span) + return false; + +#pragma warning(push) +#pragma warning(disable : 4146) + angle2 = -clipangle; +#pragma warning(pop) + } + + + // Find the first clippost + // that touches the source post + // (adjacent pixels are touching). + angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT; + angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT; + sx1 = viewangletox[angle1]; + sx2 = viewangletox[angle2]; + + // Does not cross a pixel. + if (sx1 == sx2) + return false; + sx2--; + + start = solidsegs; + while (start->last < sx2) + start++; + + if (sx1 >= start->first + && sx2 <= start->last) + { + // The clippost contains the new span. + return false; + } + + return true; +} + + +// +// R_Subsector +// Determine floor/ceiling planes. +// Add sprites of things in sector. +// Draw one or more line segments. +// +void R_Subsector(int num) +{ + int count; + seg_t* line; + subsector_t* sub; + +#ifdef RANGECHECK + if (num >= numsubsectors) + { + //I_Error("Error: R_Subsector: ss %i with numss = %i", + // num, + // numsubsectors); + + + doom_strcpy(error_buf, "Error: R_Subsector: ss "); + doom_concat(error_buf, doom_itoa(num, 10)); + doom_concat(error_buf, " with numss = "); + doom_concat(error_buf, doom_itoa(numsubsectors, 10)); + I_Error(error_buf); + } +#endif + + sscount++; + sub = &subsectors[num]; + frontsector = sub->sector; + count = sub->numlines; + line = &segs[sub->firstline]; + + if (frontsector->floorheight < viewz) + { + floorplane = R_FindPlane(frontsector->floorheight, + frontsector->floorpic, + frontsector->lightlevel); + } + else + floorplane = 0; + + if (frontsector->ceilingheight > viewz + || frontsector->ceilingpic == skyflatnum) + { + ceilingplane = R_FindPlane(frontsector->ceilingheight, + frontsector->ceilingpic, + frontsector->lightlevel); + } + else + ceilingplane = 0; + + R_AddSprites(frontsector); + + while (count--) + { + R_AddLine(line); + line++; + } +} + + +// +// RenderBSPNode +// Renders all subsectors below a given node, +// traversing subtree recursively. +// Just call with BSP root. +void R_RenderBSPNode(int bspnum) +{ + node_t* bsp; + int side; + + // Found a subsector? + if (bspnum & NF_SUBSECTOR) + { + if (bspnum == -1) + R_Subsector(0); + else + R_Subsector(bspnum & (~NF_SUBSECTOR)); + return; + } + + bsp = &nodes[bspnum]; + + // Decide which side the view point is on. + side = R_PointOnSide(viewx, viewy, bsp); + + // Recursively divide front space. + R_RenderBSPNode(bsp->children[side]); + + // Possibly divide back space. + if (R_CheckBBox(bsp->bbox[side ^ 1])) + R_RenderBSPNode(bsp->children[side ^ 1]); +} diff --git a/src/DOOM/r_bsp.h b/src/DOOM/r_bsp.h new file mode 100644 index 0000000..07f47d6 --- /dev/null +++ b/src/DOOM/r_bsp.h @@ -0,0 +1,69 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Refresh module, BSP traversal and handling. +// +//----------------------------------------------------------------------------- + +#ifndef __R_BSP__ +#define __R_BSP__ + + +#include "r_defs.h" + + +extern seg_t* curline; +extern side_t* sidedef; +extern line_t* linedef; +extern sector_t* frontsector; +extern sector_t* backsector; + +extern int rw_x; +extern int rw_stopx; + +extern doom_boolean segtextured; + +// false if the back side is the same plane +extern doom_boolean markfloor; +extern doom_boolean markceiling; + +extern doom_boolean skymap; + +extern drawseg_t drawsegs[MAXDRAWSEGS]; +extern drawseg_t* ds_p; + +extern lighttable_t** hscalelight; +extern lighttable_t** vscalelight; +extern lighttable_t** dscalelight; + + +typedef void (*drawfunc_t) (int start, int stop); + + +// BSP? +void R_ClearClipSegs(void); +void R_ClearDrawSegs(void); +void R_RenderBSPNode(int bspnum); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/r_data.c b/src/DOOM/r_data.c new file mode 100644 index 0000000..8e66529 --- /dev/null +++ b/src/DOOM/r_data.c @@ -0,0 +1,846 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// Revision 1.3 1997/01/29 20:10 +// DESCRIPTION: +// Preparation of data for rendering, +// generation of lookups, caching, retrieval by name. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "i_system.h" +#include "z_zone.h" +#include "m_swap.h" +#include "w_wad.h" +#include "doomdef.h" +#include "r_local.h" +#include "p_local.h" +#include "doomstat.h" +#include "r_sky.h" +#if defined(DOOM_WIN32) + +// #define strncasecmp strnicmp +#elif defined(DOOM_APPLE) + +#else +#ifdef LINUX +#include +#endif +#endif +#include "r_data.h" + + +// +// Graphics. +// DOOM graphics for walls and sprites +// is stored in vertical runs of opaque pixels (posts). +// A column is composed of zero or more posts, +// a patch or sprite is composed of zero or more columns. +// + + +// +// Texture definition. +// Each texture is composed of one or more patches, +// with patches being lumps stored in the WAD. +// The lumps are referenced by number, and patched +// into the rectangular texture space using origin +// and possibly other attributes. +// +typedef struct +{ + short originx; + short originy; + short patch; + short stepdir; + short colormap; +} mappatch_t; + + +// +// Texture definition. +// A DOOM wall texture is a list of patches +// which are to be combined in a predefined order. +// +typedef struct +{ + char name[8]; + doom_boolean masked; + short width; + short height; + //void **columndirectory; // OBSOLETE + int columndirectory; // [pd] If it's not used, at least make sure it's the right size! Pointers are 8 bytes in x64 + short patchcount; + mappatch_t patches[1]; +} maptexture_t; + + +// A single patch from a texture definition, +// basically a rectangular area within +// the texture rectangle. +typedef struct +{ + // Block origin (allways UL), + // which has allready accounted + // for the internal origin of the patch. + int originx; + int originy; + int patch; +} texpatch_t; + + +// A maptexturedef_t describes a rectangular texture, +// which is composed of one or more mappatch_t structures +// that arrange graphic patches. +typedef struct +{ + // Keep name for switch changing, etc. + char name[8]; + short width; + short height; + + // All the patches[patchcount] + // are drawn back to front into the cached texture. + short patchcount; + texpatch_t patches[1]; +} texture_t; + + +int firstflat; +int lastflat; +int numflats; + +int firstpatch; +int lastpatch; +int numpatches; + +int firstspritelump; +int lastspritelump; +int numspritelumps; + +int numtextures; +texture_t** textures; + +int* texturewidthmask; +// needed for texture pegging +fixed_t* textureheight; +int* texturecompositesize; +short** texturecolumnlump; +unsigned short** texturecolumnofs; +byte** texturecomposite; + +// for global animation +int* flattranslation; +int* texturetranslation; + +// needed for pre rendering +fixed_t* spritewidth; +fixed_t* spriteoffset; +fixed_t* spritetopoffset; + +lighttable_t* colormaps; + +int flatmemory; +int texturememory; +int spritememory; + + +// +// MAPTEXTURE_T CACHING +// When a texture is first needed, +// it counts the number of composite columns +// required in the texture and allocates space +// for a column directory and any new columns. +// The directory will simply point inside other patches +// if there is only one patch in a given column, +// but any columns with multiple patches +// will have new column_ts generated. +// + + +// +// R_DrawColumnInCache +// Clip and draw a column +// from a patch into a cached post. +// +void R_DrawColumnInCache(column_t* patch, byte* cache, int originy, int cacheheight) +{ + int count; + int position; + byte* source; + byte* dest; + + dest = (byte*)cache + 3; + + while (patch->topdelta != 0xff) + { + source = (byte*)patch + 3; + count = patch->length; + position = originy + patch->topdelta; + + if (position < 0) + { + count += position; + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + if (count > 0) + doom_memcpy(cache + position, source, count); + + patch = (column_t*)((byte*)patch + patch->length + 4); + } +} + + +// +// R_GenerateComposite +// Using the texture definition, +// the composite texture is created from the patches, +// and each column is cached. +// +void R_GenerateComposite(int texnum) +{ + byte* block; + texture_t* texture; + texpatch_t* patch; + patch_t* realpatch; + int x; + int x1; + int x2; + int i; + column_t* patchcol; + short* collump; + unsigned short* colofs; + + texture = textures[texnum]; + + block = Z_Malloc(texturecompositesize[texnum], + PU_STATIC, + &texturecomposite[texnum]); + + collump = texturecolumnlump[texnum]; + colofs = texturecolumnofs[texnum]; + + // Composite the columns together. + patch = texture->patches; + + for (i = 0, patch = texture->patches; + i < texture->patchcount; + i++, patch++) + { + realpatch = W_CacheLumpNum(patch->patch, PU_CACHE); + x1 = patch->originx; + x2 = x1 + SHORT(realpatch->width); + + if (x1 < 0) + x = 0; + else + x = x1; + + if (x2 > texture->width) + x2 = texture->width; + + for (; x < x2; x++) + { + // Column does not have multiple patches? + if (collump[x] >= 0) + continue; + + patchcol = (column_t*)((byte*)realpatch + + LONG(realpatch->columnofs[x - x1])); + R_DrawColumnInCache(patchcol, + block + colofs[x], + patch->originy, + texture->height); + } + } + + // Now that the texture has been built in column cache, + // it is purgable from zone memory. + Z_ChangeTag(block, PU_CACHE); +} + + +// +// R_GenerateLookup +// +void R_GenerateLookup(int texnum) +{ + texture_t* texture; + byte* patchcount; // patchcount[texture->width] + texpatch_t* patch; + patch_t* realpatch; + int x; + int x1; + int x2; + int i; + short* collump; + unsigned short* colofs; + + texture = textures[texnum]; + + // Composited texture not created yet. + texturecomposite[texnum] = 0; + + texturecompositesize[texnum] = 0; + collump = texturecolumnlump[texnum]; + colofs = texturecolumnofs[texnum]; + + // Now count the number of columns + // that are covered by more than one patch. + // Fill in the lump / offset, so columns + // with only a single patch are all done. + patchcount = (byte*)doom_malloc(texture->width); + doom_memset(patchcount, 0, texture->width); + patch = texture->patches; + + for (i = 0, patch = texture->patches; + i < texture->patchcount; + i++, patch++) + { + realpatch = W_CacheLumpNum(patch->patch, PU_CACHE); + x1 = patch->originx; + x2 = x1 + SHORT(realpatch->width); + + if (x1 < 0) + x = 0; + else + x = x1; + + if (x2 > texture->width) + x2 = texture->width; + for (; x < x2; x++) + { + patchcount[x]++; + collump[x] = patch->patch; + colofs[x] = LONG(realpatch->columnofs[x - x1]) + 3; + } + } + + for (x = 0; x < texture->width; x++) + { + if (!patchcount[x]) + { + //doom_print("R_GenerateLookup: column without a patch (%s)\n", + // texture->name); + doom_print("R_GenerateLookup: column without a patch ("); + doom_print(texture->name); + doom_print(")\n"); + return; + } + // I_Error ("R_GenerateLookup: column without a patch"); + + if (patchcount[x] > 1) + { + // Use the cached block. + collump[x] = -1; + colofs[x] = texturecompositesize[texnum]; + + if (texturecompositesize[texnum] > 0x10000 - texture->height) + { + //I_Error("Error: R_GenerateLookup: texture %i is >64k", + // texnum); + + doom_strcpy(error_buf, "Error: R_GenerateLookup: texture "); + doom_concat(error_buf, doom_itoa(texnum, 10)); + doom_concat(error_buf, " is >64k"); + I_Error(error_buf); + } + + texturecompositesize[texnum] += texture->height; + } + } + + doom_free(patchcount); +} + + +// +// R_GetColumn +// +byte* R_GetColumn(int tex, int col) +{ + int lump; + int ofs; + + col &= texturewidthmask[tex]; + lump = texturecolumnlump[tex][col]; + ofs = texturecolumnofs[tex][col]; + + if (lump > 0) + return (byte*)W_CacheLumpNum(lump, PU_CACHE) + ofs; + + if (!texturecomposite[tex]) + R_GenerateComposite(tex); + + return texturecomposite[tex] + ofs; +} + + +// +// R_InitTextures +// Initializes the texture list +// with the textures from the world map. +// +void R_InitTextures(void) +{ + maptexture_t* mtexture; + texture_t* texture; + mappatch_t* mpatch; + texpatch_t* patch; + + int i; + int j; + + int* maptex; + int* maptex2; + int* maptex1; + + char name[9]; + char* names; + char* name_p; + + int* patchlookup; + + int totalwidth; + int nummappatches; + int offset; + int maxoff; + int maxoff2; + int numtextures1; + int numtextures2; + + int* directory; + + int temp1; + int temp2; + int temp3; + + // Load the patch names from pnames.lmp. + name[8] = 0; + names = W_CacheLumpName("PNAMES", PU_STATIC); + nummappatches = LONG(*((int*)names)); + name_p = names + 4; + patchlookup = doom_malloc(nummappatches * sizeof(*patchlookup)); + + for (i = 0; i < nummappatches; i++) + { + doom_strncpy(name, name_p + i * 8, 8); + patchlookup[i] = W_CheckNumForName(name); + } + Z_Free(names); + + // Load the map texture definitions from textures.lmp. + // The data is contained in one or two lumps, + // TEXTURE1 for shareware, plus TEXTURE2 for commercial. + maptex = maptex1 = W_CacheLumpName("TEXTURE1", PU_STATIC); + numtextures1 = LONG(*maptex); + maxoff = W_LumpLength(W_GetNumForName("TEXTURE1")); + directory = maptex + 1; + + if (W_CheckNumForName("TEXTURE2") != -1) + { + maptex2 = W_CacheLumpName("TEXTURE2", PU_STATIC); + numtextures2 = LONG(*maptex2); + maxoff2 = W_LumpLength(W_GetNumForName("TEXTURE2")); + } + else + { + maptex2 = 0; + numtextures2 = 0; + maxoff2 = 0; + } + numtextures = numtextures1 + numtextures2; + + textures = Z_Malloc(numtextures * sizeof(texture_t*), PU_STATIC, 0); + texturecolumnlump = Z_Malloc(numtextures * sizeof(short*), PU_STATIC, 0); + texturecolumnofs = Z_Malloc(numtextures * sizeof(unsigned short*), PU_STATIC, 0); + texturecomposite = Z_Malloc(numtextures * sizeof(byte*), PU_STATIC, 0); + texturecompositesize = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0); + texturewidthmask = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0); + textureheight = Z_Malloc(numtextures * sizeof(fixed_t), PU_STATIC, 0); + + totalwidth = 0; + + // Really complex printing shit... + temp1 = W_GetNumForName("S_START"); // P_??????? + temp2 = W_GetNumForName("S_END") - 1; + temp3 = ((temp2 - temp1 + 63) / 64) + ((numtextures + 63) / 64); + doom_print("["); + for (i = 0; i < temp3; i++) + doom_print(" "); + doom_print(" ]"); + for (i = 0; i < temp3; i++) + doom_print("\x8"); + doom_print("\x8\x8\x8\x8\x8\x8\x8\x8\x8\x8"); + + for (i = 0; i < numtextures; i++, directory++) + { + if (!(i & 63)) + doom_print("."); + + if (i == numtextures1) + { + // Start looking in second texture file. + maptex = maptex2; + maxoff = maxoff2; + directory = maptex + 1; + } + + offset = LONG(*directory); + + if (offset > maxoff) + I_Error("Error: R_InitTextures: bad texture directory"); + + mtexture = (maptexture_t*)((byte*)maptex + offset); + + texture = textures[i] = + Z_Malloc(sizeof(texture_t) + + sizeof(texpatch_t) * (SHORT(mtexture->patchcount) - 1), + PU_STATIC, 0); + + texture->width = SHORT(mtexture->width); + texture->height = SHORT(mtexture->height); + texture->patchcount = SHORT(mtexture->patchcount); + + doom_memcpy(texture->name, mtexture->name, sizeof(texture->name)); + mpatch = &mtexture->patches[0]; + patch = &texture->patches[0]; + + for (j = 0; j < texture->patchcount; j++, mpatch++, patch++) + { + patch->originx = SHORT(mpatch->originx); + patch->originy = SHORT(mpatch->originy); + patch->patch = patchlookup[SHORT(mpatch->patch)]; + if (patch->patch == -1) + { + //I_Error("Error: R_InitTextures: Missing patch in texture %s", + // texture->name); + + doom_strcpy(error_buf, "Error: R_InitTextures: Missing patch in texture "); + doom_concat(error_buf, texture->name); + I_Error(error_buf); + } + } + texturecolumnlump[i] = Z_Malloc(texture->width * sizeof(short), PU_STATIC, 0); + texturecolumnofs[i] = Z_Malloc(texture->width * sizeof(unsigned short), PU_STATIC, 0); + + j = 1; + while (j * 2 <= texture->width) + j <<= 1; + + texturewidthmask[i] = j - 1; + textureheight[i] = texture->height << FRACBITS; + + totalwidth += texture->width; + } + + Z_Free(maptex1); + if (maptex2) + Z_Free(maptex2); + + // Precalculate whatever possible. + for (i = 0; i < numtextures; i++) + R_GenerateLookup(i); + + // Create translation table for global animation. + texturetranslation = Z_Malloc((numtextures + 1) * sizeof(int), PU_STATIC, 0); + + for (i = 0; i < numtextures; i++) + texturetranslation[i] = i; + + doom_free(patchlookup); +} + + +// +// R_InitFlats +// +void R_InitFlats(void) +{ + int i; + + firstflat = W_GetNumForName("F_START") + 1; + lastflat = W_GetNumForName("F_END") - 1; + numflats = lastflat - firstflat + 1; + + // Create translation table for global animation. + flattranslation = Z_Malloc((numflats + 1) * sizeof(int), PU_STATIC, 0); + + for (i = 0; i < numflats; i++) + flattranslation[i] = i; +} + + +// +// R_InitSpriteLumps +// Finds the width and hoffset of all sprites in the wad, +// so the sprite does not need to be cached completely +// just for having the header info ready during rendering. +// +void R_InitSpriteLumps(void) +{ + int i; + patch_t* patch; + + firstspritelump = W_GetNumForName("S_START") + 1; + lastspritelump = W_GetNumForName("S_END") - 1; + + numspritelumps = lastspritelump - firstspritelump + 1; + spritewidth = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); + spriteoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); + spritetopoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); + + for (i = 0; i < numspritelumps; i++) + { + if (!(i & 63)) + doom_print("."); + + patch = W_CacheLumpNum(firstspritelump + i, PU_CACHE); + spritewidth[i] = SHORT(patch->width) << FRACBITS; + spriteoffset[i] = SHORT(patch->leftoffset) << FRACBITS; + spritetopoffset[i] = SHORT(patch->topoffset) << FRACBITS; + } +} + + +// +// R_InitColormaps +// +void R_InitColormaps(void) +{ + int lump, length; + + // Load in the light tables, + // 256 byte align tables. + lump = W_GetNumForName("COLORMAP"); + length = W_LumpLength(lump) + 255; + colormaps = Z_Malloc(length, PU_STATIC, 0); + colormaps = (byte*)(((unsigned long long)colormaps + 255) & ~0xff); + W_ReadLump(lump, colormaps); +} + + +// +// R_InitData +// Locates all the lumps +// that will be used by all views +// Must be called after W_Init. +// +void R_InitData(void) +{ + R_InitTextures(); + doom_print("\nInitTextures"); + R_InitFlats(); + doom_print("\nInitFlats"); + R_InitSpriteLumps(); + doom_print("\nInitSprites"); + R_InitColormaps(); + doom_print("\nInitColormaps"); +} + + +// +// R_FlatNumForName +// Retrieval, get a flat number for a flat name. +// +int R_FlatNumForName(char* name) +{ + int i; + char namet[9]; + + i = W_CheckNumForName(name); + + if (i == -1) + { + namet[8] = 0; + doom_memcpy(namet, name, 8); + //I_Error("Error: R_FlatNumForName: %s not found", namet); + + doom_strcpy(error_buf, "Error: R_FlatNumForName: "); + doom_concat(error_buf, namet); + doom_concat(error_buf, " not found"); + I_Error(error_buf); + } + return i - firstflat; +} + + +// +// R_CheckTextureNumForName +// Check whether texture is available. +// Filter out NoTexture indicator. +// +int R_CheckTextureNumForName(char* name) +{ + int i; + + // "NoTexture" marker. + if (name[0] == '-') + return 0; + + for (i = 0; i < numtextures; i++) + if (!doom_strncasecmp(textures[i]->name, name, 8)) + return i; + + return -1; +} + + +// +// R_TextureNumForName +// Calls R_CheckTextureNumForName, +// aborts with error message. +// +int R_TextureNumForName(char* name) +{ + int i; + + i = R_CheckTextureNumForName(name); + + if (i == -1) + { + //I_Error("Error: R_TextureNumForName: %s not found", + // name); + + doom_strcpy(error_buf, "Error: R_TextureNumForName: "); + doom_concat(error_buf, name); + doom_concat(error_buf, " not found"); + I_Error(error_buf); + } + return i; +} + + +// +// R_PrecacheLevel +// Preloads all relevant graphics for the level. +// +void R_PrecacheLevel(void) +{ + char* flatpresent; + char* texturepresent; + char* spritepresent; + + int i; + int j; + int k; + int lump; + + texture_t* texture; + thinker_t* th; + spriteframe_t* sf; + + if (demoplayback) + return; + + // Precache flats. + flatpresent = doom_malloc(numflats); + doom_memset(flatpresent, 0, numflats); + + for (i = 0; i < numsectors; i++) + { + flatpresent[sectors[i].floorpic] = 1; + flatpresent[sectors[i].ceilingpic] = 1; + } + + flatmemory = 0; + + for (i = 0; i < numflats; i++) + { + if (flatpresent[i]) + { + lump = firstflat + i; + flatmemory += lumpinfo[lump].size; + W_CacheLumpNum(lump, PU_CACHE); + } + } + + // Precache textures. + texturepresent = doom_malloc(numtextures); + doom_memset(texturepresent, 0, numtextures); + + for (i = 0; i < numsides; i++) + { + texturepresent[sides[i].toptexture] = 1; + texturepresent[sides[i].midtexture] = 1; + texturepresent[sides[i].bottomtexture] = 1; + } + + // Sky texture is always present. + // Note that F_SKY1 is the name used to + // indicate a sky floor/ceiling as a flat, + // while the sky texture is stored like + // a wall texture, with an episode dependend + // name. + texturepresent[skytexture] = 1; + + texturememory = 0; + for (i = 0; i < numtextures; i++) + { + if (!texturepresent[i]) + continue; + + texture = textures[i]; + + for (j = 0; j < texture->patchcount; j++) + { + lump = texture->patches[j].patch; + texturememory += lumpinfo[lump].size; + W_CacheLumpNum(lump, PU_CACHE); + } + } + + // Precache sprites. + spritepresent = doom_malloc(numsprites); + doom_memset(spritepresent, 0, numsprites); + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_MobjThinker) + spritepresent[((mobj_t*)th)->sprite] = 1; + } + + spritememory = 0; + for (i = 0; i < numsprites; i++) + { + if (!spritepresent[i]) + continue; + + for (j = 0; j < sprites[i].numframes; j++) + { + sf = &sprites[i].spriteframes[j]; + for (k = 0; k < 8; k++) + { + lump = firstspritelump + sf->lump[k]; + spritememory += lumpinfo[lump].size; + W_CacheLumpNum(lump, PU_CACHE); + } + } + } + + doom_free(texturepresent); + doom_free(flatpresent); + doom_free(spritepresent); +} diff --git a/src/DOOM/r_data.h b/src/DOOM/r_data.h new file mode 100644 index 0000000..ae4fab4 --- /dev/null +++ b/src/DOOM/r_data.h @@ -0,0 +1,54 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Refresh module, data I/O, caching, retrieval of graphics +// by name. +// +//----------------------------------------------------------------------------- + +#ifndef __R_DATA__ +#define __R_DATA__ + + +#include "r_defs.h" +#include "r_state.h" + + +// Retrieve column data for span blitting. +byte* R_GetColumn(int tex, int col); + +// I/O, setting up the stuff. +void R_InitData(void); +void R_PrecacheLevel(void); + +// Retrieval. +// Floor/ceiling opaque texture tiles, +// lookup by name. For animation? +int R_FlatNumForName(char* name); + +// Called by P_Ticker for switches and animations, +// returns the texture number for the texture name. +int R_TextureNumForName(char* name); +int R_CheckTextureNumForName(char* name); + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/r_defs.h b/src/DOOM/r_defs.h new file mode 100644 index 0000000..0a24f6b --- /dev/null +++ b/src/DOOM/r_defs.h @@ -0,0 +1,434 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Refresh/rendering module, shared data struct definitions. +// +//----------------------------------------------------------------------------- + +#ifndef __R_DEFS__ +#define __R_DEFS__ + + +// Screenwidth. +#include "doomdef.h" + +// Some more or less basic data types +// we depend on. +#include "m_fixed.h" + +// We rely on the thinker data struct +// to handle sound origins in sectors. +#include "d_think.h" +// SECTORS do store MObjs anyway. +#include "p_mobj.h" + + +// Silhouette, needed for clipping Segs (mainly) +// and sprites representing things. +#define SIL_NONE 0 +#define SIL_BOTTOM 1 +#define SIL_TOP 2 +#define SIL_BOTH 3 + +#define MAXDRAWSEGS 256 + + +// +// INTERNAL MAP TYPES +// used by play and refresh +// + +// +// Your plain vanilla vertex. +// Note: transformed values not buffered locally, +// like some DOOM-alikes ("wt", "WebView") did. +// +typedef struct +{ + fixed_t x; + fixed_t y; +} vertex_t; + + +// Forward of LineDefs, for Sectors. +struct line_s; + +// Each sector has a degenmobj_t in its center +// for sound origin purposes. +// I suppose this does not handle sound from +// moving objects (doppler), because +// position is prolly just buffered, not +// updated. +typedef struct +{ + thinker_t thinker; // not used for anything + fixed_t x; + fixed_t y; + fixed_t z; +} degenmobj_t; + +// +// The SECTORS record, at runtime. +// Stores things/mobjs. +// +typedef struct +{ + fixed_t floorheight; + fixed_t ceilingheight; + short floorpic; + short ceilingpic; + short lightlevel; + short special; + short tag; + + // 0 = untraversed, 1,2 = sndlines -1 + int soundtraversed; + + // thing that made a sound (or null) + mobj_t* soundtarget; + + // mapblock bounding box for height changes + int blockbox[4]; + + // origin for any sounds played by the sector + degenmobj_t soundorg; + + // if == validcount, already checked + int validcount; + + // list of mobjs in sector + mobj_t* thinglist; + + // thinker_t for reversable actions + void* specialdata; + + int linecount; + struct line_s** lines; // [linecount] size +} sector_t; + + +// +// The SideDef. +// +typedef struct +{ + // add this to the calculated texture column + fixed_t textureoffset; + + // add this to the calculated texture top + fixed_t rowoffset; + + // Texture indices. + // We do not maintain names here. + short toptexture; + short bottomtexture; + short midtexture; + + // Sector the SideDef is facing. + sector_t* sector; +} side_t; + + +// +// Move clipping aid for LineDefs. +// +typedef enum +{ + ST_HORIZONTAL, + ST_VERTICAL, + ST_POSITIVE, + ST_NEGATIVE +} slopetype_t; + + +typedef struct line_s +{ + // Vertices, from v1 to v2. + vertex_t* v1; + vertex_t* v2; + + // Precalculated v2 - v1 for side checking. + fixed_t dx; + fixed_t dy; + + // Animation related. + short flags; + short special; + short tag; + + // Visual appearance: SideDefs. + // sidenum[1] will be -1 if one sided + short sidenum[2]; + + // Neat. Another bounding box, for the extent + // of the LineDef. + fixed_t bbox[4]; + + // To aid move clipping. + slopetype_t slopetype; + + // Front and back sector. + // Note: redundant? Can be retrieved from SideDefs. + sector_t* frontsector; + sector_t* backsector; + + // if == validcount, already checked + int validcount; + + // thinker_t for reversable actions + void* specialdata; +} line_t; + + +// +// A SubSector. +// References a Sector. +// Basically, this is a list of LineSegs, +// indicating the visible walls that define +// (all or some) sides of a convex BSP leaf. +// +typedef struct subsector_s +{ + sector_t* sector; + short numlines; + short firstline; +} subsector_t; + + +// +// The LineSeg. +// +typedef struct +{ + vertex_t* v1; + vertex_t* v2; + + fixed_t offset; + + angle_t angle; + + side_t* sidedef; + line_t* linedef; + + // Sector references. + // Could be retrieved from linedef, too. + // backsector is 0 for one sided lines + sector_t* frontsector; + sector_t* backsector; +} seg_t; + + +// +// BSP node. +// +typedef struct +{ + // Partition line. + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; + + // Bounding box for each child. + fixed_t bbox[2][4]; + + // If NF_SUBSECTOR its a subsector. + unsigned short children[2]; +} node_t; + + +// posts are runs of non masked source pixels +typedef struct +{ + byte topdelta; // -1 is the last post in a column + byte length; // length data bytes follows +} post_t; + +// column_t is a list of 0 or more post_t, (byte)-1 terminated +typedef post_t column_t; + + +// +// OTHER TYPES +// + +// This could be wider for >8 bit display. +// Indeed, true color support is posibble +// precalculating 24bpp lightmap/colormap LUT. +// from darkening PLAYPAL to all black. +// Could even us emore than 32 levels. +typedef byte lighttable_t; + + +// +// ? +// +typedef struct drawseg_s +{ + seg_t* curline; + int x1; + int x2; + + fixed_t scale1; + fixed_t scale2; + fixed_t scalestep; + + // 0=none, 1=bottom, 2=top, 3=both + int silhouette; + + // do not clip sprites above this + fixed_t bsilheight; + + // do not clip sprites below this + fixed_t tsilheight; + + // Pointers to lists for sprite clipping, + // all three adjusted so [x1] is first value. + short* sprtopclip; + short* sprbottomclip; + short* maskedtexturecol; +} drawseg_t; + + +// Patches. +// A patch holds one or more columns. +// Patches are used for sprites and all masked pictures, +// and we compose textures from the TEXTURE1/2 lists +// of patches. +typedef struct +{ + short width; // bounding box size + short height; + short leftoffset; // pixels to the left of origin + short topoffset; // pixels below the origin + int columnofs[8]; // only [width] used + // the [0] is &columnofs[width] +} patch_t; + + +// A vissprite_t is a thing +// that will be drawn during a refresh. +// I.e. a sprite object that is partly visible. +typedef struct vissprite_s +{ + // Doubly linked list. + struct vissprite_s* prev; + struct vissprite_s* next; + + int x1; + int x2; + + // for line side calculation + fixed_t gx; + fixed_t gy; + + // global bottom / top for silhouette clipping + fixed_t gz; + fixed_t gzt; + + // horizontal position of x1 + fixed_t startfrac; + + fixed_t scale; + + // negative if flipped + fixed_t xiscale; + + fixed_t texturemid; + int patch; + + // for color translation and shadow draw, + // maxbright frames as well + lighttable_t* colormap; + + int mobjflags; +} vissprite_t; + + +// +// Sprites are patches with a special naming convention +// so they can be recognized by R_InitSprites. +// The base name is NNNNFx or NNNNFxFx, with +// x indicating the rotation, x = 0, 1-7. +// The sprite and frame specified by a thing_t +// is range checked at run time. +// A sprite is a patch_t that is assumed to represent +// a three dimensional object and may have multiple +// rotations pre drawn. +// Horizontal flipping is used to save space, +// thus NNNNF2F5 defines a mirrored patch. +// Some sprites will only have one picture used +// for all views: NNNNF0 +// +typedef struct +{ + // If false use 0 for any position. + // Note: as eight entries are available, + // we might as well insert the same name eight times. + doom_boolean rotate; + + // Lump to use for view angles 0-7. + short lump[8]; + + // Flip bit (1 = flip) to use for view angles 0-7. + byte flip[8]; +} spriteframe_t; + + +// +// A sprite definition: +// a number of animation frames. +// +typedef struct +{ + int numframes; + spriteframe_t* spriteframes; +} spritedef_t; + + +// +// Now what is a visplane, anyway? +// +typedef struct +{ + fixed_t height; + int picnum; + int lightlevel; + int minx; + int maxx; + + // leave pads for [minx-1]/[maxx+1] + + byte pad1; + // Here lies the rub for all + // dynamic resize/change of resolution. + byte top[SCREENWIDTH]; + byte pad2; + byte pad3; + // See above. + byte bottom[SCREENWIDTH]; + byte pad4; +} visplane_t; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/r_draw.c b/src/DOOM/r_draw.c new file mode 100644 index 0000000..6ee9e1e --- /dev/null +++ b/src/DOOM/r_draw.c @@ -0,0 +1,710 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// The actual span/column drawing functions. +// Here find the main potential for optimization, +// e.g. inline assembly, different algorithms. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "doomdef.h" +#include "i_system.h" +#include "z_zone.h" +#include "w_wad.h" +#include "r_local.h" +#include "v_video.h" // Needs access to LFB (guess what). +#include "doomstat.h" // State. + + +// ? +#define MAXWIDTH 1120 +#define MAXHEIGHT 832 + +// status bar height at bottom of screen +#define SBARHEIGHT 32 + +#define FUZZTABLE 50 +#define FUZZOFF (SCREENWIDTH) + + +// +// All drawing to the view buffer is accomplished in this file. +// The other refresh files only know about ccordinates, +// not the architecture of the frame buffer. +// Conveniently, the frame buffer is a linear one, +// and we need only the base address, +// and the total size == width*height*depth/8., +// + +byte* viewimage; +int viewwidth; +int scaledviewwidth; +int viewheight; +int viewwindowx; +int viewwindowy; +byte* ylookup[MAXHEIGHT]; +int columnofs[MAXWIDTH]; + +// Color tables for different players, +// translate a limited part to another +// (color ramps used for suit colors). +// +byte translations[3][256]; + +// +// R_DrawColumn +// Source is the top of the column to scale. +// +lighttable_t* dc_colormap; +int dc_x; +int dc_yl; +int dc_yh; +fixed_t dc_iscale; +fixed_t dc_texturemid; + +// first pixel in a column (possibly virtual) +byte* dc_source; + +// just for profiling +int dccount; + +int fuzzoffset[FUZZTABLE] = +{ + FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF +}; + +int fuzzpos = 0; + +byte* dc_translation; +byte* translationtables; + + +// +// A column is a vertical slice/span from a wall texture that, +// given the DOOM style restrictions on the view orientation, +// will always have constant z depth. +// Thus a special case loop for very fast rendering can +// be used. It has also been used with Wolfenstein 3D. +// +void R_DrawColumn(void) +{ + int count; + byte* dest; + fixed_t frac; + fixed_t fracstep; + + count = dc_yh - dc_yl; + + // Zero length, column does not exceed a pixel. + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH + || dc_yl < 0 + || dc_yh >= SCREENHEIGHT) + { + //I_Error("Error: R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); + + doom_strcpy(error_buf, "Error: R_DrawColumn: "); + doom_concat(error_buf, doom_itoa(dc_yl, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(dc_yh, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(dc_x, 10)); + I_Error(error_buf); + } +#endif + + // Framebuffer destination address. + // Use ylookup LUT to avoid multiply with ScreenWidth. + // Use columnofs LUT for subwindows? + dest = ylookup[dc_yl] + columnofs[dc_x]; + + // Determine scaling, + // which is the only mapping to be done. + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + // Inner loop that does the actual texture mapping, + // e.g. a DDA-lile scaling. + // This is as fast as it gets. + do + { + // Re-map color indices from wall texture column + // using a lighting/special effects LUT. + *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]]; + + dest += SCREENWIDTH; + frac += fracstep; + + } while (count--); +} + + +void R_DrawColumnLow(void) +{ + int count; + byte* dest; + byte* dest2; + fixed_t frac; + fixed_t fracstep; + + count = dc_yh - dc_yl; + + // Zero length. + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH + || dc_yl < 0 + || dc_yh >= SCREENHEIGHT) + { + //I_Error("Error: R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); + + doom_strcpy(error_buf, "Error: R_DrawColumn: "); + doom_concat(error_buf, doom_itoa(dc_yl, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(dc_yh, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(dc_x, 10)); + I_Error(error_buf); + } +#endif + // Blocky mode, need to multiply by 2. + dc_x <<= 1; + + dest = ylookup[dc_yl] + columnofs[dc_x]; + dest2 = ylookup[dc_yl] + columnofs[dc_x + 1]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + do + { + // Hack. Does not work corretly. + *dest2 = *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]]; + dest += SCREENWIDTH; + dest2 += SCREENWIDTH; + frac += fracstep; + + } while (count--); +} + + +// +// Spectre/Invisibility. +// + +// +// Framebuffer postprocessing. +// Creates a fuzzy image by copying pixels +// from adjacent ones to left and right. +// Used with an all black colormap, this +// could create the SHADOW effect, +// i.e. spectres and invisible players. +// +void R_DrawFuzzColumn(void) +{ + int count; + byte* dest; + fixed_t frac; + fixed_t fracstep; + + // Adjust borders. Low... + if (!dc_yl) + dc_yl = 1; + + // .. and high. + if (dc_yh == viewheight - 1) + dc_yh = viewheight - 2; + + count = dc_yh - dc_yl; + + // Zero length. + if (count < 0) + return; + + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH + || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + //I_Error("Error: R_DrawFuzzColumn: %i to %i at %i", + // dc_yl, dc_yh, dc_x); + + doom_strcpy(error_buf, "Error: R_DrawFuzzColumn: "); + doom_concat(error_buf, doom_itoa(dc_yl, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(dc_yh, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(dc_x, 10)); + I_Error(error_buf); + } +#endif + + // Does not work with blocky mode. + dest = ylookup[dc_yl] + columnofs[dc_x]; + + // Looks familiar. + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + // Looks like an attempt at dithering, + // using the colormap #6 (of 0-31, a bit + // brighter than average). + do + { + // Lookup framebuffer, and retrieve + // a pixel that is either one column + // left or right of the current one. + // Add index from colormap to index. + *dest = colormaps[6 * 256 + dest[fuzzoffset[fuzzpos]]]; + + // Clamp table lookup index. + if (++fuzzpos == FUZZTABLE) + fuzzpos = 0; + + dest += SCREENWIDTH; + + frac += fracstep; + } while (count--); +} + + +// +// R_DrawTranslatedColumn +// Used to draw player sprites +// with the green colorramp mapped to others. +// Could be used with different translation +// tables, e.g. the lighter colored version +// of the BaronOfHell, the HellKnight, uses +// identical sprites, kinda brightened up. +// +void R_DrawTranslatedColumn(void) +{ + int count; + byte* dest; + fixed_t frac; + fixed_t fracstep; + + count = dc_yh - dc_yl; + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH + || dc_yl < 0 + || dc_yh >= SCREENHEIGHT) + { + //I_Error("Error: R_DrawColumn: %i to %i at %i", + // dc_yl, dc_yh, dc_x); + + doom_strcpy(error_buf, "Error: R_DrawColumn: "); + doom_concat(error_buf, doom_itoa(dc_yl, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(dc_yh, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(dc_x, 10)); + I_Error(error_buf); + } +#endif + + // FIXME. As above. + dest = ylookup[dc_yl] + columnofs[dc_x]; + + // Looks familiar. + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + // Here we do an additional index re-mapping. + do + { + // Translation tables are used + // to map certain colorramps to other ones, + // used with PLAY sprites. + // Thus the "green" ramp of the player 0 sprite + // is mapped to gray, red, black/indigo. + *dest = dc_colormap[dc_translation[dc_source[frac >> FRACBITS]]]; + dest += SCREENWIDTH; + + frac += fracstep; + } while (count--); +} + + +// +// R_InitTranslationTables +// Creates the translation tables to map +// the green color ramp to gray, brown, red. +// Assumes a given structure of the PLAYPAL. +// Could be read from a lump instead. +// +void R_InitTranslationTables(void) +{ + int i; + + translationtables = Z_Malloc(256 * 3 + 255, PU_STATIC, 0); + translationtables = (byte*)(((unsigned long long)translationtables + 255) & ~255); + + // translate just the 16 green colors + for (i = 0; i < 256; i++) + { + if (i >= 0x70 && i <= 0x7f) + { + // map green ramp to gray, brown, red + translationtables[i] = 0x60 + (i & 0xf); + translationtables[i + 256] = 0x40 + (i & 0xf); + translationtables[i + 512] = 0x20 + (i & 0xf); + } + else + { + // Keep all other colors as is. + translationtables[i] = translationtables[i + 256] + = translationtables[i + 512] = i; + } + } +} + + +// +// R_DrawSpan +// With DOOM style restrictions on view orientation, +// the floors and ceilings consist of horizontal slices +// or spans with constant z depth. +// However, rotation around the world z axis is possible, +// thus this mapping, while simpler and faster than +// perspective correct texture mapping, has to traverse +// the texture at an angle in all but a few cases. +// In consequence, flats are not stored by column (like walls), +// and the inner loop has to step in texture space u and v. +// +int ds_y; +int ds_x1; +int ds_x2; + +lighttable_t* ds_colormap; + +fixed_t ds_xfrac; +fixed_t ds_yfrac; +fixed_t ds_xstep; +fixed_t ds_ystep; + +// start of a 64*64 tile image +byte* ds_source; + +// just for profiling +int dscount; + + +// +// Draws the actual span. +void R_DrawSpan(void) +{ + fixed_t xfrac; + fixed_t yfrac; + byte* dest; + int count; + int spot; + +#ifdef RANGECHECK + if (ds_x2 < ds_x1 + || ds_x1<0 + || ds_x2 >= SCREENWIDTH + || (unsigned)ds_y>SCREENHEIGHT) + { + //I_Error("Error: R_DrawSpan: %i to %i at %i", + // ds_x1, ds_x2, ds_y); + + doom_strcpy(error_buf, "Error: R_DrawSpan: "); + doom_concat(error_buf, doom_itoa(ds_x1, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(ds_x2, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(ds_y, 10)); + I_Error(error_buf); + } +#endif + + xfrac = ds_xfrac; + yfrac = ds_yfrac; + + dest = ylookup[ds_y] + columnofs[ds_x1]; + + // We do not check for zero spans here? + count = ds_x2 - ds_x1; + + do + { + // Current texture index in u,v. + spot = ((yfrac >> (16 - 6)) & (63 * 64)) + ((xfrac >> 16) & 63); + + // Lookup pixel from flat texture tile, + // re-index using light/colormap. + *dest++ = ds_colormap[ds_source[spot]]; + + // Next step in u,v. + xfrac += ds_xstep; + yfrac += ds_ystep; + + } while (count--); +} + + +// +// Again.. +// +void R_DrawSpanLow(void) +{ + fixed_t xfrac; + fixed_t yfrac; + byte* dest; + int count; + int spot; + +#ifdef RANGECHECK + if (ds_x2 < ds_x1 + || ds_x1<0 + || ds_x2 >= SCREENWIDTH + || (unsigned)ds_y>SCREENHEIGHT) + { + //I_Error("Error: R_DrawSpan: %i to %i at %i", + // ds_x1, ds_x2, ds_y); + + + doom_strcpy(error_buf, "Error: R_DrawSpan: "); + doom_concat(error_buf, doom_itoa(ds_x1, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(ds_x2, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(ds_y, 10)); + I_Error(error_buf); + } +#endif + + xfrac = ds_xfrac; + yfrac = ds_yfrac; + + // Blocky mode, need to multiply by 2. + ds_x1 <<= 1; + ds_x2 <<= 1; + + dest = ylookup[ds_y] + columnofs[ds_x1]; + + count = ds_x2 - ds_x1; + do + { + spot = ((yfrac >> (16 - 6)) & (63 * 64)) + ((xfrac >> 16) & 63); + // Lowres/blocky mode does it twice, + // while scale is adjusted appropriately. + *dest++ = ds_colormap[ds_source[spot]]; + *dest++ = ds_colormap[ds_source[spot]]; + + xfrac += ds_xstep; + yfrac += ds_ystep; + + } while (count--); +} + + +// +// R_InitBuffer +// Creats lookup tables that avoid +// multiplies and other hazzles +// for getting the framebuffer address +// of a pixel to draw. +// +void R_InitBuffer(int width, int height) +{ + int i; + + // Handle resize, + // e.g. smaller view windows + // with border and/or status bar. + viewwindowx = (SCREENWIDTH - width) >> 1; + + // Column offset. For windows. + for (i = 0; i < width; i++) + columnofs[i] = viewwindowx + i; + + // Samw with base row offset. + if (width == SCREENWIDTH) + viewwindowy = 0; + else + viewwindowy = (SCREENHEIGHT - SBARHEIGHT - height) >> 1; + + // Preclaculate all row offsets. + for (i = 0; i < height; i++) + ylookup[i] = screens[0] + (i + viewwindowy) * SCREENWIDTH; +} + + +// +// R_FillBackScreen +// Fills the back screen with a pattern +// for variable screen sizes +// Also draws a beveled edge. +// +void R_FillBackScreen(void) +{ + byte* src; + byte* dest; + int x; + int y; + patch_t* patch; + + // DOOM border patch. + char name1[] = "FLOOR7_2"; + + // DOOM II border patch. + char name2[] = "GRNROCK"; + + char* name; + + if (scaledviewwidth == 320) + return; + + if (gamemode == commercial) + name = name2; + else + name = name1; + + src = W_CacheLumpName(name, PU_CACHE); + dest = screens[1]; + + for (y = 0; y < SCREENHEIGHT - SBARHEIGHT; y++) + { + for (x = 0; x < SCREENWIDTH / 64; x++) + { + doom_memcpy(dest, src + ((y & 63) << 6), 64); + dest += 64; + } + + if (SCREENWIDTH & 63) + { + doom_memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); + dest += (SCREENWIDTH & 63); + } + } + + patch = W_CacheLumpName("brdr_t", PU_CACHE); + + for (x = 0; x < scaledviewwidth; x += 8) + V_DrawPatch(viewwindowx + x, viewwindowy - 8, 1, patch); + patch = W_CacheLumpName("brdr_b", PU_CACHE); + + for (x = 0; x < scaledviewwidth; x += 8) + V_DrawPatch(viewwindowx + x, viewwindowy + viewheight, 1, patch); + patch = W_CacheLumpName("brdr_l", PU_CACHE); + + for (y = 0; y < viewheight; y += 8) + V_DrawPatch(viewwindowx - 8, viewwindowy + y, 1, patch); + patch = W_CacheLumpName("brdr_r", PU_CACHE); + + for (y = 0; y < viewheight; y += 8) + V_DrawPatch(viewwindowx + scaledviewwidth, viewwindowy + y, 1, patch); + + + // Draw beveled edge. + V_DrawPatch(viewwindowx - 8, + viewwindowy - 8, + 1, + W_CacheLumpName("brdr_tl", PU_CACHE)); + + V_DrawPatch(viewwindowx + scaledviewwidth, + viewwindowy - 8, + 1, + W_CacheLumpName("brdr_tr", PU_CACHE)); + + V_DrawPatch(viewwindowx - 8, + viewwindowy + viewheight, + 1, + W_CacheLumpName("brdr_bl", PU_CACHE)); + + V_DrawPatch(viewwindowx + scaledviewwidth, + viewwindowy + viewheight, + 1, + W_CacheLumpName("brdr_br", PU_CACHE)); +} + + +// +// Copy a screen buffer. +// +void R_VideoErase(unsigned ofs, int count) +{ + // LFB copy. + // This might not be a good idea if memcpy + // is not optiomal, e.g. byte by byte on + // a 32bit CPU, as GNU GCC/Linux libc did + // at one point. + doom_memcpy(screens[0] + ofs, screens[1] + ofs, count); +} + + +// +// R_DrawViewBorder +// Draws the border around the view +// for different size windows? +// +void V_MarkRect(int x, int y, int width, int height); + +void R_DrawViewBorder(void) +{ + int top; + int side; + int ofs; + int i; + + if (scaledviewwidth == SCREENWIDTH) + return; + + top = ((SCREENHEIGHT - SBARHEIGHT) - viewheight) / 2; + side = (SCREENWIDTH - scaledviewwidth) / 2; + + // copy top and one line of left side + R_VideoErase(0, top * SCREENWIDTH + side); + + // copy one line of right side and bottom + ofs = (viewheight + top) * SCREENWIDTH - side; + R_VideoErase(ofs, top * SCREENWIDTH + side); + + // copy sides using wraparound + ofs = top * SCREENWIDTH + SCREENWIDTH - side; + side <<= 1; + + for (i = 1; i < viewheight; i++) + { + R_VideoErase(ofs, side); + ofs += SCREENWIDTH; + } + + // ? + V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT - SBARHEIGHT); +} diff --git a/src/DOOM/r_draw.h b/src/DOOM/r_draw.h new file mode 100644 index 0000000..562eb39 --- /dev/null +++ b/src/DOOM/r_draw.h @@ -0,0 +1,103 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// System specific interface stuff. +// +//----------------------------------------------------------------------------- + +#ifndef __R_DRAW__ +#define __R_DRAW__ + + +#include "r_defs.h" + + +extern lighttable_t* dc_colormap; +extern int dc_x; +extern int dc_yl; +extern int dc_yh; +extern fixed_t dc_iscale; +extern fixed_t dc_texturemid; + +// first pixel in a column +extern byte* dc_source; + + +// The span blitting interface. +// Hook in assembler or system specific BLT +// here. +void R_DrawColumn(void); +void R_DrawColumnLow(void); + +// The Spectre/Invisibility effect. +void R_DrawFuzzColumn(void); +void R_DrawFuzzColumnLow(void); + +// Draw with color translation tables, +// for player sprite rendering, +// Green/Red/Blue/Indigo shirts. +void R_DrawTranslatedColumn(void); +void R_DrawTranslatedColumnLow(void); + +void R_VideoErase(unsigned ofs, int count); + +extern int ds_y; +extern int ds_x1; +extern int ds_x2; + +extern lighttable_t* ds_colormap; + +extern fixed_t ds_xfrac; +extern fixed_t ds_yfrac; +extern fixed_t ds_xstep; +extern fixed_t ds_ystep; + +// start of a 64*64 tile image +extern byte* ds_source; + +extern byte* translationtables; +extern byte* dc_translation; + + +// Span blitting for rows, floor/ceiling. +// No Sepctre effect needed. +void R_DrawSpan(void); + +// Low resolution mode, 160x200? +void R_DrawSpanLow(void); + +void R_InitBuffer(int width, int height); + + +// Initialize color translation tables, +// for player rendering etc. +void R_InitTranslationTables(void); + +// Rendering function. +void R_FillBackScreen(void); + +// If the view size is not full screen, draws a border around it. +void R_DrawViewBorder(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/r_local.h b/src/DOOM/r_local.h new file mode 100644 index 0000000..170abf9 --- /dev/null +++ b/src/DOOM/r_local.h @@ -0,0 +1,52 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Refresh (R_*) module, global header. +// All the rendering/drawing stuff is here. +// +//----------------------------------------------------------------------------- + +#ifndef __R_LOCAL__ +#define __R_LOCAL__ + +// Binary Angles, sine/cosine/atan lookups. +#include "tables.h" + +// Screen size related parameters. +#include "doomdef.h" + +// Include the refresh/render data structs. +#include "r_data.h" + +// +// Separate header file for each module. +// +#include "r_main.h" +#include "r_bsp.h" +#include "r_segs.h" +#include "r_plane.h" +#include "r_data.h" +#include "r_things.h" +#include "r_draw.h" + +#endif // __R_LOCAL__ + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/r_main.c b/src/DOOM/r_main.c new file mode 100644 index 0000000..3a456b8 --- /dev/null +++ b/src/DOOM/r_main.c @@ -0,0 +1,777 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Rendering main loop and setup functions, +// utility functions (BSP, geometry, trigonometry). +// See tables.c, too. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + + +#include "doomdef.h" +#include "d_net.h" +#include "m_bbox.h" +#include "r_local.h" +#include "r_sky.h" + + +#define FIELDOFVIEW 2048 // Fineangles in the SCREENWIDTH wide window. +#define DISTMAP 2 + + +int viewangleoffset; + +// increment every time a check is made +int validcount = 1; + +lighttable_t* fixedcolormap; + +int centerx; +int centery; + +fixed_t centerxfrac; +fixed_t centeryfrac; +fixed_t projection; + +// just for profiling purposes +int framecount; + +int sscount; +int linecount; +int loopcount; + +fixed_t viewx; +fixed_t viewy; +fixed_t viewz; + +angle_t viewangle; + +fixed_t viewcos; +fixed_t viewsin; + +player_t* viewplayer; + +// 0 = high, 1 = low +int detailshift; + +// +// precalculated math tables +// +angle_t clipangle; + +// The viewangletox[viewangle + FINEANGLES/4] lookup +// maps the visible view angles to screen X coordinates, +// flattening the arc to a flat projection plane. +// There will be many angles mapped to the same X. +int viewangletox[FINEANGLES / 2]; + +// The xtoviewangleangle[] table maps a screen pixel +// to the lowest viewangle that maps back to x ranges +// from clipangle to -clipangle. +angle_t xtoviewangle[SCREENWIDTH + 1]; + +fixed_t* finecosine = &finesine[FINEANGLES / 4]; + +lighttable_t* scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; +lighttable_t* scalelightfixed[MAXLIGHTSCALE]; +lighttable_t* zlight[LIGHTLEVELS][MAXLIGHTZ]; + +// bumped light from gun blasts +int extralight; + +doom_boolean setsizeneeded; +int setblocks; +int setdetail; + + +extern lighttable_t** walllights; +extern int detailLevel; +extern int screenblocks; + + +void (*colfunc) (void); +void (*basecolfunc) (void); +void (*fuzzcolfunc) (void); +void (*transcolfunc) (void); +void (*spanfunc) (void); + + +// +// R_AddPointToBox +// Expand a given bbox +// so that it encloses a given point. +// +void R_AddPointToBox(int x, int y, fixed_t* box) +{ + if (x < box[BOXLEFT]) + box[BOXLEFT] = x; + if (x > box[BOXRIGHT]) + box[BOXRIGHT] = x; + if (y < box[BOXBOTTOM]) + box[BOXBOTTOM] = y; + if (y > box[BOXTOP]) + box[BOXTOP] = y; +} + + +// +// R_PointOnSide +// Traverse BSP (sub) tree, +// check point against partition plane. +// Returns side 0 (front) or 1 (back). +// +int R_PointOnSide(fixed_t x, fixed_t y, node_t* node) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!node->dx) + { + if (x <= node->x) + return node->dy > 0; + + return node->dy < 0; + } + if (!node->dy) + { + if (y <= node->y) + return node->dx < 0; + + return node->dx > 0; + } + + dx = (x - node->x); + dy = (y - node->y); + + // Try to quickly decide by looking at sign bits. + if ((node->dy ^ node->dx ^ dx ^ dy) & 0x80000000) + { + if ((node->dy ^ dx) & 0x80000000) + { + // (left is negative) + return 1; + } + return 0; + } + + left = FixedMul(node->dy >> FRACBITS, dx); + right = FixedMul(dy, node->dx >> FRACBITS); + + if (right < left) + { + // front side + return 0; + } + // back side + return 1; +} + + +int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t* line) +{ + fixed_t lx; + fixed_t ly; + fixed_t ldx; + fixed_t ldy; + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + lx = line->v1->x; + ly = line->v1->y; + + ldx = line->v2->x - lx; + ldy = line->v2->y - ly; + + if (!ldx) + { + if (x <= lx) + return ldy > 0; + + return ldy < 0; + } + if (!ldy) + { + if (y <= ly) + return ldx < 0; + + return ldx > 0; + } + + dx = (x - lx); + dy = (y - ly); + + // Try to quickly decide by looking at sign bits. + if ((ldy ^ ldx ^ dx ^ dy) & 0x80000000) + { + if ((ldy ^ dx) & 0x80000000) + { + // (left is negative) + return 1; + } + return 0; + } + + left = FixedMul(ldy >> FRACBITS, dx); + right = FixedMul(dy, ldx >> FRACBITS); + + if (right < left) + { + // front side + return 0; + } + // back side + return 1; +} + + +// +// R_PointToAngle +// To get a global angle from cartesian coordinates, +// the coordinates are flipped until they are in +// the first octant of the coordinate system, then +// the y (<=x) is scaled and divided by x to get a +// tangent (slope) value which is looked up in the +// tantoangle[] table. + +angle_t R_PointToAngle(fixed_t x, fixed_t y) +{ + x -= viewx; + y -= viewy; + + if ((!x) && (!y)) + return 0; + + if (x >= 0) + { + // x >=0 + if (y >= 0) + { + // y>= 0 + + if (x > y) + { + // octant 0 + return tantoangle[SlopeDiv(y, x)]; + } + else + { + // octant 1 + return ANG90 - 1 - tantoangle[SlopeDiv(x, y)]; + } + } + else + { + // y<0 + y = -y; + + if (x > y) + { + // octant 8 +#pragma warning(push) +#pragma warning(disable : 4146) + return -tantoangle[SlopeDiv(y, x)]; +#pragma warning(pop) + } + else + { + // octant 7 + return ANG270 + tantoangle[SlopeDiv(x, y)]; + } + } + } + else + { + // x<0 + x = -x; + + if (y >= 0) + { + // y>= 0 + if (x > y) + { + // octant 3 + return ANG180 - 1 - tantoangle[SlopeDiv(y, x)]; + } + else + { + // octant 2 + return ANG90 + tantoangle[SlopeDiv(x, y)]; + } + } + else + { + // y<0 + y = -y; + + if (x > y) + { + // octant 4 + return ANG180 + tantoangle[SlopeDiv(y, x)]; + } + else + { + // octant 5 + return ANG270 - 1 - tantoangle[SlopeDiv(x, y)]; + } + } + } + return 0; +} + + +angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + viewx = x1; + viewy = y1; + + return R_PointToAngle(x2, y2); +} + + +fixed_t R_PointToDist(fixed_t x, fixed_t y) +{ + int angle; + fixed_t dx; + fixed_t dy; + fixed_t temp; + fixed_t dist; + + dx = doom_abs(x - viewx); + dy = doom_abs(y - viewy); + + if (dy > dx) + { + temp = dx; + dx = dy; + dy = temp; + } + + angle = (tantoangle[FixedDiv(dy, dx) >> DBITS] + ANG90) >> ANGLETOFINESHIFT; + + // use as cosine + dist = FixedDiv(dx, finesine[angle]); + + return dist; +} + + +// +// R_InitPointToAngle +// +void R_InitPointToAngle(void) +{ +} + + +// +// R_ScaleFromGlobalAngle +// Returns the texture mapping scale +// for the current line (horizontal span) +// at the given angle. +// rw_distance must be calculated first. +// +fixed_t R_ScaleFromGlobalAngle(angle_t visangle) +{ + fixed_t scale; + int anglea; + int angleb; + int sinea; + int sineb; + fixed_t num; + int den; + + anglea = ANG90 + (visangle - viewangle); + angleb = ANG90 + (visangle - rw_normalangle); + + // both sines are allways positive + sinea = finesine[anglea >> ANGLETOFINESHIFT]; + sineb = finesine[angleb >> ANGLETOFINESHIFT]; + num = FixedMul(projection, sineb) << detailshift; + den = FixedMul(rw_distance, sinea); + + if (den > num >> 16) + { + scale = FixedDiv(num, den); + + if (scale > 64 * FRACUNIT) + scale = 64 * FRACUNIT; + else if (scale < 256) + scale = 256; + } + else + scale = 64 * FRACUNIT; + + return scale; +} + + +// +// R_InitTables +// +void R_InitTables(void) +{ +} + + +// +// R_InitTextureMapping +// +void R_InitTextureMapping(void) +{ + int i; + int x; + int t; + fixed_t focallength; + + // Use tangent table to generate viewangletox: + // viewangletox will give the next greatest x + // after the view angle. + // + // Calc focallength + // so FIELDOFVIEW angles covers SCREENWIDTH. + focallength = FixedDiv(centerxfrac, + finetangent[FINEANGLES / 4 + FIELDOFVIEW / 2]); + + for (i = 0; i < FINEANGLES / 2; i++) + { + if (finetangent[i] > FRACUNIT * 2) + t = -1; + else if (finetangent[i] < -FRACUNIT * 2) + t = viewwidth + 1; + else + { + t = FixedMul(finetangent[i], focallength); + t = (centerxfrac - t + FRACUNIT - 1) >> FRACBITS; + + if (t < -1) + t = -1; + else if (t > viewwidth + 1) + t = viewwidth + 1; + } + viewangletox[i] = t; + } + + // Scan viewangletox[] to generate xtoviewangle[]: + // xtoviewangle will give the smallest view angle + // that maps to x. + for (x = 0; x <= viewwidth; x++) + { + i = 0; + while (viewangletox[i] > x) + i++; + xtoviewangle[x] = (i << ANGLETOFINESHIFT) - ANG90; + } + + // Take out the fencepost cases from viewangletox. + for (i = 0; i < FINEANGLES / 2; i++) + { + t = FixedMul(finetangent[i], focallength); + t = centerx - t; + + if (viewangletox[i] == -1) + viewangletox[i] = 0; + else if (viewangletox[i] == viewwidth + 1) + viewangletox[i] = viewwidth; + } + + clipangle = xtoviewangle[0]; +} + + +// +// R_InitLightTables +// Only inits the zlight table, +// because the scalelight table changes with view size. +// + +void R_InitLightTables(void) +{ + int i; + int j; + int level; + int startmap; + int scale; + + // Calculate the light levels to use + // for each level / distance combination. + for (i = 0; i < LIGHTLEVELS; i++) + { + startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; + for (j = 0; j < MAXLIGHTZ; j++) + { + scale = FixedDiv((SCREENWIDTH / 2 * FRACUNIT), (j + 1) << LIGHTZSHIFT); + scale >>= LIGHTSCALESHIFT; + level = startmap - scale / DISTMAP; + + if (level < 0) + level = 0; + + if (level >= NUMCOLORMAPS) + level = NUMCOLORMAPS - 1; + + zlight[i][j] = colormaps + level * 256; + } + } +} + + +// +// R_SetViewSize +// Do not really change anything here, +// because it might be in the middle of a refresh. +// The change will take effect next refresh. +// + +void R_SetViewSize(int blocks, int detail) +{ + setsizeneeded = true; + setblocks = blocks; + setdetail = detail; +} + + +// +// R_ExecuteSetViewSize +// +void R_ExecuteSetViewSize(void) +{ + fixed_t cosadj; + fixed_t dy; + int i; + int j; + int level; + int startmap; + + setsizeneeded = false; + + if (setblocks == 11) + { + scaledviewwidth = SCREENWIDTH; + viewheight = SCREENHEIGHT; + } + else + { + scaledviewwidth = setblocks * 32; + viewheight = (setblocks * 168 / 10) & ~7; + } + + detailshift = setdetail; + viewwidth = scaledviewwidth >> detailshift; + + centery = viewheight / 2; + centerx = viewwidth / 2; + centerxfrac = centerx << FRACBITS; + centeryfrac = centery << FRACBITS; + projection = centerxfrac; + + if (!detailshift) + { + colfunc = basecolfunc = R_DrawColumn; + fuzzcolfunc = R_DrawFuzzColumn; + transcolfunc = R_DrawTranslatedColumn; + spanfunc = R_DrawSpan; + } + else + { + colfunc = basecolfunc = R_DrawColumnLow; + fuzzcolfunc = R_DrawFuzzColumn; + transcolfunc = R_DrawTranslatedColumn; + spanfunc = R_DrawSpanLow; + } + + R_InitBuffer(scaledviewwidth, viewheight); + + R_InitTextureMapping(); + + // psprite scales + pspritescale = FRACUNIT * viewwidth / SCREENWIDTH; + pspriteiscale = FRACUNIT * SCREENWIDTH / viewwidth; + + // thing clipping + for (i = 0; i < viewwidth; i++) + screenheightarray[i] = viewheight; + + // planes + for (i = 0; i < viewheight; i++) + { + dy = ((i - viewheight / 2) << FRACBITS) + FRACUNIT / 2; + dy = doom_abs(dy); + yslope[i] = FixedDiv((viewwidth << detailshift) / 2 * FRACUNIT, dy); + } + + for (i = 0; i < viewwidth; i++) + { + cosadj = doom_abs(finecosine[xtoviewangle[i] >> ANGLETOFINESHIFT]); + distscale[i] = FixedDiv(FRACUNIT, cosadj); + } + + // Calculate the light levels to use + // for each level / scale combination. + for (i = 0; i < LIGHTLEVELS; i++) + { + startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; + for (j = 0; j < MAXLIGHTSCALE; j++) + { + level = startmap - j * SCREENWIDTH / (viewwidth << detailshift) / DISTMAP; + + if (level < 0) + level = 0; + + if (level >= NUMCOLORMAPS) + level = NUMCOLORMAPS - 1; + + scalelight[i][j] = colormaps + level * 256; + } + } +} + + +// +// R_Init +// +void R_Init(void) +{ + R_InitData(); + doom_print("\nR_InitData"); + R_InitPointToAngle(); + doom_print("\nR_InitPointToAngle"); + R_InitTables(); + // viewwidth / viewheight / detailLevel are set by the defaults + doom_print("\nR_InitTables"); + + R_SetViewSize(screenblocks, detailLevel); + R_InitPlanes(); + doom_print("\nR_InitPlanes"); + R_InitLightTables(); + doom_print("\nR_InitLightTables"); + R_InitSkyMap(); + doom_print("\nR_InitSkyMap"); + R_InitTranslationTables(); + doom_print("\nR_InitTranslationsTables"); + + framecount = 0; +} + + +// +// R_PointInSubsector +// +subsector_t* R_PointInSubsector(fixed_t x, fixed_t y) +{ + node_t* node; + int side; + int nodenum; + + // single subsector is a special case + if (!numnodes) + return subsectors; + + nodenum = numnodes - 1; + + while (!(nodenum & NF_SUBSECTOR)) + { + node = &nodes[nodenum]; + side = R_PointOnSide(x, y, node); + nodenum = node->children[side]; + } + + return &subsectors[nodenum & ~NF_SUBSECTOR]; +} + + +// +// R_SetupFrame +// +void R_SetupFrame(player_t* player) +{ + int i; + + viewplayer = player; + viewx = player->mo->x; + viewy = player->mo->y; + viewangle = player->mo->angle + viewangleoffset; + extralight = player->extralight; + + viewz = player->viewz; + + viewsin = finesine[viewangle >> ANGLETOFINESHIFT]; + viewcos = finecosine[viewangle >> ANGLETOFINESHIFT]; + + sscount = 0; + + if (player->fixedcolormap) + { + fixedcolormap = + colormaps + + player->fixedcolormap * 256 * sizeof(lighttable_t); + + walllights = scalelightfixed; + + for (i = 0; i < MAXLIGHTSCALE; i++) + scalelightfixed[i] = fixedcolormap; + } + else + fixedcolormap = 0; + + framecount++; + validcount++; +} + + +// +// R_RenderView +// +void R_RenderPlayerView(player_t* player) +{ + R_SetupFrame(player); + + // Clear buffers. + R_ClearClipSegs(); + R_ClearDrawSegs(); + R_ClearPlanes(); + R_ClearSprites(); + + // check for new console commands. + NetUpdate(); + + // The head node is the last node output. + R_RenderBSPNode(numnodes - 1); + + // Check for new console commands. + NetUpdate(); + + R_DrawPlanes(); + + // Check for new console commands. + NetUpdate(); + + R_DrawMasked(); + + // Check for new console commands. + NetUpdate(); +} diff --git a/src/DOOM/r_main.h b/src/DOOM/r_main.h new file mode 100644 index 0000000..5457b9a --- /dev/null +++ b/src/DOOM/r_main.h @@ -0,0 +1,130 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// System specific interface stuff. +// +//----------------------------------------------------------------------------- + +#ifndef __R_MAIN__ +#define __R_MAIN__ + +#include "d_player.h" +#include "r_data.h" + + +// +// POV related. +// +extern fixed_t viewcos; +extern fixed_t viewsin; + +extern int viewwidth; +extern int viewheight; +extern int viewwindowx; +extern int viewwindowy; + +extern int centerx; +extern int centery; + +extern fixed_t centerxfrac; +extern fixed_t centeryfrac; +extern fixed_t projection; + +extern int validcount; + +extern int linecount; +extern int loopcount; + + +// +// Lighting LUT. +// Used for z-depth cuing per column/row, +// and other lighting effects (sector ambient, flash). +// + +// Lighting constants. +// Now why not 32 levels here? +#define LIGHTLEVELS 16 +#define LIGHTSEGSHIFT 4 + +#define MAXLIGHTSCALE 48 +#define LIGHTSCALESHIFT 12 +#define MAXLIGHTZ 128 +#define LIGHTZSHIFT 20 + +extern lighttable_t* scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; +extern lighttable_t* scalelightfixed[MAXLIGHTSCALE]; +extern lighttable_t* zlight[LIGHTLEVELS][MAXLIGHTZ]; + +extern int extralight; +extern lighttable_t* fixedcolormap; + + +// Number of diminishing brightness levels. +// There a 0-31, i.e. 32 LUT in the COLORMAP lump. +#define NUMCOLORMAPS 32 + + +// Blocky/low detail mode. +//B remove this? +// 0 = high, 1 = low +extern int detailshift; + + +// +// Function pointers to switch refresh/drawing functions. +// Used to select shadow mode etc. +// +extern void (*colfunc) (void); +extern void (*basecolfunc) (void); +extern void (*fuzzcolfunc) (void); +// No shadow effects on floors. +extern void (*spanfunc) (void); + + +// +// Utility functions. +int R_PointOnSide(fixed_t x, fixed_t y, node_t* node); +int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t* line); +angle_t R_PointToAngle(fixed_t x, fixed_t y); +angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); +fixed_t R_PointToDist(fixed_t x, fixed_t y); +fixed_t R_ScaleFromGlobalAngle(angle_t visangle); +subsector_t* R_PointInSubsector(fixed_t x, fixed_t y); +void R_AddPointToBox(int x, int y, fixed_t* box); + + +// +// REFRESH - the actual rendering functions. +// + +// Called by G_Drawer. +void R_RenderPlayerView(player_t* player); + +// Called by startup code. +void R_Init(void); + +// Called by M_Responder. +void R_SetViewSize(int blocks, int detail); + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/r_plane.c b/src/DOOM/r_plane.c new file mode 100644 index 0000000..dd520c9 --- /dev/null +++ b/src/DOOM/r_plane.c @@ -0,0 +1,456 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Here is a core component: drawing the floors and ceilings, +// while maintaining a per column clipping list only. +// Moreover, the sky areas have to be determined. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "i_system.h" +#include "z_zone.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_local.h" +#include "r_sky.h" + + +#define MAXVISPLANES 128 +#define MAXOPENINGS SCREENWIDTH*64 + + +planefunction_t floorfunc; +planefunction_t ceilingfunc; + +// +// opening +// + +// Here comes the obnoxious "visplane". +visplane_t visplanes[MAXVISPLANES]; +visplane_t* lastvisplane; +visplane_t* floorplane; +visplane_t* ceilingplane; + +// ? +short openings[MAXOPENINGS]; +short* lastopening; + +// +// Clip values are the solid pixel bounding the range. +// floorclip starts out SCREENHEIGHT +// ceilingclip starts out -1 +// +short floorclip[SCREENWIDTH]; +short ceilingclip[SCREENWIDTH]; + +// +// spanstart holds the start of a plane span +// initialized to 0 at start +// +int spanstart[SCREENHEIGHT]; +int spanstop[SCREENHEIGHT]; + +// +// texture mapping +// +lighttable_t** planezlight; +fixed_t planeheight; + +fixed_t yslope[SCREENHEIGHT]; +fixed_t distscale[SCREENWIDTH]; +fixed_t basexscale; +fixed_t baseyscale; + +fixed_t cachedheight[SCREENHEIGHT]; +fixed_t cacheddistance[SCREENHEIGHT]; +fixed_t cachedxstep[SCREENHEIGHT]; +fixed_t cachedystep[SCREENHEIGHT]; + + +// +// R_InitPlanes +// Only at game startup. +// +void R_InitPlanes(void) +{ + // Doh! +} + + +// +// R_MapPlane +// +// Uses global vars: +// planeheight +// ds_source +// basexscale +// baseyscale +// viewx +// viewy +// +// BASIC PRIMITIVE +// +void R_MapPlane(int y, int x1, int x2) +{ + angle_t angle; + fixed_t distance; + fixed_t length; + unsigned index; + +#ifdef RANGECHECK + if (x2 < x1 + || x1<0 + || x2 >= viewwidth + || (unsigned)y>(unsigned)viewheight) + { + //I_Error("Error: R_MapPlane: %i, %i at %i", x1, x2, y); + + doom_strcpy(error_buf, "Error: R_MapPlane: "); + doom_concat(error_buf, doom_itoa(x1, 10)); + doom_concat(error_buf, ", "); + doom_concat(error_buf, doom_itoa(x2, 10)); + doom_concat(error_buf, " at "); + doom_concat(error_buf, doom_itoa(y, 10)); + I_Error(error_buf); + } +#endif + + if (planeheight != cachedheight[y]) + { + cachedheight[y] = planeheight; + distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]); + ds_xstep = cachedxstep[y] = FixedMul(distance, basexscale); + ds_ystep = cachedystep[y] = FixedMul(distance, baseyscale); + } + else + { + distance = cacheddistance[y]; + ds_xstep = cachedxstep[y]; + ds_ystep = cachedystep[y]; + } + + length = FixedMul(distance, distscale[x1]); + angle = (viewangle + xtoviewangle[x1]) >> ANGLETOFINESHIFT; + ds_xfrac = viewx + FixedMul(finecosine[angle], length); + ds_yfrac = -viewy - FixedMul(finesine[angle], length); + + if (fixedcolormap) + ds_colormap = fixedcolormap; + else + { + index = distance >> LIGHTZSHIFT; + + if (index >= MAXLIGHTZ) + index = MAXLIGHTZ - 1; + + ds_colormap = planezlight[index]; + } + + ds_y = y; + ds_x1 = x1; + ds_x2 = x2; + + // high or low detail + spanfunc(); +} + + +// +// R_ClearPlanes +// At begining of frame. +// +void R_ClearPlanes(void) +{ + int i; + angle_t angle; + + // opening / clipping determination + for (i = 0; i < viewwidth; i++) + { + floorclip[i] = viewheight; + ceilingclip[i] = -1; + } + + lastvisplane = visplanes; + lastopening = openings; + + // texture calculation + doom_memset(cachedheight, 0, sizeof(cachedheight)); + + // left to right mapping + angle = (viewangle - ANG90) >> ANGLETOFINESHIFT; + + // scale will be unit scale at SCREENWIDTH/2 distance + basexscale = FixedDiv(finecosine[angle], centerxfrac); + baseyscale = -FixedDiv(finesine[angle], centerxfrac); +} + + +// +// R_FindPlane +// +visplane_t* R_FindPlane(fixed_t height, int picnum, int lightlevel) +{ + visplane_t* check; + + if (picnum == skyflatnum) + { + height = 0; // all skys map together + lightlevel = 0; + } + + for (check = visplanes; check < lastvisplane; check++) + { + if (height == check->height + && picnum == check->picnum + && lightlevel == check->lightlevel) + { + break; + } + } + + if (check < lastvisplane) + return check; + + if (lastvisplane - visplanes == MAXVISPLANES) + I_Error("Error: R_FindPlane: no more visplanes"); + + lastvisplane++; + + check->height = height; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->minx = SCREENWIDTH; + check->maxx = -1; + + doom_memset(check->top, 0xff, sizeof(check->top)); + + return check; +} + + +// +// R_CheckPlane +// +visplane_t* R_CheckPlane(visplane_t* pl, int start, int stop) +{ + int intrl; + int intrh; + int unionl; + int unionh; + int x; + + if (start < pl->minx) + { + intrl = pl->minx; + unionl = start; + } + else + { + unionl = pl->minx; + intrl = start; + } + + if (stop > pl->maxx) + { + intrh = pl->maxx; + unionh = stop; + } + else + { + unionh = pl->maxx; + intrh = stop; + } + + for (x = intrl; x <= intrh; x++) + if (pl->top[x] != 0xff) + break; + + if (x > intrh) + { + pl->minx = unionl; + pl->maxx = unionh; + + // use the same one + return pl; + } + + // make a new visplane + lastvisplane->height = pl->height; + lastvisplane->picnum = pl->picnum; + lastvisplane->lightlevel = pl->lightlevel; + + pl = lastvisplane++; + pl->minx = start; + pl->maxx = stop; + + doom_memset(pl->top, 0xff, sizeof(pl->top)); + + return pl; +} + + +// +// R_MakeSpans +// +void R_MakeSpans(int x, int t1, int b1, int t2, int b2) +{ + while (t1 < t2 && t1 <= b1) + { + R_MapPlane(t1, spanstart[t1], x - 1); + t1++; + } + while (b1 > b2 && b1 >= t1) + { + R_MapPlane(b1, spanstart[b1], x - 1); + b1--; + } + + while (t2 < t1 && t2 <= b2) + { + spanstart[t2] = x; + t2++; + } + while (b2 > b1 && b2 >= t2) + { + spanstart[b2] = x; + b2--; + } +} + + +// +// R_DrawPlanes +// At the end of each frame. +// +void R_DrawPlanes(void) +{ + visplane_t* pl; + int light; + int x; + int stop; + int angle; + +#ifdef RANGECHECK + if (ds_p - drawsegs > MAXDRAWSEGS) + { + //I_Error("Error: R_DrawPlanes: drawsegs overflow (%i)", + // ds_p - drawsegs); + + doom_strcpy(error_buf, "Error: R_DrawPlanes: drawsegs overflow ("); + doom_concat(error_buf, doom_itoa((int)(ds_p - drawsegs), 10)); + doom_concat(error_buf, ")"); + I_Error(error_buf); + } + + if (lastvisplane - visplanes > MAXVISPLANES) + { + //I_Error("Error: R_DrawPlanes: visplane overflow (%i)", + // lastvisplane - visplanes); + + doom_strcpy(error_buf, "Error: R_DrawPlanes: visplane overflow ("); + doom_concat(error_buf, doom_itoa((int)(lastvisplane - visplanes), 10)); + doom_concat(error_buf, ")"); + I_Error(error_buf); + } + + if (lastopening - openings > MAXOPENINGS) + { + //I_Error("Error: R_DrawPlanes: opening overflow (%i)", + // lastopening - openings); + + doom_strcpy(error_buf, "Error: R_DrawPlanes: opening overflow ("); + doom_concat(error_buf, doom_itoa((int)(lastopening - openings), 10)); + doom_concat(error_buf, ")"); + I_Error(error_buf); + } +#endif + + for (pl = visplanes; pl < lastvisplane; pl++) + { + if (pl->minx > pl->maxx) + continue; + + + // sky flat + if (pl->picnum == skyflatnum) + { + dc_iscale = pspriteiscale >> detailshift; + + // Sky is allways drawn full bright, + // i.e. colormaps[0] is used. + // Because of this hack, sky is not affected + // by INVUL inverse mapping. + dc_colormap = colormaps; + dc_texturemid = skytexturemid; + for (x = pl->minx; x <= pl->maxx; x++) + { + dc_yl = pl->top[x]; + dc_yh = pl->bottom[x]; + + if (dc_yl <= dc_yh) + { + angle = (viewangle + xtoviewangle[x]) >> ANGLETOSKYSHIFT; + dc_x = x; + dc_source = R_GetColumn(skytexture, angle); + colfunc(); + } + } + continue; + } + + // regular flat + ds_source = W_CacheLumpNum(firstflat + + flattranslation[pl->picnum], + PU_STATIC); + + planeheight = doom_abs(pl->height - viewz); + light = (pl->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (light >= LIGHTLEVELS) + light = LIGHTLEVELS - 1; + + if (light < 0) + light = 0; + + planezlight = zlight[light]; + + pl->top[pl->maxx + 1] = 0xff; + pl->top[pl->minx - 1] = 0xff; + + stop = pl->maxx + 1; + + for (x = pl->minx; x <= stop; x++) + { + R_MakeSpans(x, pl->top[x - 1], + pl->bottom[x - 1], + pl->top[x], + pl->bottom[x]); + } + + Z_ChangeTag(ds_source, PU_CACHE); + } +} diff --git a/src/DOOM/r_plane.h b/src/DOOM/r_plane.h new file mode 100644 index 0000000..e7ca768 --- /dev/null +++ b/src/DOOM/r_plane.h @@ -0,0 +1,58 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Refresh, visplane stuff (floor, ceilings). +// +//----------------------------------------------------------------------------- + +#ifndef __R_PLANE__ +#define __R_PLANE__ + + +#include "r_data.h" + + +// Visplane related. +extern short* lastopening; + +typedef void (*planefunction_t) (int top, int bottom); + +extern planefunction_t floorfunc; +extern planefunction_t ceilingfunc_t; + +extern short floorclip[SCREENWIDTH]; +extern short ceilingclip[SCREENWIDTH]; + +extern fixed_t yslope[SCREENHEIGHT]; +extern fixed_t distscale[SCREENWIDTH]; + +void R_InitPlanes(void); +void R_ClearPlanes(void); +void R_MapPlane(int y, int x1, int x2); +void R_MakeSpans(int x, int t1, int b1, int t2, int b2); +void R_DrawPlanes(void); +visplane_t* R_FindPlane(fixed_t height, int picnum, int lightlevel); +visplane_t* R_CheckPlane(visplane_t* pl, int start, int stop); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/r_segs.c b/src/DOOM/r_segs.c new file mode 100644 index 0000000..73686d5 --- /dev/null +++ b/src/DOOM/r_segs.c @@ -0,0 +1,728 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// All the clipping: columns, horizontal spans, sky columns. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "i_system.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_local.h" +#include "r_sky.h" + + +#define HEIGHTBITS 12 +#define HEIGHTUNIT (1<curline; + frontsector = curline->frontsector; + backsector = curline->backsector; + texnum = texturetranslation[curline->sidedef->midtexture]; + + lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (curline->v1->y == curline->v2->y) + lightnum--; + else if (curline->v1->x == curline->v2->x) + lightnum++; + + if (lightnum < 0) + walllights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + walllights = scalelight[LIGHTLEVELS - 1]; + else + walllights = scalelight[lightnum]; + + maskedtexturecol = ds->maskedtexturecol; + + rw_scalestep = ds->scalestep; + spryscale = ds->scale1 + (x1 - ds->x1) * rw_scalestep; + mfloorclip = ds->sprbottomclip; + mceilingclip = ds->sprtopclip; + + // find positioning + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + dc_texturemid = frontsector->floorheight > backsector->floorheight + ? frontsector->floorheight : backsector->floorheight; + dc_texturemid = dc_texturemid + textureheight[texnum] - viewz; + } + else + { + dc_texturemid = frontsector->ceilingheight < backsector->ceilingheight + ? frontsector->ceilingheight : backsector->ceilingheight; + dc_texturemid = dc_texturemid - viewz; + } + dc_texturemid += curline->sidedef->rowoffset; + + if (fixedcolormap) + dc_colormap = fixedcolormap; + + // draw the columns + for (dc_x = x1; dc_x <= x2; dc_x++) + { + // calculate lighting + if (maskedtexturecol[dc_x] != DOOM_MAXSHORT) + { + if (!fixedcolormap) + { + index = spryscale >> LIGHTSCALESHIFT; + + if (index >= MAXLIGHTSCALE) + index = MAXLIGHTSCALE - 1; + + dc_colormap = walllights[index]; + } + + sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); + dc_iscale = 0xffffffffu / (unsigned)spryscale; + + // draw the texture + col = (column_t*)( + (byte*)R_GetColumn(texnum, maskedtexturecol[dc_x]) - 3); + + R_DrawMaskedColumn(col); + maskedtexturecol[dc_x] = DOOM_MAXSHORT; + } + spryscale += rw_scalestep; + } +} + + +// +// R_RenderSegLoop +// Draws zero, one, or two textures (and possibly a masked +// texture) for walls. +// Can draw or mark the starting pixel of floor and ceiling +// textures. +// CALLED: CORE LOOPING ROUTINE. +// +void R_RenderSegLoop(void) +{ + angle_t angle; + unsigned index; + int yl; + int yh; + int mid; + fixed_t texturecolumn; + int top; + int bottom; + + for (; rw_x < rw_stopx; rw_x++) + { + // mark floor / ceiling areas + yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS; + + // no space above wall? + if (yl < ceilingclip[rw_x] + 1) + yl = ceilingclip[rw_x] + 1; + + if (markceiling) + { + top = ceilingclip[rw_x] + 1; + bottom = yl - 1; + + if (bottom >= floorclip[rw_x]) + bottom = floorclip[rw_x] - 1; + + if (top <= bottom) + { + ceilingplane->top[rw_x] = top; + ceilingplane->bottom[rw_x] = bottom; + } + } + + yh = bottomfrac >> HEIGHTBITS; + + if (yh >= floorclip[rw_x]) + yh = floorclip[rw_x] - 1; + + if (markfloor) + { + top = yh + 1; + bottom = floorclip[rw_x] - 1; + if (top <= ceilingclip[rw_x]) + top = ceilingclip[rw_x] + 1; + if (top <= bottom) + { + floorplane->top[rw_x] = top; + floorplane->bottom[rw_x] = bottom; + } + } + + // texturecolumn and lighting are independent of wall tiers + if (segtextured) + { + // calculate texture offset + angle = (rw_centerangle + xtoviewangle[rw_x]) >> ANGLETOFINESHIFT; + texturecolumn = rw_offset - FixedMul(finetangent[angle], rw_distance); + texturecolumn >>= FRACBITS; + // calculate lighting + index = rw_scale >> LIGHTSCALESHIFT; + + if (index >= MAXLIGHTSCALE) + index = MAXLIGHTSCALE - 1; + + dc_colormap = walllights[index]; + dc_x = rw_x; + dc_iscale = 0xffffffffu / (unsigned)rw_scale; + } + + // draw the wall tiers + if (midtexture) + { + // single sided line + dc_yl = yl; + dc_yh = yh; + dc_texturemid = rw_midtexturemid; + dc_source = R_GetColumn(midtexture, texturecolumn); + colfunc(); + ceilingclip[rw_x] = viewheight; + floorclip[rw_x] = -1; + } + else + { + // two sided line + if (toptexture) + { + // top wall + mid = pixhigh >> HEIGHTBITS; + pixhigh += pixhighstep; + + if (mid >= floorclip[rw_x]) + mid = floorclip[rw_x] - 1; + + if (mid >= yl) + { + dc_yl = yl; + dc_yh = mid; + dc_texturemid = rw_toptexturemid; + dc_source = R_GetColumn(toptexture, texturecolumn); + colfunc(); + ceilingclip[rw_x] = mid; + } + else + ceilingclip[rw_x] = yl - 1; + } + else + { + // no top wall + if (markceiling) + ceilingclip[rw_x] = yl - 1; + } + + if (bottomtexture) + { + // bottom wall + mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS; + pixlow += pixlowstep; + + // no space above wall? + if (mid <= ceilingclip[rw_x]) + mid = ceilingclip[rw_x] + 1; + + if (mid <= yh) + { + dc_yl = mid; + dc_yh = yh; + dc_texturemid = rw_bottomtexturemid; + dc_source = R_GetColumn(bottomtexture, + texturecolumn); + colfunc(); + floorclip[rw_x] = mid; + } + else + floorclip[rw_x] = yh + 1; + } + else + { + // no bottom wall + if (markfloor) + floorclip[rw_x] = yh + 1; + } + + if (maskedtexture) + { + // save texturecol + // for backdrawing of masked mid texture + maskedtexturecol[rw_x] = texturecolumn; + } + } + + rw_scale += rw_scalestep; + topfrac += topstep; + bottomfrac += bottomstep; + } +} + + +// +// R_StoreWallRange +// A wall segment will be drawn +// between start and stop pixels (inclusive). +// +void R_StoreWallRange(int start, int stop) +{ + fixed_t hyp; + fixed_t sineval; + angle_t distangle, offsetangle; + fixed_t vtop; + int lightnum; + + // don't overflow and crash + if (ds_p == &drawsegs[MAXDRAWSEGS]) + return; + +#ifdef RANGECHECK + if (start >= viewwidth || start > stop) + { + //I_Error("Error: Bad R_RenderWallRange: %i to %i", start, stop); + + doom_strcpy(error_buf, "Error: Bad R_RenderWallRange: "); + doom_concat(error_buf, doom_itoa(start, 10)); + doom_concat(error_buf, " to "); + doom_concat(error_buf, doom_itoa(stop, 10)); + I_Error(error_buf); + } +#endif + + sidedef = curline->sidedef; + linedef = curline->linedef; + + // mark the segment as visible for auto map + linedef->flags |= ML_MAPPED; + + // calculate rw_distance for scale calculation + rw_normalangle = curline->angle + ANG90; + offsetangle = (angle_t)doom_abs((int)rw_normalangle - (int)rw_angle1); + + if (offsetangle > ANG90) + offsetangle = ANG90; + + distangle = ANG90 - offsetangle; + hyp = R_PointToDist(curline->v1->x, curline->v1->y); + sineval = finesine[distangle >> ANGLETOFINESHIFT]; + rw_distance = FixedMul(hyp, sineval); + + + ds_p->x1 = rw_x = start; + ds_p->x2 = stop; + ds_p->curline = curline; + rw_stopx = stop + 1; + + // calculate scale at both ends and step + ds_p->scale1 = rw_scale = + R_ScaleFromGlobalAngle(viewangle + xtoviewangle[start]); + + if (stop > start) + { + ds_p->scale2 = R_ScaleFromGlobalAngle(viewangle + xtoviewangle[stop]); + ds_p->scalestep = rw_scalestep = + (ds_p->scale2 - rw_scale) / (stop - start); + } + else + { + // UNUSED: try to fix the stretched line bug +#if 0 + if (rw_distance < FRACUNIT / 2) + { + fixed_t trx, try; + fixed_t gxt, gyt; + + trx = curline->v1->x - viewx; + try = curline->v1->y - viewy; + + gxt = FixedMul(trx, viewcos); + gyt = -FixedMul(try, viewsin); + ds_p->scale1 = FixedDiv(projection, gxt - gyt) << detailshift; + } +#endif + ds_p->scale2 = ds_p->scale1; + } + + // calculate texture boundaries + // and decide if floor / ceiling marks are needed + worldtop = frontsector->ceilingheight - viewz; + worldbottom = frontsector->floorheight - viewz; + + midtexture = toptexture = bottomtexture = maskedtexture = 0; + ds_p->maskedtexturecol = 0; + + if (!backsector) + { + // single sided line + midtexture = texturetranslation[sidedef->midtexture]; + // a single sided line is terminal, so it must mark ends + markfloor = markceiling = true; + if (linedef->flags & ML_DONTPEGBOTTOM) + { + vtop = frontsector->floorheight + + textureheight[sidedef->midtexture]; + // bottom of texture at bottom + rw_midtexturemid = vtop - viewz; + } + else + { + // top of texture at top + rw_midtexturemid = worldtop; + } + rw_midtexturemid += sidedef->rowoffset; + + ds_p->silhouette = SIL_BOTH; + ds_p->sprtopclip = screenheightarray; + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = DOOM_MAXINT; + ds_p->tsilheight = DOOM_MININT; + } + else + { + // two sided line + ds_p->sprtopclip = ds_p->sprbottomclip = 0; + ds_p->silhouette = 0; + + if (frontsector->floorheight > backsector->floorheight) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = frontsector->floorheight; + } + else if (backsector->floorheight > viewz) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = DOOM_MAXINT; + // ds_p->sprbottomclip = negonearray; + } + + if (frontsector->ceilingheight < backsector->ceilingheight) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = frontsector->ceilingheight; + } + else if (backsector->ceilingheight < viewz) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = DOOM_MININT; + // ds_p->sprtopclip = screenheightarray; + } + + if (backsector->ceilingheight <= frontsector->floorheight) + { + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = DOOM_MAXINT; + ds_p->silhouette |= SIL_BOTTOM; + } + + if (backsector->floorheight >= frontsector->ceilingheight) + { + ds_p->sprtopclip = screenheightarray; + ds_p->tsilheight = DOOM_MININT; + ds_p->silhouette |= SIL_TOP; + } + + worldhigh = backsector->ceilingheight - viewz; + worldlow = backsector->floorheight - viewz; + + // hack to allow height changes in outdoor areas + if (frontsector->ceilingpic == skyflatnum + && backsector->ceilingpic == skyflatnum) + { + worldtop = worldhigh; + } + + if (worldlow != worldbottom + || backsector->floorpic != frontsector->floorpic + || backsector->lightlevel != frontsector->lightlevel) + { + markfloor = true; + } + else + { + // same plane on both sides + markfloor = false; + } + + if (worldhigh != worldtop + || backsector->ceilingpic != frontsector->ceilingpic + || backsector->lightlevel != frontsector->lightlevel) + { + markceiling = true; + } + else + { + // same plane on both sides + markceiling = false; + } + + if (backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight) + { + // closed door + markceiling = markfloor = true; + } + + if (worldhigh < worldtop) + { + // top texture + toptexture = texturetranslation[sidedef->toptexture]; + if (linedef->flags & ML_DONTPEGTOP) + { + // top of texture at top + rw_toptexturemid = worldtop; + } + else + { + vtop = + backsector->ceilingheight + + textureheight[sidedef->toptexture]; + + // bottom of texture + rw_toptexturemid = vtop - viewz; + } + } + if (worldlow > worldbottom) + { + // bottom texture + bottomtexture = texturetranslation[sidedef->bottomtexture]; + + if (linedef->flags & ML_DONTPEGBOTTOM) + { + // bottom of texture at bottom + // top of texture at top + rw_bottomtexturemid = worldtop; + } + else // top of texture at top + rw_bottomtexturemid = worldlow; + } + rw_toptexturemid += sidedef->rowoffset; + rw_bottomtexturemid += sidedef->rowoffset; + + // allocate space for masked texture tables + if (sidedef->midtexture) + { + // masked midtexture + maskedtexture = true; + ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x; + lastopening += rw_stopx - rw_x; + } + } + + // calculate rw_offset (only needed for textured lines) + segtextured = midtexture | toptexture | bottomtexture | maskedtexture; + + if (segtextured) + { + offsetangle = rw_normalangle - rw_angle1; + + if (offsetangle > ANG180) +#pragma warning(push) +#pragma warning(disable : 4146) + offsetangle = -offsetangle; +#pragma warning(pop) + + if (offsetangle > ANG90) + offsetangle = ANG90; + + sineval = finesine[offsetangle >> ANGLETOFINESHIFT]; + rw_offset = FixedMul(hyp, sineval); + + if (rw_normalangle - rw_angle1 < ANG180) + rw_offset = -rw_offset; + + rw_offset += sidedef->textureoffset + curline->offset; + rw_centerangle = ANG90 + viewangle - rw_normalangle; + + // calculate light table + // use different light tables + // for horizontal / vertical / diagonal + // OPTIMIZE: get rid of LIGHTSEGSHIFT globally + if (!fixedcolormap) + { + lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (curline->v1->y == curline->v2->y) + lightnum--; + else if (curline->v1->x == curline->v2->x) + lightnum++; + + if (lightnum < 0) + walllights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + walllights = scalelight[LIGHTLEVELS - 1]; + else + walllights = scalelight[lightnum]; + } + } + + // if a floor / ceiling plane is on the wrong side + // of the view plane, it is definitely invisible + // and doesn't need to be marked. + + if (frontsector->floorheight >= viewz) + { + // above view plane + markfloor = false; + } + + if (frontsector->ceilingheight <= viewz + && frontsector->ceilingpic != skyflatnum) + { + // below view plane + markceiling = false; + } + + + // calculate incremental stepping values for texture edges + worldtop >>= 4; + worldbottom >>= 4; + + topstep = -FixedMul(rw_scalestep, worldtop); + topfrac = (centeryfrac >> 4) - FixedMul(worldtop, rw_scale); + + bottomstep = -FixedMul(rw_scalestep, worldbottom); + bottomfrac = (centeryfrac >> 4) - FixedMul(worldbottom, rw_scale); + + if (backsector) + { + worldhigh >>= 4; + worldlow >>= 4; + + if (worldhigh < worldtop) + { + pixhigh = (centeryfrac >> 4) - FixedMul(worldhigh, rw_scale); + pixhighstep = -FixedMul(rw_scalestep, worldhigh); + } + + if (worldlow > worldbottom) + { + pixlow = (centeryfrac >> 4) - FixedMul(worldlow, rw_scale); + pixlowstep = -FixedMul(rw_scalestep, worldlow); + } + } + + // render it + if (markceiling) + ceilingplane = R_CheckPlane(ceilingplane, rw_x, rw_stopx - 1); + + if (markfloor) + floorplane = R_CheckPlane(floorplane, rw_x, rw_stopx - 1); + + R_RenderSegLoop(); + + // save sprite clipping info + if (((ds_p->silhouette & SIL_TOP) || maskedtexture) + && !ds_p->sprtopclip) + { + doom_memcpy(lastopening, ceilingclip + start, 2 * (rw_stopx - start)); + ds_p->sprtopclip = lastopening - start; + lastopening += rw_stopx - start; + } + + if (((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) + && !ds_p->sprbottomclip) + { + doom_memcpy(lastopening, floorclip + start, 2 * (rw_stopx - start)); + ds_p->sprbottomclip = lastopening - start; + lastopening += rw_stopx - start; + } + + if (maskedtexture && !(ds_p->silhouette & SIL_TOP)) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = DOOM_MININT; + } + if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM)) + { + ds_p->silhouette |= SIL_BOTTOM; + ds_p->bsilheight = DOOM_MAXINT; + } + ds_p++; +} diff --git a/src/DOOM/r_segs.h b/src/DOOM/r_segs.h new file mode 100644 index 0000000..9bb6bb5 --- /dev/null +++ b/src/DOOM/r_segs.h @@ -0,0 +1,38 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Refresh module, drawing LineSegs from BSP. +// +//----------------------------------------------------------------------------- + +#ifndef __R_SEGS__ +#define __R_SEGS__ + + +#include "r_defs.h" + + +void R_RenderMaskedSegRange(drawseg_t* ds, int x1, int x2); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/r_sky.c b/src/DOOM/r_sky.c new file mode 100644 index 0000000..1d6cf2d --- /dev/null +++ b/src/DOOM/r_sky.c @@ -0,0 +1,51 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Sky rendering. The DOOM sky is a texture map like any +// wall, wrapping around. A 1024 columns equal 360 degrees. +// The default sky map is 256 columns and repeats 4 times +// on a 320 screen? +// +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "m_fixed.h" // Needed for FRACUNIT. +#include "r_data.h" // Needed for Flat retrieval. +#include "r_sky.h" + + +// +// sky mapping +// +int skyflatnum; +int skytexture; +int skytexturemid; + + +// +// R_InitSkyMap +// Called whenever the view size changes. +// +void R_InitSkyMap(void) +{ + skytexturemid = 100 * FRACUNIT; +} diff --git a/src/DOOM/r_sky.h b/src/DOOM/r_sky.h new file mode 100644 index 0000000..e954104 --- /dev/null +++ b/src/DOOM/r_sky.h @@ -0,0 +1,43 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Sky rendering. +// +//----------------------------------------------------------------------------- + +#ifndef __R_SKY__ +#define __R_SKY__ + +// SKY, store the number for name. +#define SKYFLATNAME "F_SKY1" + +// The sky map is 256*128*4 maps. +#define ANGLETOSKYSHIFT 22 + +extern int skytexture; +extern int skytexturemid; + +// Called whenever the view size changes. +void R_InitSkyMap(void); + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/r_state.h b/src/DOOM/r_state.h new file mode 100644 index 0000000..2dfbca3 --- /dev/null +++ b/src/DOOM/r_state.h @@ -0,0 +1,123 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Refresh/render internal state variables (global). +// +//----------------------------------------------------------------------------- + +#ifndef __R_STATE__ +#define __R_STATE__ + + +#include "d_player.h" // Need data structure definitions. +#include "r_defs.h" + + +// +// Refresh internal data structures, +// for rendering. +// + +// needed for texture pegging +extern fixed_t* textureheight; + +// needed for pre rendering (fracs) +extern fixed_t* spritewidth; + +extern fixed_t* spriteoffset; +extern fixed_t* spritetopoffset; + +extern lighttable_t* colormaps; + +extern int viewwidth; +extern int scaledviewwidth; +extern int viewheight; + +extern int firstflat; + +// for global animation +extern int* flattranslation; +extern int* texturetranslation; + +// Sprite.... +extern int firstspritelump; +extern int lastspritelump; +extern int numspritelumps; + +// +// Lookup tables for map data. +// +extern int numsprites; +extern spritedef_t* sprites; + +extern int numvertexes; +extern vertex_t* vertexes; + +extern int numsegs; +extern seg_t* segs; + +extern int numsectors; +extern sector_t* sectors; + +extern int numsubsectors; +extern subsector_t* subsectors; + +extern int numnodes; +extern node_t* nodes; + +extern int numlines; +extern line_t* lines; + +extern int numsides; +extern side_t* sides; + +// +// POV data. +// +extern fixed_t viewx; +extern fixed_t viewy; +extern fixed_t viewz; + +extern angle_t viewangle; +extern player_t* viewplayer; + +// ? +extern angle_t clipangle; + +extern int viewangletox[FINEANGLES / 2]; +extern angle_t xtoviewangle[SCREENWIDTH + 1]; + +extern fixed_t rw_distance; +extern angle_t rw_normalangle; + +// angle to line origin +extern int rw_angle1; + +// Segs count? +extern int sscount; + +extern visplane_t* floorplane; +extern visplane_t* ceilingplane; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/r_things.c b/src/DOOM/r_things.c new file mode 100644 index 0000000..276dc07 --- /dev/null +++ b/src/DOOM/r_things.c @@ -0,0 +1,1022 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Refresh of things, i.e. objects represented by sprites. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + + +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "z_zone.h" +#include "w_wad.h" +#include "r_local.h" +#include "doomstat.h" + + +#define MINZ (FRACUNIT*4) +#define BASEYCENTER 100 + + +typedef struct +{ + int x1; + int x2; + + int column; + int topclip; + int bottomclip; +} maskdraw_t; + + +// +// Sprite rotation 0 is facing the viewer, +// rotation 1 is one angle turn CLOCKWISE around the axis. +// This is not the same as the angle, +// which increases counter clockwise (protractor). +// There was a lot of stuff grabbed wrong, so I changed it... +// +fixed_t pspritescale; +fixed_t pspriteiscale; + +lighttable_t** spritelights; + +// constant arrays +// used for psprite clipping and initializing clipping +short negonearray[SCREENWIDTH]; +short screenheightarray[SCREENWIDTH]; + +// variables used to look up +// and range check thing_t sprites patches +spritedef_t* sprites; +int numsprites; + +spriteframe_t sprtemp[29]; +int maxframe; +char* spritename; +vissprite_t vissprites[MAXVISSPRITES]; +vissprite_t* vissprite_p; +int newvissprite; +vissprite_t vsprsortedhead; + + +// +// INITIALIZATION FUNCTIONS +// + +// +// R_InstallSpriteLump +// Local function for R_InitSprites. +// +void R_InstallSpriteLump(int lump, unsigned frame, unsigned rotation, doom_boolean flipped) +{ + int r; + + if (frame >= 29 || rotation > 8) + { + //I_Error("Error: R_InstallSpriteLump: " + // "Bad frame characters in lump %i", lump); + doom_strcpy(error_buf, "Error: R_InstallSpriteLump: Bad frame characters in lump "); + doom_concat(error_buf, doom_itoa(lump, 10)); + I_Error(error_buf); + } + + if ((int)frame > maxframe) + maxframe = frame; + + if (rotation == 0) + { + // the lump should be used for all rotations + if (sprtemp[frame].rotate == false) + { + //I_Error("Error: R_InitSprites: Sprite %s frame %c has " + // "multip rot=0 lump", spritename, 'A' + frame); + doom_strcpy(error_buf, "Error: R_InitSprites: Sprite "); + doom_concat(error_buf, spritename); + doom_concat(error_buf, " frame "); + doom_concat(error_buf, doom_ctoa('A' + frame)); + doom_concat(error_buf, " has multip rot=0 lump"); + I_Error(error_buf); + } + + if (sprtemp[frame].rotate == true) + { + //I_Error("Error: R_InitSprites: Sprite %s frame %c has rotations " + // "and a rot=0 lump", spritename, 'A' + frame); + doom_strcpy(error_buf, "Error: R_InitSprites: Sprite "); + doom_concat(error_buf, spritename); + doom_concat(error_buf, " frame "); + doom_concat(error_buf, doom_ctoa('A' + frame)); + doom_concat(error_buf, " has rotations "); + I_Error(error_buf); + } + + sprtemp[frame].rotate = false; + for (r = 0; r < 8; r++) + { + sprtemp[frame].lump[r] = lump - firstspritelump; + sprtemp[frame].flip[r] = (byte)flipped; + } + return; + } + + // the lump is only used for one rotation + if (sprtemp[frame].rotate == false) + { + //I_Error("Error: R_InitSprites: Sprite %s frame %c has rotations " + // "and a rot=0 lump", spritename, 'A' + frame); + doom_strcpy(error_buf, "Error: R_InitSprites: Sprite "); + doom_concat(error_buf, spritename); + doom_concat(error_buf, " frame "); + doom_concat(error_buf, doom_ctoa('A' + frame)); + doom_concat(error_buf, " has rotations "); + I_Error(error_buf); + } + + sprtemp[frame].rotate = true; + + // make 0 based + rotation--; + if (sprtemp[frame].lump[rotation] != -1) + { + //I_Error("Error: R_InitSprites: Sprite %s : %c : %c " + // "has two lumps mapped to it", + // spritename, 'A' + frame, '1' + rotation); + doom_strcpy(error_buf, "Error: R_InitSprites: Sprite "); + doom_concat(error_buf, spritename); + doom_concat(error_buf, " : "); + doom_concat(error_buf, doom_ctoa('A' + frame)); + doom_concat(error_buf, " : "); + doom_concat(error_buf, doom_ctoa('1' + rotation)); + doom_concat(error_buf, " "); + I_Error(error_buf); + } + + sprtemp[frame].lump[rotation] = lump - firstspritelump; + sprtemp[frame].flip[rotation] = (byte)flipped; +} + + +// +// R_InitSpriteDefs +// Pass a null terminated list of sprite names +// (4 chars exactly) to be used. +// Builds the sprite rotation matrixes to account +// for horizontally flipped sprites. +// Will report an error if the lumps are inconsistant. +// Only called at startup. +// +// Sprite lump names are 4 characters for the actor, +// a letter for the frame, and a number for the rotation. +// A sprite that is flippable will have an additional +// letter/number appended. +// The rotation character can be 0 to signify no rotations. +// +void R_InitSpriteDefs(char** namelist) +{ + char** check; + int i; + int l; + int intname; + int frame; + int rotation; + int start; + int end; + int patched; + + // count the number of sprite names + check = namelist; + while (*check != 0) + check++; + + numsprites = (int)(check - namelist); + + if (!numsprites) + return; + + sprites = Z_Malloc(numsprites * sizeof(*sprites), PU_STATIC, 0); + + start = firstspritelump - 1; + end = lastspritelump + 1; + + // scan all the lump names for each of the names, + // noting the highest frame letter. + // Just compare 4 characters as ints + for (i = 0; i < numsprites; i++) + { + spritename = namelist[i]; + doom_memset(sprtemp, -1, sizeof(sprtemp)); + + maxframe = -1; + intname = *(int*)namelist[i]; + + // scan the lumps, + // filling in the frames for whatever is found + for (l = start + 1; l < end; l++) + { + if (*(int*)lumpinfo[l].name == intname) + { + frame = lumpinfo[l].name[4] - 'A'; + rotation = lumpinfo[l].name[5] - '0'; + + if (modifiedgame) + patched = W_GetNumForName(lumpinfo[l].name); + else + patched = l; + + R_InstallSpriteLump(patched, frame, rotation, false); + + if (lumpinfo[l].name[6]) + { + frame = lumpinfo[l].name[6] - 'A'; + rotation = lumpinfo[l].name[7] - '0'; + R_InstallSpriteLump(l, frame, rotation, true); + } + } + } + + // check the frames that were found for completeness + if (maxframe == -1) + { + sprites[i].numframes = 0; + continue; + } + + maxframe++; + + for (frame = 0; frame < maxframe; frame++) + { + switch ((int)sprtemp[frame].rotate) + { + case -1: + { + // no rotations were found for that frame at all + //I_Error("Error: R_InitSprites: No patches found " + // "for %s frame %c", namelist[i], frame + 'A'); + doom_strcpy(error_buf, "Error: R_InitSprites: No patches found for "); + doom_concat(error_buf, namelist[i]); + doom_concat(error_buf, " frame "); + doom_concat(error_buf, doom_ctoa(frame + 'A')); + I_Error(error_buf); + break; + } + + case 0: + // only the first rotation is needed + break; + + case 1: + // must have all 8 frames + for (rotation = 0; rotation < 8; rotation++) + if (sprtemp[frame].lump[rotation] == -1) + { + //I_Error("Error: R_InitSprites: Sprite %s frame %c " + // "is missing rotations", + // namelist[i], frame + 'A'); + doom_strcpy(error_buf, "Error: R_InitSprites: Sprite "); + doom_concat(error_buf, namelist[i]); + doom_concat(error_buf, " frame "); + doom_concat(error_buf, doom_ctoa(frame + 'A')); + doom_concat(error_buf, " is missing rotations"); + I_Error(error_buf); + } + break; + } + } + + // allocate space for the frames present and copy sprtemp to it + sprites[i].numframes = maxframe; + sprites[i].spriteframes = + Z_Malloc(maxframe * sizeof(spriteframe_t), PU_STATIC, 0); + doom_memcpy(sprites[i].spriteframes, sprtemp, maxframe * sizeof(spriteframe_t)); + } +} + + +// +// GAME FUNCTIONS +// + +// +// R_InitSprites +// Called at program start. +// +void R_InitSprites(char** namelist) +{ + int i; + + for (i = 0; i < SCREENWIDTH; i++) + { + negonearray[i] = -1; + } + + R_InitSpriteDefs(namelist); +} + + +// +// R_ClearSprites +// Called at frame start. +// +void R_ClearSprites(void) +{ + vissprite_p = vissprites; +} + + +// +// R_NewVisSprite +// +vissprite_t overflowsprite; + +vissprite_t* R_NewVisSprite(void) +{ + if (vissprite_p == &vissprites[MAXVISSPRITES]) + return &overflowsprite; + + vissprite_p++; + return vissprite_p - 1; +} + + +// +// R_DrawMaskedColumn +// Used for sprites and masked mid textures. +// Masked means: partly transparent, i.e. stored +// in posts/runs of opaque pixels. +// +short* mfloorclip; +short* mceilingclip; + +fixed_t spryscale; +fixed_t sprtopscreen; + +void R_DrawMaskedColumn(column_t* column) +{ + int topscreen; + int bottomscreen; + fixed_t basetexturemid; + + basetexturemid = dc_texturemid; + + for (; column->topdelta != 0xff; ) + { + // calculate unclipped screen coordinates + // for post + topscreen = sprtopscreen + spryscale * column->topdelta; + bottomscreen = topscreen + spryscale * column->length; + + dc_yl = (topscreen + FRACUNIT - 1) >> FRACBITS; + dc_yh = (bottomscreen - 1) >> FRACBITS; + + if (dc_yh >= mfloorclip[dc_x]) + dc_yh = mfloorclip[dc_x] - 1; + if (dc_yl <= mceilingclip[dc_x]) + dc_yl = mceilingclip[dc_x] + 1; + + if (dc_yl <= dc_yh) + { + dc_source = (byte*)column + 3; + dc_texturemid = basetexturemid - (column->topdelta << FRACBITS); + // dc_source = (byte *)column + 3 - column->topdelta; + + // Drawn by either R_DrawColumn + // or (SHADOW) R_DrawFuzzColumn. + colfunc(); + } + column = (column_t*)((byte*)column + column->length + 4); + } + + dc_texturemid = basetexturemid; +} + + +// +// R_DrawVisSprite +// mfloorclip and mceilingclip should also be set. +// +void R_DrawVisSprite(vissprite_t* vis, int x1, int x2) +{ + column_t* column; + int texturecolumn; + fixed_t frac; + patch_t* patch; + + patch = W_CacheLumpNum(vis->patch + firstspritelump, PU_CACHE); + + dc_colormap = vis->colormap; + + if (!dc_colormap) + { + // 0 colormap = shadow draw + colfunc = fuzzcolfunc; + } + else if (vis->mobjflags & MF_TRANSLATION) + { + colfunc = R_DrawTranslatedColumn; + dc_translation = translationtables - 256 + + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8)); + } + + dc_iscale = doom_abs(vis->xiscale) >> detailshift; + dc_texturemid = vis->texturemid; + frac = vis->startfrac; + spryscale = vis->scale; + sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); + + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) + { + texturecolumn = frac >> FRACBITS; +#ifdef RANGECHECK + if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) + I_Error("Error: R_DrawSpriteRange: bad texturecolumn"); +#endif + column = (column_t*)((byte*)patch + + LONG(patch->columnofs[texturecolumn])); + R_DrawMaskedColumn(column); + } + + colfunc = basecolfunc; +} + + +// +// R_ProjectSprite +// Generates a vissprite for a thing +// if it might be visible. +// +void R_ProjectSprite(mobj_t* thing) +{ + fixed_t tr_x; + fixed_t tr_y; + + fixed_t gxt; + fixed_t gyt; + + fixed_t tx; + fixed_t tz; + + fixed_t xscale; + + int x1; + int x2; + + spritedef_t* sprdef; + spriteframe_t* sprframe; + int lump; + + unsigned rot; + doom_boolean flip; + + int index; + + vissprite_t* vis; + + angle_t ang; + fixed_t iscale; + + // transform the origin point + tr_x = thing->x - viewx; + tr_y = thing->y - viewy; + + gxt = FixedMul(tr_x, viewcos); + gyt = -FixedMul(tr_y, viewsin); + + tz = gxt - gyt; + + // thing is behind view plane? + if (tz < MINZ) + return; + + xscale = FixedDiv(projection, tz); + + gxt = -FixedMul(tr_x, viewsin); + gyt = FixedMul(tr_y, viewcos); + tx = -(gyt + gxt); + + // too far off the side? + if (doom_abs(tx) > (tz << 2)) + return; + + // decide which patch to use for sprite relative to player +#ifdef RANGECHECK + if ((unsigned)thing->sprite >= (unsigned)numsprites) + { + //I_Error("Error: R_ProjectSprite: invalid sprite number %i ", + // thing->sprite); + + doom_strcpy(error_buf, "Error: R_ProjectSprite: invalid sprite number "); + doom_concat(error_buf, doom_itoa(thing->sprite, 10)); + doom_concat(error_buf, " "); + I_Error(error_buf); + } +#endif + sprdef = &sprites[thing->sprite]; +#ifdef RANGECHECK + if ((thing->frame & FF_FRAMEMASK) >= sprdef->numframes) + { + //I_Error("Error: R_ProjectSprite: invalid sprite frame %i : %i ", + // thing->sprite, thing->frame); + + doom_strcpy(error_buf, "Error: R_ProjectSprite: invalid sprite frame "); + doom_concat(error_buf, doom_itoa(thing->sprite, 10)); + doom_concat(error_buf, " : "); + doom_concat(error_buf, doom_itoa(thing->frame, 10)); + doom_concat(error_buf, " "); + I_Error(error_buf); + } +#endif + sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; + + if (sprframe->rotate) + { + // choose a different rotation based on player view + ang = R_PointToAngle(thing->x, thing->y); + rot = (ang - thing->angle + (unsigned)(ANG45 / 2) * 9) >> 29; + lump = sprframe->lump[rot]; + flip = (doom_boolean)sprframe->flip[rot]; + } + else + { + // use single rotation for all views + lump = sprframe->lump[0]; + flip = (doom_boolean)sprframe->flip[0]; + } + + // calculate edges of the shape + tx -= spriteoffset[lump]; + x1 = (centerxfrac + FixedMul(tx, xscale)) >> FRACBITS; + + // off the right side? + if (x1 > viewwidth) + return; + + tx += spritewidth[lump]; + x2 = ((centerxfrac + FixedMul(tx, xscale)) >> FRACBITS) - 1; + + // off the left side + if (x2 < 0) + return; + + // store information in a vissprite + vis = R_NewVisSprite(); + vis->mobjflags = thing->flags; + vis->scale = xscale << detailshift; + vis->gx = thing->x; + vis->gy = thing->y; + vis->gz = thing->z; + vis->gzt = thing->z + spritetopoffset[lump]; + vis->texturemid = vis->gzt - viewz; + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2; + iscale = FixedDiv(FRACUNIT, xscale); + + if (flip) + { + vis->startfrac = spritewidth[lump] - 1; + vis->xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale * (vis->x1 - x1); + vis->patch = lump; + + // get light level + if (thing->flags & MF_SHADOW) + { + // shadow draw + vis->colormap = 0; + } + else if (fixedcolormap) + { + // fixed map + vis->colormap = fixedcolormap; + } + else if (thing->frame & FF_FULLBRIGHT) + { + // full bright + vis->colormap = colormaps; + } + + else + { + // diminished light + index = xscale >> (LIGHTSCALESHIFT - detailshift); + + if (index >= MAXLIGHTSCALE) + index = MAXLIGHTSCALE - 1; + + vis->colormap = spritelights[index]; + } +} + + +// +// R_AddSprites +// During BSP traversal, this adds sprites by sector. +// +void R_AddSprites(sector_t* sec) +{ + mobj_t* thing; + int lightnum; + + // 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 (sec->validcount == validcount) + return; + + // Well, now it will be done. + sec->validcount = validcount; + + lightnum = (sec->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (lightnum < 0) + spritelights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + spritelights = scalelight[LIGHTLEVELS - 1]; + else + spritelights = scalelight[lightnum]; + + // Handle all things in sector. + for (thing = sec->thinglist; thing; thing = thing->snext) + R_ProjectSprite(thing); +} + + +// +// R_DrawPSprite +// +void R_DrawPSprite(pspdef_t* psp) +{ + fixed_t tx; + int x1; + int x2; + spritedef_t* sprdef; + spriteframe_t* sprframe; + int lump; + doom_boolean flip; + vissprite_t* vis; + vissprite_t avis; + + // decide which patch to use +#ifdef RANGECHECK + if ((unsigned)psp->state->sprite >= (unsigned)numsprites) + { + //I_Error("Error: R_ProjectSprite: invalid sprite number %i ", + // psp->state->sprite); + doom_strcpy(error_buf, "Error: R_ProjectSprite: invalid sprite number "); + doom_concat(error_buf, doom_itoa(psp->state->sprite, 10)); + doom_concat(error_buf, " "); + I_Error(error_buf); + } +#endif + sprdef = &sprites[psp->state->sprite]; +#ifdef RANGECHECK + if ((psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes) + { + //I_Error("Error: R_ProjectSprite: invalid sprite frame %i : %i ", + // psp->state->sprite, psp->state->frame); + doom_strcpy(error_buf, "Error: R_ProjectSprite: invalid sprite frame "); + doom_concat(error_buf, doom_itoa(psp->state->sprite, 10)); + doom_concat(error_buf, " : "); + doom_concat(error_buf, doom_itoa(psp->state->frame, 10)); + doom_concat(error_buf, " "); + I_Error(error_buf); + } +#endif + sprframe = &sprdef->spriteframes[psp->state->frame & FF_FRAMEMASK]; + + lump = sprframe->lump[0]; + flip = (doom_boolean)sprframe->flip[0]; + + // calculate edges of the shape + tx = psp->sx - 160 * FRACUNIT; + + tx -= spriteoffset[lump]; + x1 = (centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS; + + // off the right side + if (x1 > viewwidth) + return; + + tx += spritewidth[lump]; + x2 = ((centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS) - 1; + + // off the left side + if (x2 < 0) + return; + + // store information in a vissprite + vis = &avis; + vis->mobjflags = 0; + vis->texturemid = (BASEYCENTER << FRACBITS) + FRACUNIT / 2 - (psp->sy - spritetopoffset[lump]); + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2; + vis->scale = pspritescale << detailshift; + + if (flip) + { + vis->xiscale = -pspriteiscale; + vis->startfrac = spritewidth[lump] - 1; + } + else + { + vis->xiscale = pspriteiscale; + vis->startfrac = 0; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale * (vis->x1 - x1); + + vis->patch = lump; + + if (viewplayer->powers[pw_invisibility] > 4 * 32 + || viewplayer->powers[pw_invisibility] & 8) + { + // shadow draw + vis->colormap = 0; + } + else if (fixedcolormap) + { + // fixed color + vis->colormap = fixedcolormap; + } + else if (psp->state->frame & FF_FULLBRIGHT) + { + // full bright + vis->colormap = colormaps; + } + else + { + // local light + vis->colormap = spritelights[MAXLIGHTSCALE - 1]; + } + + R_DrawVisSprite(vis, vis->x1, vis->x2); +} + + +// +// R_DrawPlayerSprites +// +void R_DrawPlayerSprites(void) +{ + int i; + int lightnum; + pspdef_t* psp; + + // get light level + lightnum = + (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) + + extralight; + + if (lightnum < 0) + spritelights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + spritelights = scalelight[LIGHTLEVELS - 1]; + else + spritelights = scalelight[lightnum]; + + // clip to screen bounds + mfloorclip = screenheightarray; + mceilingclip = negonearray; + + // add all active psprites + for (i = 0, psp = viewplayer->psprites; + i < NUMPSPRITES; + i++, psp++) + { + if (psp->state) + R_DrawPSprite(psp); + } +} + + +// +// R_SortVisSprites +// +void R_SortVisSprites(void) +{ + int i; + int count; + vissprite_t* ds; + vissprite_t* best; + vissprite_t unsorted; + fixed_t bestscale; + + count = (int)(vissprite_p - vissprites); + + unsorted.next = unsorted.prev = &unsorted; + + if (!count) + return; + + for (ds = vissprites; ds < vissprite_p; ds++) + { + ds->next = ds + 1; + ds->prev = ds - 1; + } + + vissprites[0].prev = &unsorted; + unsorted.next = &vissprites[0]; + (vissprite_p - 1)->next = &unsorted; + unsorted.prev = vissprite_p - 1; + + // pull the vissprites out by scale + //best = 0; // shut up the compiler warning + vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; + for (i = 0; i < count; i++) + { + bestscale = DOOM_MAXINT; + for (ds = unsorted.next; ds != &unsorted; ds = ds->next) + { + if (ds->scale < bestscale) + { + bestscale = ds->scale; + best = ds; + } + } + best->next->prev = best->prev; + best->prev->next = best->next; + best->next = &vsprsortedhead; + best->prev = vsprsortedhead.prev; + vsprsortedhead.prev->next = best; + vsprsortedhead.prev = best; + } +} + + +// +// R_DrawSprite +// +void R_DrawSprite(vissprite_t* spr) +{ + drawseg_t* ds; + short clipbot[SCREENWIDTH]; + short cliptop[SCREENWIDTH]; + int x; + int r1; + int r2; + fixed_t scale; + fixed_t lowscale; + int silhouette; + + for (x = spr->x1; x <= spr->x2; x++) + clipbot[x] = cliptop[x] = -2; + + // Scan drawsegs from end to start for obscuring segs. + // The first drawseg that has a greater scale + // is the clip seg. + for (ds = ds_p - 1; ds >= drawsegs; ds--) + { + // determine if the drawseg obscures the sprite + if (ds->x1 > spr->x2 + || ds->x2 < spr->x1 + || (!ds->silhouette + && !ds->maskedtexturecol)) + { + // does not cover sprite + continue; + } + + r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; + r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; + + if (ds->scale1 > ds->scale2) + { + lowscale = ds->scale2; + scale = ds->scale1; + } + else + { + lowscale = ds->scale1; + scale = ds->scale2; + } + + if (scale < spr->scale + || (lowscale < spr->scale + && !R_PointOnSegSide(spr->gx, spr->gy, ds->curline))) + { + // masked mid texture? + if (ds->maskedtexturecol) + R_RenderMaskedSegRange(ds, r1, r2); + // seg is behind sprite + continue; + } + + + // clip this piece of the sprite + silhouette = ds->silhouette; + + if (spr->gz >= ds->bsilheight) + silhouette &= ~SIL_BOTTOM; + + if (spr->gzt <= ds->tsilheight) + silhouette &= ~SIL_TOP; + + if (silhouette == 1) + { + // bottom sil + for (x = r1; x <= r2; x++) + if (clipbot[x] == -2) + clipbot[x] = ds->sprbottomclip[x]; + } + else if (silhouette == 2) + { + // top sil + for (x = r1; x <= r2; x++) + if (cliptop[x] == -2) + cliptop[x] = ds->sprtopclip[x]; + } + else if (silhouette == 3) + { + // both + for (x = r1; x <= r2; x++) + { + if (clipbot[x] == -2) + clipbot[x] = ds->sprbottomclip[x]; + if (cliptop[x] == -2) + cliptop[x] = ds->sprtopclip[x]; + } + } + } + + // all clipping has been performed, so draw the sprite + + // check for unclipped columns + for (x = spr->x1; x <= spr->x2; x++) + { + if (clipbot[x] == -2) + clipbot[x] = viewheight; + + if (cliptop[x] == -2) + cliptop[x] = -1; + } + + mfloorclip = clipbot; + mceilingclip = cliptop; + R_DrawVisSprite(spr, spr->x1, spr->x2); +} + + +// +// R_DrawMasked +// +void R_DrawMasked(void) +{ + vissprite_t* spr; + drawseg_t* ds; + + R_SortVisSprites(); + + if (vissprite_p > vissprites) + { + // draw all vissprites back to front + for (spr = vsprsortedhead.next; + spr != &vsprsortedhead; + spr = spr->next) + { + + R_DrawSprite(spr); + } + } + + // render any remaining masked mid textures + for (ds = ds_p - 1; ds >= drawsegs; ds--) + if (ds->maskedtexturecol) + R_RenderMaskedSegRange(ds, ds->x1, ds->x2); + + // draw the psprites on top of everything + // but does not draw on side views + if (!viewangleoffset) + R_DrawPlayerSprites(); +} diff --git a/src/DOOM/r_things.h b/src/DOOM/r_things.h new file mode 100644 index 0000000..df92650 --- /dev/null +++ b/src/DOOM/r_things.h @@ -0,0 +1,65 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Rendering of moving objects, sprites. +// +//----------------------------------------------------------------------------- + +#ifndef __R_THINGS__ +#define __R_THINGS__ + + +#include "r_defs.h" + + +#define MAXVISSPRITES 128 + + +extern vissprite_t vissprites[MAXVISSPRITES]; +extern vissprite_t* vissprite_p; +extern vissprite_t vsprsortedhead; + +// Constant arrays used for psprite clipping +// and initializing clipping. +extern short negonearray[SCREENWIDTH]; +extern short screenheightarray[SCREENWIDTH]; + +// vars for R_DrawMaskedColumn +extern short* mfloorclip; +extern short* mceilingclip; +extern fixed_t spryscale; +extern fixed_t sprtopscreen; + +extern fixed_t pspritescale; +extern fixed_t pspriteiscale; + + +void R_DrawMaskedColumn(column_t* column); +void R_SortVisSprites(void); +void R_AddSprites(sector_t* sec); +void R_InitSprites(char** namelist); +void R_ClearSprites(void); +void R_DrawMasked(void); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/s_sound.c b/src/DOOM/s_sound.c new file mode 100644 index 0000000..7e83ea2 --- /dev/null +++ b/src/DOOM/s_sound.c @@ -0,0 +1,729 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: none +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + + +#include "i_system.h" +#include "i_sound.h" +#include "sounds.h" +#include "s_sound.h" +#include "z_zone.h" +#include "m_random.h" +#include "w_wad.h" +#include "doomdef.h" +#include "p_local.h" +#include "doomstat.h" + + +#define S_MAX_VOLUME 127 + +// when to clip out sounds +// Does not fit the large outdoor areas. +#define S_CLIPPING_DIST (1200*0x10000) + +// Distance tp origin when sounds should be maxed out. +// This should relate to movement clipping resolution +// (see BLOCKMAP handling). +// Originally: (200*0x10000). +#define S_CLOSE_DIST (160*0x10000) + +#define S_ATTENUATOR ((S_CLIPPING_DIST-S_CLOSE_DIST)>>FRACBITS) + +// Adjustable by menu. +#define NORM_VOLUME snd_MaxVolume + +#define NORM_PITCH 128 +#define NORM_PRIORITY 64 +#define NORM_SEP 128 + +#define S_PITCH_PERTURB 1 +#define S_STEREO_SWING (96*0x10000) + +// percent attenuation from front to back +#define S_IFRACVOL 30 + +#define NA 0 +#define S_NUMCHANNELS 2 + + +// Current music/sfx card - index useless +// w/o a reference LUT in a sound module. +extern int snd_MusicDevice; +extern int snd_SfxDevice; +// Config file? Same disclaimer as above. +extern int snd_DesiredMusicDevice; +extern int snd_DesiredSfxDevice; + + +typedef struct +{ + // sound information (if null, channel avail.) + sfxinfo_t* sfxinfo; + + // origin of sound + void* origin; + + // handle of the sound being played + int handle; +} channel_t; + + +// the set of channels available +static channel_t* channels_s_sound; + +// whether songs are mus_paused +static doom_boolean mus_paused; + +// music currently being played +static musicinfo_t* mus_playing_s_sound = 0; + +static int nextcleanup; + + +// These are not used, but should be (menu). +// Maximum volume of a sound effect. +// Internal default is max out of 0-15. +int snd_SfxVolume = 15; + +// Maximum volume of music. Useless so far. +int snd_MusicVolume = 15; + +// following is set +// by the defaults code in M_misc: +// number of channels available +int numChannels; + + +// +// Prototypes +// +int S_getChannel(void* origin, sfxinfo_t* sfxinfo); +int S_AdjustSoundParams(mobj_t* listener, mobj_t* source, int* vol, int* sep, int* pitch); +void S_StopChannel(int cnum); + + +// +// Initializes sound stuff, including volume +// Sets channels, SFX and music volume, +// allocates channel buffer, sets S_sfx lookup. +// +void S_Init(int sfxVolume, int musicVolume) +{ + int i; + + //doom_print("S_Init: default sfx volume %d\n", sfxVolume); + doom_print("S_Init: default sfx volume "); + doom_print(doom_itoa(sfxVolume, 10)); + doom_print("\n"); + + // Whatever these did with DMX, these are rather dummies now. + I_SetChannels(); + + S_SetSfxVolume(sfxVolume); + // No music with Linux - another dummy. + S_SetMusicVolume(musicVolume); + + // Allocating the internal channels for mixing + // (the maximum numer of sounds rendered + // simultaneously) within zone memory. + channels_s_sound = + (channel_t*)Z_Malloc(numChannels * sizeof(channel_t), PU_STATIC, 0); + + // Free all channels for use + for (i = 0; i < numChannels; i++) + channels_s_sound[i].sfxinfo = 0; + + // no sounds are playing, and they are not mus_paused + mus_paused = 0; + + // Note that sounds have not been cached (yet). + for (i = 1; i < NUMSFX; i++) + S_sfx[i].lumpnum = S_sfx[i].usefulness = -1; +} + + +// +// Per level startup code. +// Kills playing sounds at start of level, +// determines music if any, changes music. +// +void S_Start(void) +{ + int cnum; + int mnum; + + // kill all playing sounds at start of level + // (trust me - a good idea) + for (cnum = 0; cnum < numChannels; cnum++) + if (channels_s_sound[cnum].sfxinfo) + S_StopChannel(cnum); + + // start new music for the level + mus_paused = 0; + + if (gamemode == commercial) + mnum = mus_runnin + gamemap - 1; + else + { + int spmus[] = + { + // Song - Who? - Where? + + mus_e3m4, // American e4m1 + mus_e3m2, // Romero e4m2 + mus_e3m3, // Shawn e4m3 + mus_e1m5, // American e4m4 + mus_e2m7, // Tim e4m5 + mus_e2m4, // Romero e4m6 + mus_e2m6, // J.Anderson e4m7 CHIRON.WAD + mus_e2m5, // Shawn e4m8 + mus_e1m9 // Tim e4m9 + }; + + if (gameepisode < 4) + mnum = mus_e1m1 + (gameepisode - 1) * 9 + gamemap - 1; + else + mnum = spmus[gamemap - 1]; + } + + // HACK FOR COMMERCIAL + // if (commercial && mnum > mus_e3m9) + // mnum -= mus_e3m9; + + S_ChangeMusic(mnum, true); + + nextcleanup = 15; +} + + +void S_StartSoundAtVolume(void* origin_p, int sfx_id, int volume) +{ + int rc; + int sep; + int pitch; + int priority; + sfxinfo_t* sfx; + int cnum; + + mobj_t* origin = (mobj_t*)origin_p; + + // check for bogus sound # + if (sfx_id < 1 || sfx_id > NUMSFX) + { + //I_Error("Error: Bad sfx #: %d", sfx_id); + doom_strcpy(error_buf, "Error: Bad sfx #: "); + doom_concat(error_buf, doom_itoa(sfx_id, 10)); + I_Error(error_buf); + } + + sfx = &S_sfx[sfx_id]; + + // Initialize sound parameters + if (sfx->link) + { + pitch = sfx->pitch; + priority = sfx->priority; + volume += sfx->volume; + + if (volume < 1) + return; + + if (volume > snd_SfxVolume) + volume = snd_SfxVolume; + } + else + { + pitch = NORM_PITCH; + priority = NORM_PRIORITY; + } + + // Check to see if it is audible, + // and if not, modify the params + if (origin && origin != players[consoleplayer].mo) + { + rc = S_AdjustSoundParams(players[consoleplayer].mo, + origin, + &volume, + &sep, + &pitch); + + if (origin->x == players[consoleplayer].mo->x + && origin->y == players[consoleplayer].mo->y) + { + sep = NORM_SEP; + } + + if (!rc) + return; + } + else + { + sep = NORM_SEP; + } + + // hacks to vary the sfx pitches + if (sfx_id >= sfx_sawup + && sfx_id <= sfx_sawhit) + { + pitch += 8 - (M_Random() & 15); + + if (pitch < 0) + pitch = 0; + else if (pitch > 255) + pitch = 255; + } + else if (sfx_id != sfx_itemup + && sfx_id != sfx_tink) + { + pitch += 16 - (M_Random() & 31); + + if (pitch < 0) + pitch = 0; + else if (pitch > 255) + pitch = 255; + } + + // kill old sound + S_StopSound(origin); + + // try to find a channel + cnum = S_getChannel(origin, sfx); + + if (cnum < 0) + return; + + // + // This is supposed to handle the loading/caching. + // For some odd reason, the caching is done nearly + // each time the sound is needed? + // + + // get lumpnum if necessary + if (sfx->lumpnum < 0) + sfx->lumpnum = I_GetSfxLumpNum(sfx); + +#ifndef SNDSRV + // cache data if necessary + if (!sfx->data) + { + doom_print( + "S_StartSoundAtVolume: 16bit and not pre-cached - wtf?\n"); + + // DOS remains, 8bit handling + //sfx->data = (void *) W_CacheLumpNum(sfx->lumpnum, PU_MUSIC); + // fprintf( stderr, + // "S_StartSoundAtVolume: loading %d (lump %d) : 0x%x\n", + // sfx_id, sfx->lumpnum, (int)sfx->data ); + + } +#endif + + // increase the usefulness + if (sfx->usefulness++ < 0) + sfx->usefulness = 1; + + // Assigns the handle to one of the channels in the + // mix/output buffer. + channels_s_sound[cnum].handle = I_StartSound(sfx_id, + /*sfx->data,*/ + volume, + sep, + pitch, + priority); +} + + +void S_StartSound(void* origin, int sfx_id) +{ + S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume); +} + + +void S_StopSound(void* origin) +{ + int cnum; + + for (cnum = 0; cnum < numChannels; cnum++) + { + if (channels_s_sound[cnum].sfxinfo && channels_s_sound[cnum].origin == origin) + { + S_StopChannel(cnum); + break; + } + } +} + + +// +// Stop and resume music, during game PAUSE. +// +void S_PauseSound(void) +{ + if (mus_playing_s_sound && !mus_paused) + { + I_PauseSong(mus_playing_s_sound->handle); + mus_paused = true; + } +} + + +void S_ResumeSound(void) +{ + if (mus_playing_s_sound && mus_paused) + { + I_ResumeSong(mus_playing_s_sound->handle); + mus_paused = false; + } +} + + +// +// Updates music & sounds +// +void S_UpdateSounds(void* listener_p) +{ + int audible; + int cnum; + int volume; + int sep; + int pitch; + sfxinfo_t* sfx; + channel_t* c; + + mobj_t* listener = (mobj_t*)listener_p; + + for (cnum = 0; cnum < numChannels; cnum++) + { + c = &channels_s_sound[cnum]; + sfx = c->sfxinfo; + + if (c->sfxinfo) + { + if (I_SoundIsPlaying(c->handle)) + { + // initialize parameters + volume = snd_SfxVolume; + pitch = NORM_PITCH; + sep = NORM_SEP; + + if (sfx->link) + { + pitch = sfx->pitch; + volume += sfx->volume; + if (volume < 1) + { + S_StopChannel(cnum); + continue; + } + else if (volume > snd_SfxVolume) + { + volume = snd_SfxVolume; + } + } + + // check non-local sounds for distance clipping + // or modify their params + if (c->origin && listener_p != c->origin) + { + audible = S_AdjustSoundParams(listener, + c->origin, + &volume, + &sep, + &pitch); + + if (!audible) + { + S_StopChannel(cnum); + } + else + I_UpdateSoundParams(c->handle, volume, sep, pitch); + } + } + else + { + // if channel is allocated but sound has stopped, + // free it + S_StopChannel(cnum); + } + } + } +} + + +void S_SetMusicVolume(int volume) +{ + if (volume < 0 || volume > 127) + { + //I_Error("Error: Attempt to set music volume at %d", + // volume); + doom_strcpy(error_buf, "Error: Attempt to set music volume at "); + doom_concat(error_buf, doom_itoa(volume, 10)); + I_Error(error_buf); + } + + I_SetMusicVolume(127); + I_SetMusicVolume(volume); + snd_MusicVolume = volume; +} + + +void S_SetSfxVolume(int volume) +{ + if (volume < 0 || volume > 127) + { + //I_Error("Error: Attempt to set sfx volume at %d", volume); + doom_strcpy(error_buf, "Error: Attempt to set sfx volume at "); + doom_concat(error_buf, doom_itoa(volume, 10)); + I_Error(error_buf); + } + + snd_SfxVolume = volume; +} + + +// +// Starts some music with the music id found in sounds.h. +// +void S_StartMusic(int m_id) +{ + S_ChangeMusic(m_id, false); +} + + +void S_ChangeMusic(int musicnum, int looping) +{ + musicinfo_t* music; + char namebuf[9]; + + if ((musicnum <= mus_None) + || (musicnum >= NUMMUSIC)) + { + //I_Error("Error: Bad music number %d", musicnum); + doom_strcpy(error_buf, "Error: Bad music number "); + doom_concat(error_buf, doom_itoa(musicnum, 10)); + I_Error(error_buf); + } + else + music = &S_music[musicnum]; + + if (mus_playing_s_sound == music) + return; + + // shutdown old music + S_StopMusic(); + + // get lumpnum if neccessary + if (!music->lumpnum) + { + //doom_sprintf(namebuf, "d_%s", music->name); + doom_strcpy(namebuf, "d_"); + doom_concat(namebuf, music->name); + music->lumpnum = W_GetNumForName(namebuf); + } + + // load & register it + music->data = (void*)W_CacheLumpNum(music->lumpnum, PU_MUSIC); + music->handle = I_RegisterSong(music->data); + + // play it + I_PlaySong(music->handle, looping); + + mus_playing_s_sound = music; +} + + +void S_StopMusic(void) +{ + if (mus_playing_s_sound) + { + if (mus_paused) + I_ResumeSong(mus_playing_s_sound->handle); + + I_StopSong(mus_playing_s_sound->handle); + I_UnRegisterSong(mus_playing_s_sound->handle); + Z_ChangeTag(mus_playing_s_sound->data, PU_CACHE); + + mus_playing_s_sound->data = 0; + mus_playing_s_sound = 0; + } +} + + +void S_StopChannel(int cnum) +{ + int i; + channel_t* c = &channels_s_sound[cnum]; + + if (c->sfxinfo) + { + // stop the sound playing + if (I_SoundIsPlaying(c->handle)) + { +#ifdef SAWDEBUG + if (c->sfxinfo == &S_sfx[sfx_sawful]) + doom_print("stopped\n"); +#endif + I_StopSound(c->handle); + } + + // check to see + // if other channels are playing the sound + for (i = 0; i < numChannels; i++) + { + if (cnum != i + && c->sfxinfo == channels_s_sound[i].sfxinfo) + { + break; + } + } + + // degrade usefulness of sound data + c->sfxinfo->usefulness--; + + c->sfxinfo = 0; + } +} + + +// +// Changes volume, stereo-separation, and pitch variables +// from the norm of a sound effect to be played. +// If the sound is not audible, returns a 0. +// Otherwise, modifies parameters and returns 1. +// +int S_AdjustSoundParams(mobj_t* listener, mobj_t* source, int* vol, int* sep, int* pitch) +{ + fixed_t approx_dist; + fixed_t adx; + fixed_t ady; + angle_t angle; + + // calculate the distance to sound origin + // and clip it if necessary + adx = doom_abs(listener->x - source->x); + ady = doom_abs(listener->y - source->y); + + // From _GG1_ p.428. Appox. eucledian distance fast. + approx_dist = adx + ady - ((adx < ady ? adx : ady) >> 1); + + if (gamemap != 8 + && approx_dist > S_CLIPPING_DIST) + { + return 0; + } + + // angle of source to listener + angle = R_PointToAngle2(listener->x, + listener->y, + source->x, + source->y); + + if (angle > listener->angle) + angle = angle - listener->angle; + else + angle = angle + (0xffffffff - listener->angle); + + angle >>= ANGLETOFINESHIFT; + + // stereo separation + *sep = 128 - (FixedMul(S_STEREO_SWING, finesine[angle]) >> FRACBITS); + + // volume calculation + if (approx_dist < S_CLOSE_DIST) + { + *vol = snd_SfxVolume; + } + else if (gamemap == 8) + { + if (approx_dist > S_CLIPPING_DIST) + approx_dist = S_CLIPPING_DIST; + + *vol = 15 + ((snd_SfxVolume - 15) + * ((S_CLIPPING_DIST - approx_dist) >> FRACBITS)) + / S_ATTENUATOR; + } + else + { + // distance effect + *vol = (snd_SfxVolume + * ((S_CLIPPING_DIST - approx_dist) >> FRACBITS)) + / S_ATTENUATOR; + } + + return (*vol > 0); +} + + +// +// S_getChannel : +// If none available, return -1. Otherwise channel #. +// +int S_getChannel(void* origin, sfxinfo_t* sfxinfo) +{ + // channel number to use + int cnum; + + channel_t* c; + + // Find an open channel + for (cnum = 0; cnum < numChannels; cnum++) + { + if (!channels_s_sound[cnum].sfxinfo) + break; + else if (origin && channels_s_sound[cnum].origin == origin) + { + S_StopChannel(cnum); + break; + } + } + + // None available + if (cnum == numChannels) + { + // Look for lower priority + for (cnum = 0; cnum < numChannels; cnum++) + if (channels_s_sound[cnum].sfxinfo->priority >= sfxinfo->priority) break; + + if (cnum == numChannels) + { + // FUCK! No lower priority. Sorry, Charlie. + return -1; + } + else + { + // Otherwise, kick out lower priority. + S_StopChannel(cnum); + } + } + + c = &channels_s_sound[cnum]; + + // channel is decided to be cnum. + c->sfxinfo = sfxinfo; + c->origin = origin; + + return cnum; +} diff --git a/src/DOOM/s_sound.h b/src/DOOM/s_sound.h new file mode 100644 index 0000000..3ee691a --- /dev/null +++ b/src/DOOM/s_sound.h @@ -0,0 +1,81 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// The not so system specific sound interface. +// +//----------------------------------------------------------------------------- + +#ifndef __S_SOUND__ +#define __S_SOUND__ + + +// +// Initializes sound stuff, including volume +// Sets channels, SFX and music volume, +// allocates channel buffer, sets S_sfx lookup. +// +void S_Init(int sfxVolume, int musicVolume); + +// +// Per level startup code. +// Kills playing sounds at start of level, +// determines music if any, changes music. +// +void S_Start(void); + +// +// Start sound for thing at +// using from sounds.h +// +void S_StartSound(void* origin, int sound_id); + +// Will start a sound at a given volume. +void S_StartSoundAtVolume(void* origin, int sound_id, int volume); + +// Stop sound for thing at +void S_StopSound(void* origin); + +// Start music using from sounds.h +void S_StartMusic(int music_id); + +// Start music using from sounds.h, +// and set whether looping +void S_ChangeMusic(int music_id, int looping); + +// Stops the music fer sure. +void S_StopMusic(void); + +// Stop and resume music, during game PAUSE. +void S_PauseSound(void); +void S_ResumeSound(void); + +// +// Updates music & sounds +// +void S_UpdateSounds(void* listener); + +void S_SetMusicVolume(int volume); +void S_SetSfxVolume(int volume); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/sounds.c b/src/DOOM/sounds.c new file mode 100644 index 0000000..f617fd3 --- /dev/null +++ b/src/DOOM/sounds.c @@ -0,0 +1,223 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Created by a sound utility. +// Kept as a sample, DOOM2 sounds. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "doomtype.h" +#include "sounds.h" + + +// +// Information about all the music +// +musicinfo_t S_music[] = +{ + { 0 }, + { "e1m1", 0 }, + { "e1m2", 0 }, + { "e1m3", 0 }, + { "e1m4", 0 }, + { "e1m5", 0 }, + { "e1m6", 0 }, + { "e1m7", 0 }, + { "e1m8", 0 }, + { "e1m9", 0 }, + { "e2m1", 0 }, + { "e2m2", 0 }, + { "e2m3", 0 }, + { "e2m4", 0 }, + { "e2m5", 0 }, + { "e2m6", 0 }, + { "e2m7", 0 }, + { "e2m8", 0 }, + { "e2m9", 0 }, + { "e3m1", 0 }, + { "e3m2", 0 }, + { "e3m3", 0 }, + { "e3m4", 0 }, + { "e3m5", 0 }, + { "e3m6", 0 }, + { "e3m7", 0 }, + { "e3m8", 0 }, + { "e3m9", 0 }, + { "inter", 0 }, + { "intro", 0 }, + { "bunny", 0 }, + { "victor", 0 }, + { "introa", 0 }, + { "runnin", 0 }, + { "stalks", 0 }, + { "countd", 0 }, + { "betwee", 0 }, + { "doom", 0 }, + { "the_da", 0 }, + { "shawn", 0 }, + { "ddtblu", 0 }, + { "in_cit", 0 }, + { "dead", 0 }, + { "stlks2", 0 }, + { "theda2", 0 }, + { "doom2", 0 }, + { "ddtbl2", 0 }, + { "runni2", 0 }, + { "dead2", 0 }, + { "stlks3", 0 }, + { "romero", 0 }, + { "shawn2", 0 }, + { "messag", 0 }, + { "count2", 0 }, + { "ddtbl3", 0 }, + { "ampie", 0 }, + { "theda3", 0 }, + { "adrian", 0 }, + { "messg2", 0 }, + { "romer2", 0 }, + { "tense", 0 }, + { "shawn3", 0 }, + { "openin", 0 }, + { "evil", 0 }, + { "ultima", 0 }, + { "read_m", 0 }, + { "dm2ttl", 0 }, + { "dm2int", 0 } +}; + + +// +// Information about all the sfx +// +sfxinfo_t S_sfx[] = +{ + // S_sfx[0] needs to be a dummy for odd reasons. + { "none", false, 0, 0, -1, -1, 0 }, + { "pistol", false, 64, 0, -1, -1, 0 }, + { "shotgn", false, 64, 0, -1, -1, 0 }, + { "sgcock", false, 64, 0, -1, -1, 0 }, + { "dshtgn", false, 64, 0, -1, -1, 0 }, + { "dbopn", false, 64, 0, -1, -1, 0 }, + { "dbcls", false, 64, 0, -1, -1, 0 }, + { "dbload", false, 64, 0, -1, -1, 0 }, + { "plasma", false, 64, 0, -1, -1, 0 }, + { "bfg", false, 64, 0, -1, -1, 0 }, + { "sawup", false, 64, 0, -1, -1, 0 }, + { "sawidl", false, 118, 0, -1, -1, 0 }, + { "sawful", false, 64, 0, -1, -1, 0 }, + { "sawhit", false, 64, 0, -1, -1, 0 }, + { "rlaunc", false, 64, 0, -1, -1, 0 }, + { "rxplod", false, 70, 0, -1, -1, 0 }, + { "firsht", false, 70, 0, -1, -1, 0 }, + { "firxpl", false, 70, 0, -1, -1, 0 }, + { "pstart", false, 100, 0, -1, -1, 0 }, + { "pstop", false, 100, 0, -1, -1, 0 }, + { "doropn", false, 100, 0, -1, -1, 0 }, + { "dorcls", false, 100, 0, -1, -1, 0 }, + { "stnmov", false, 119, 0, -1, -1, 0 }, + { "swtchn", false, 78, 0, -1, -1, 0 }, + { "swtchx", false, 78, 0, -1, -1, 0 }, + { "plpain", false, 96, 0, -1, -1, 0 }, + { "dmpain", false, 96, 0, -1, -1, 0 }, + { "popain", false, 96, 0, -1, -1, 0 }, + { "vipain", false, 96, 0, -1, -1, 0 }, + { "mnpain", false, 96, 0, -1, -1, 0 }, + { "pepain", false, 96, 0, -1, -1, 0 }, + { "slop", false, 78, 0, -1, -1, 0 }, + { "itemup", true, 78, 0, -1, -1, 0 }, + { "wpnup", true, 78, 0, -1, -1, 0 }, + { "oof", false, 96, 0, -1, -1, 0 }, + { "telept", false, 32, 0, -1, -1, 0 }, + { "posit1", true, 98, 0, -1, -1, 0 }, + { "posit2", true, 98, 0, -1, -1, 0 }, + { "posit3", true, 98, 0, -1, -1, 0 }, + { "bgsit1", true, 98, 0, -1, -1, 0 }, + { "bgsit2", true, 98, 0, -1, -1, 0 }, + { "sgtsit", true, 98, 0, -1, -1, 0 }, + { "cacsit", true, 98, 0, -1, -1, 0 }, + { "brssit", true, 94, 0, -1, -1, 0 }, + { "cybsit", true, 92, 0, -1, -1, 0 }, + { "spisit", true, 90, 0, -1, -1, 0 }, + { "bspsit", true, 90, 0, -1, -1, 0 }, + { "kntsit", true, 90, 0, -1, -1, 0 }, + { "vilsit", true, 90, 0, -1, -1, 0 }, + { "mansit", true, 90, 0, -1, -1, 0 }, + { "pesit", true, 90, 0, -1, -1, 0 }, + { "sklatk", false, 70, 0, -1, -1, 0 }, + { "sgtatk", false, 70, 0, -1, -1, 0 }, + { "skepch", false, 70, 0, -1, -1, 0 }, + { "vilatk", false, 70, 0, -1, -1, 0 }, + { "claw", false, 70, 0, -1, -1, 0 }, + { "skeswg", false, 70, 0, -1, -1, 0 }, + { "pldeth", false, 32, 0, -1, -1, 0 }, + { "pdiehi", false, 32, 0, -1, -1, 0 }, + { "podth1", false, 70, 0, -1, -1, 0 }, + { "podth2", false, 70, 0, -1, -1, 0 }, + { "podth3", false, 70, 0, -1, -1, 0 }, + { "bgdth1", false, 70, 0, -1, -1, 0 }, + { "bgdth2", false, 70, 0, -1, -1, 0 }, + { "sgtdth", false, 70, 0, -1, -1, 0 }, + { "cacdth", false, 70, 0, -1, -1, 0 }, + { "skldth", false, 70, 0, -1, -1, 0 }, + { "brsdth", false, 32, 0, -1, -1, 0 }, + { "cybdth", false, 32, 0, -1, -1, 0 }, + { "spidth", false, 32, 0, -1, -1, 0 }, + { "bspdth", false, 32, 0, -1, -1, 0 }, + { "vildth", false, 32, 0, -1, -1, 0 }, + { "kntdth", false, 32, 0, -1, -1, 0 }, + { "pedth", false, 32, 0, -1, -1, 0 }, + { "skedth", false, 32, 0, -1, -1, 0 }, + { "posact", true, 120, 0, -1, -1, 0 }, + { "bgact", true, 120, 0, -1, -1, 0 }, + { "dmact", true, 120, 0, -1, -1, 0 }, + { "bspact", true, 100, 0, -1, -1, 0 }, + { "bspwlk", true, 100, 0, -1, -1, 0 }, + { "vilact", true, 100, 0, -1, -1, 0 }, + { "noway", false, 78, 0, -1, -1, 0 }, + { "barexp", false, 60, 0, -1, -1, 0 }, + { "punch", false, 64, 0, -1, -1, 0 }, + { "hoof", false, 70, 0, -1, -1, 0 }, + { "metal", false, 70, 0, -1, -1, 0 }, + { "chgun", false, 64, &S_sfx[sfx_pistol], 150, 0, 0 }, + { "tink", false, 60, 0, -1, -1, 0 }, + { "bdopn", false, 100, 0, -1, -1, 0 }, + { "bdcls", false, 100, 0, -1, -1, 0 }, + { "itmbk", false, 100, 0, -1, -1, 0 }, + { "flame", false, 32, 0, -1, -1, 0 }, + { "flamst", false, 32, 0, -1, -1, 0 }, + { "getpow", false, 60, 0, -1, -1, 0 }, + { "bospit", false, 70, 0, -1, -1, 0 }, + { "boscub", false, 70, 0, -1, -1, 0 }, + { "bossit", false, 70, 0, -1, -1, 0 }, + { "bospn", false, 70, 0, -1, -1, 0 }, + { "bosdth", false, 70, 0, -1, -1, 0 }, + { "manatk", false, 70, 0, -1, -1, 0 }, + { "mandth", false, 70, 0, -1, -1, 0 }, + { "sssit", false, 70, 0, -1, -1, 0 }, + { "ssdth", false, 70, 0, -1, -1, 0 }, + { "keenpn", false, 70, 0, -1, -1, 0 }, + { "keendt", false, 70, 0, -1, -1, 0 }, + { "skeact", false, 70, 0, -1, -1, 0 }, + { "skesit", false, 70, 0, -1, -1, 0 }, + { "skeatk", false, 70, 0, -1, -1, 0 }, + { "radio", false, 60, 0, -1, -1, 0 } +}; diff --git a/src/DOOM/sounds.h b/src/DOOM/sounds.h new file mode 100644 index 0000000..0e053e7 --- /dev/null +++ b/src/DOOM/sounds.h @@ -0,0 +1,290 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Created by the sound utility written by Dave Taylor. +// Kept as a sample, DOOM2 sounds. Frozen. +// +//----------------------------------------------------------------------------- + +#ifndef __SOUNDS__ +#define __SOUNDS__ + + +// +// SoundFX struct. +// +typedef struct sfxinfo_struct sfxinfo_t; + +struct sfxinfo_struct +{ + // up to 6-character name + char* name; + + // Sfx singularity (only one at a time) + int singularity; + + // Sfx priority + int priority; + + // referenced sound if a link + sfxinfo_t* link; + + // pitch if a link + int pitch; + + // volume if a link + int volume; + + // sound data + void* data; + + // this is checked every second to see if sound + // can be thrown out (if 0, then decrement, if -1, + // then throw out, if > 0, then it is in use) + int usefulness; + + // lump number of sfx + int lumpnum; +}; + + +// +// MusicInfo struct. +// +typedef struct +{ + // up to 6-character name + char* name; + + // lump number of music + int lumpnum; + + // music data + void* data; + + // music handle once registered + int handle; +} musicinfo_t; + + +// the complete set of sound effects +extern sfxinfo_t S_sfx[]; + +// the complete set of music +extern musicinfo_t S_music[]; + +// +// Identifiers for all music in game. +// +typedef enum +{ + mus_None, + mus_e1m1, + mus_e1m2, + mus_e1m3, + mus_e1m4, + mus_e1m5, + mus_e1m6, + mus_e1m7, + mus_e1m8, + mus_e1m9, + mus_e2m1, + mus_e2m2, + mus_e2m3, + mus_e2m4, + mus_e2m5, + mus_e2m6, + mus_e2m7, + mus_e2m8, + mus_e2m9, + mus_e3m1, + mus_e3m2, + mus_e3m3, + mus_e3m4, + mus_e3m5, + mus_e3m6, + mus_e3m7, + mus_e3m8, + mus_e3m9, + mus_inter, + mus_intro, + mus_bunny, + mus_victor, + mus_introa, + mus_runnin, + mus_stalks, + mus_countd, + mus_betwee, + mus_doom, + mus_the_da, + mus_shawn, + mus_ddtblu, + mus_in_cit, + mus_dead, + mus_stlks2, + mus_theda2, + mus_doom2, + mus_ddtbl2, + mus_runni2, + mus_dead2, + mus_stlks3, + mus_romero, + mus_shawn2, + mus_messag, + mus_count2, + mus_ddtbl3, + mus_ampie, + mus_theda3, + mus_adrian, + mus_messg2, + mus_romer2, + mus_tense, + mus_shawn3, + mus_openin, + mus_evil, + mus_ultima, + mus_read_m, + mus_dm2ttl, + mus_dm2int, + NUMMUSIC +} musicenum_t; + + +// +// Identifiers for all sfx in game. +// +typedef enum +{ + sfx_None, + sfx_pistol, + sfx_shotgn, + sfx_sgcock, + sfx_dshtgn, + sfx_dbopn, + sfx_dbcls, + sfx_dbload, + sfx_plasma, + sfx_bfg, + sfx_sawup, + sfx_sawidl, + sfx_sawful, + sfx_sawhit, + sfx_rlaunc, + sfx_rxplod, + sfx_firsht, + sfx_firxpl, + sfx_pstart, + sfx_pstop, + sfx_doropn, + sfx_dorcls, + sfx_stnmov, + sfx_swtchn, + sfx_swtchx, + sfx_plpain, + sfx_dmpain, + sfx_popain, + sfx_vipain, + sfx_mnpain, + sfx_pepain, + sfx_slop, + sfx_itemup, + sfx_wpnup, + sfx_oof, + sfx_telept, + sfx_posit1, + sfx_posit2, + sfx_posit3, + sfx_bgsit1, + sfx_bgsit2, + sfx_sgtsit, + sfx_cacsit, + sfx_brssit, + sfx_cybsit, + sfx_spisit, + sfx_bspsit, + sfx_kntsit, + sfx_vilsit, + sfx_mansit, + sfx_pesit, + sfx_sklatk, + sfx_sgtatk, + sfx_skepch, + sfx_vilatk, + sfx_claw, + sfx_skeswg, + sfx_pldeth, + sfx_pdiehi, + sfx_podth1, + sfx_podth2, + sfx_podth3, + sfx_bgdth1, + sfx_bgdth2, + sfx_sgtdth, + sfx_cacdth, + sfx_skldth, + sfx_brsdth, + sfx_cybdth, + sfx_spidth, + sfx_bspdth, + sfx_vildth, + sfx_kntdth, + sfx_pedth, + sfx_skedth, + sfx_posact, + sfx_bgact, + sfx_dmact, + sfx_bspact, + sfx_bspwlk, + sfx_vilact, + sfx_noway, + sfx_barexp, + sfx_punch, + sfx_hoof, + sfx_metal, + sfx_chgun, + sfx_tink, + sfx_bdopn, + sfx_bdcls, + sfx_itmbk, + sfx_flame, + sfx_flamst, + sfx_getpow, + sfx_bospit, + sfx_boscub, + sfx_bossit, + sfx_bospn, + sfx_bosdth, + sfx_manatk, + sfx_mandth, + sfx_sssit, + sfx_ssdth, + sfx_keenpn, + sfx_keendt, + sfx_skeact, + sfx_skesit, + sfx_skeatk, + sfx_radio, + NUMSFX +} sfxenum_t; + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/st_lib.c b/src/DOOM/st_lib.c new file mode 100644 index 0000000..837afdf --- /dev/null +++ b/src/DOOM/st_lib.c @@ -0,0 +1,221 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// The status bar widget code. +// +//----------------------------------------------------------------------------- + +#include "doom_config.h" + + + +#include "doomdef.h" +#include "z_zone.h" +#include "v_video.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "st_stuff.h" +#include "st_lib.h" +#include "r_local.h" + + +// in AM_map.c +extern doom_boolean automapactive; + + +// +// Hack display negative frags. +// Loads and store the stminus lump. +// +patch_t* sttminus; + + +void STlib_init(void) +{ + sttminus = (patch_t*)W_CacheLumpName("STTMINUS", PU_STATIC); +} + +// ? +void STlib_initNum(st_number_t* n, int x, int y, patch_t** pl, int* num, doom_boolean* on, int width) +{ + n->x = x; + n->y = y; + n->oldnum = 0; + n->width = width; + n->num = num; + n->on = on; + n->p = pl; +} + +// +// A fairly efficient way to draw a number +// based on differences from the old number. +// Note: worth the trouble? +// +void STlib_drawNum(st_number_t* n, doom_boolean refresh) +{ + int numdigits = n->width; + int num = *n->num; + + int w = SHORT(n->p[0]->width); + int h = SHORT(n->p[0]->height); + int x = n->x; + + int neg; + + n->oldnum = *n->num; + + neg = num < 0; + + if (neg) + { + if (numdigits == 2 && num < -9) + num = -9; + else if (numdigits == 3 && num < -99) + num = -99; + + num = -num; + } + + // clear the area + x = n->x - numdigits * w; + + if (n->y - ST_Y < 0) + I_Error("Error: drawNum: n->y - ST_Y < 0"); + + V_CopyRect(x, n->y - ST_Y, STLIB_BG, w * numdigits, h, x, n->y, STLIB_FG); + + // if non-number, do not draw it + if (num == 1994) + return; + + x = n->x; + + // in the special case of 0, you draw 0 + if (!num) + V_DrawPatch(x - w, n->y, STLIB_FG, n->p[0]); + + // draw the new number + while (num && numdigits--) + { + x -= w; + V_DrawPatch(x, n->y, STLIB_FG, n->p[num % 10]); + num /= 10; + } + + // draw a minus sign if necessary + if (neg) + V_DrawPatch(x - 8, n->y, STLIB_FG, sttminus); +} + +// +void STlib_updateNum(st_number_t* n, doom_boolean refresh) +{ + if (*n->on) STlib_drawNum(n, refresh); +} + +// +void STlib_initPercent(st_percent_t* p, int x, int y, patch_t** pl, int* num, doom_boolean* on, patch_t* percent) +{ + STlib_initNum(&p->n, x, y, pl, num, on, 3); + p->p = percent; +} + +void STlib_updatePercent(st_percent_t* per, int refresh) +{ + if (refresh && *per->n.on) + V_DrawPatch(per->n.x, per->n.y, STLIB_FG, per->p); + + STlib_updateNum(&per->n, refresh); +} + +void STlib_initMultIcon(st_multicon_t* i, int x, int y, patch_t** il, int* inum, doom_boolean* on) +{ + i->x = x; + i->y = y; + i->oldinum = -1; + i->inum = inum; + i->on = on; + i->p = il; +} + +void STlib_updateMultIcon(st_multicon_t* mi, doom_boolean refresh) +{ + int w; + int h; + int x; + int y; + + if (*mi->on + && (mi->oldinum != *mi->inum || refresh) + && (*mi->inum != -1)) + { + if (mi->oldinum != -1) + { + x = mi->x - SHORT(mi->p[mi->oldinum]->leftoffset); + y = mi->y - SHORT(mi->p[mi->oldinum]->topoffset); + w = SHORT(mi->p[mi->oldinum]->width); + h = SHORT(mi->p[mi->oldinum]->height); + + if (y - ST_Y < 0) + I_Error("Error: updateMultIcon: y - ST_Y < 0"); + + V_CopyRect(x, y - ST_Y, STLIB_BG, w, h, x, y, STLIB_FG); + } + V_DrawPatch(mi->x, mi->y, STLIB_FG, mi->p[*mi->inum]); + mi->oldinum = *mi->inum; + } +} + +void STlib_initBinIcon(st_binicon_t* b, int x, int y, patch_t* i, doom_boolean* val, doom_boolean* on) +{ + b->x = x; + b->y = y; + b->oldval = 0; + b->val = val; + b->on = on; + b->p = i; +} + +void STlib_updateBinIcon(st_binicon_t* bi, doom_boolean refresh) +{ + int x; + int y; + int w; + int h; + + if (*bi->on && (bi->oldval != *bi->val || refresh)) + { + x = bi->x - SHORT(bi->p->leftoffset); + y = bi->y - SHORT(bi->p->topoffset); + w = SHORT(bi->p->width); + h = SHORT(bi->p->height); + + if (y - ST_Y < 0) + I_Error("Error: updateBinIcon: y - ST_Y < 0"); + + if (*bi->val) + V_DrawPatch(bi->x, bi->y, STLIB_FG, bi->p); + else + V_CopyRect(x, y - ST_Y, STLIB_BG, w, h, x, y, STLIB_FG); + + bi->oldval = *bi->val; + } +} diff --git a/src/DOOM/st_lib.h b/src/DOOM/st_lib.h new file mode 100644 index 0000000..227cc16 --- /dev/null +++ b/src/DOOM/st_lib.h @@ -0,0 +1,189 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// The status bar widget code. +// +//----------------------------------------------------------------------------- + +#ifndef __STLIB__ +#define __STLIB__ + + +// We are referring to patches. +#include "r_defs.h" + + +// +// Background and foreground screen numbers +// +#define STLIB_BG 4 +#define STLIB_FG 0 + + +// +// Typedefs of widgets +// + +// Number widget +typedef struct +{ + // upper right-hand corner + // of the number (right-justified) + int x; + int y; + + // max # of digits in number + int width; + + // last number value + int oldnum; + + // pointer to current value + int* num; + + // pointer to doom_boolean stating + // whether to update number + doom_boolean* on; + + // list of patches for 0-9 + patch_t** p; + + // user data + int data; +} st_number_t; + + +// Percent widget ("child" of number widget, +// or, more precisely, contains a number widget.) +typedef struct +{ + // number information + st_number_t n; + + // percent sign graphic + patch_t* p; +} st_percent_t; + + +// Multiple Icon widget +typedef struct +{ + // center-justified location of icons + int x; + int y; + + // last icon number + int oldinum; + + // pointer to current icon + int* inum; + + // pointer to doom_boolean stating + // whether to update icon + doom_boolean* on; + + // list of icons + patch_t** p; + + // user data + int data; +} st_multicon_t; + + +// Binary Icon widget +typedef struct +{ + // center-justified location of icon + int x; + int y; + + // last icon value + int oldval; + + // pointer to current icon status + doom_boolean* val; + + // pointer to doom_boolean + // stating whether to update icon + doom_boolean* on; + + + patch_t* p; // icon + int data; // user data + +} st_binicon_t; + + +// +// Widget creation, access, and update routines +// + +// Initializes widget library. +// More precisely, initialize STMINUS, +// everything else is done somewhere else. +// +void STlib_init(void); + +// Number widget routines +void STlib_initNum(st_number_t* n, + int x, + int y, + patch_t** pl, + int* num, + doom_boolean* on, + int width); + +void STlib_updateNum(st_number_t* n, doom_boolean refresh); + +// Percent widget routines +void STlib_initPercent(st_percent_t* p, + int x, + int y, + patch_t** pl, + int* num, + doom_boolean* on, + patch_t* percent); + +void STlib_updatePercent(st_percent_t* per, int refresh); + +// Multiple Icon widget routines +void STlib_initMultIcon(st_multicon_t* mi, + int x, + int y, + patch_t** il, + int* inum, + doom_boolean* on); + +void STlib_updateMultIcon(st_multicon_t* mi, doom_boolean refresh); + +// Binary Icon widget routines +void STlib_initBinIcon(st_binicon_t* b, + int x, + int y, + patch_t* i, + doom_boolean* val, + doom_boolean* on); + +void STlib_updateBinIcon(st_binicon_t* bi, doom_boolean refresh); + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/st_stuff.c b/src/DOOM/st_stuff.c new file mode 100644 index 0000000..001bdcf --- /dev/null +++ b/src/DOOM/st_stuff.c @@ -0,0 +1,1354 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Status bar code. +// Does the face/direction indicator animatin. +// Does palette indicators as well (red pain/berserk, bright pickup) +// +//----------------------------------------------------------------------------- + +#include "doom_config.h" + +#include "i_system.h" +#include "i_video.h" +#include "z_zone.h" +#include "m_random.h" +#include "w_wad.h" +#include "doomdef.h" +#include "g_game.h" +#include "st_stuff.h" +#include "st_lib.h" +#include "r_local.h" +#include "p_local.h" +#include "p_inter.h" +#include "am_map.h" +#include "m_cheat.h" +#include "s_sound.h" +#include "v_video.h" // Needs access to LFB. +#include "doomstat.h" // State. +#include "dstrings.h" // Data. +#include "sounds.h" + +// +// STATUS BAR DATA +// + +// Palette indices. +// For damage/bonus red-/gold-shifts +#define STARTREDPALS 1 +#define STARTBONUSPALS 9 +#define NUMREDPALS 8 +#define NUMBONUSPALS 4 +// Radiation suit, green shift. +#define RADIATIONPAL 13 + +// N/256*100% probability +// that the normal face state will change +#define ST_FACEPROBABILITY 96 + +// For Responder +#define ST_TOGGLECHAT KEY_ENTER + +// Location of status bar +#define ST_X 0 +#define ST_X2 104 + +#define ST_FX 143 +#define ST_FY 169 + +// Should be set to patch width +// for tall numbers later on +#define ST_TALLNUMWIDTH (tallnum[0]->width) + +// Number of status faces. +#define ST_NUMPAINFACES 5 +#define ST_NUMSTRAIGHTFACES 3 +#define ST_NUMTURNFACES 2 +#define ST_NUMSPECIALFACES 3 + +#define ST_FACESTRIDE (ST_NUMSTRAIGHTFACES + ST_NUMTURNFACES + ST_NUMSPECIALFACES) + +#define ST_NUMEXTRAFACES 2 + +#define ST_NUMFACES (ST_FACESTRIDE * ST_NUMPAINFACES + ST_NUMEXTRAFACES) + +#define ST_TURNOFFSET (ST_NUMSTRAIGHTFACES) +#define ST_OUCHOFFSET (ST_TURNOFFSET + ST_NUMTURNFACES) +#define ST_EVILGRINOFFSET (ST_OUCHOFFSET + 1) +#define ST_RAMPAGEOFFSET (ST_EVILGRINOFFSET + 1) +#define ST_GODFACE (ST_NUMPAINFACES*ST_FACESTRIDE) +#define ST_DEADFACE (ST_GODFACE+1) + +#define ST_FACESX 143 +#define ST_FACESY 168 + +#define ST_EVILGRINCOUNT (2*TICRATE) +#define ST_STRAIGHTFACECOUNT (TICRATE/2) +#define ST_TURNCOUNT (1*TICRATE) +#define ST_OUCHCOUNT (1*TICRATE) +#define ST_RAMPAGEDELAY (2*TICRATE) + +#define ST_MUCHPAIN 20 + +// Location and size of statistics, +// justified according to widget type. +// Problem is, within which space? STbar? Screen? +// Note: this could be read in by a lump. +// Problem is, is the stuff rendered +// into a buffer, +// or into the frame buffer? + +// AMMO number pos. +#define ST_AMMOWIDTH 3 +#define ST_AMMOX 44 +#define ST_AMMOY 171 + +// HEALTH number pos. +#define ST_HEALTHWIDTH 3 +#define ST_HEALTHX 90 +#define ST_HEALTHY 171 + +// Weapon pos. +#define ST_ARMSX 111 +#define ST_ARMSY 172 +#define ST_ARMSBGX 104 +#define ST_ARMSBGY 168 +#define ST_ARMSXSPACE 12 +#define ST_ARMSYSPACE 10 + +// Frags pos. +#define ST_FRAGSX 138 +#define ST_FRAGSY 171 +#define ST_FRAGSWIDTH 2 + +// ARMOR number pos. +#define ST_ARMORWIDTH 3 +#define ST_ARMORX 221 +#define ST_ARMORY 171 + +// Key icon positions. +#define ST_KEY0WIDTH 8 +#define ST_KEY0HEIGHT 5 +#define ST_KEY0X 239 +#define ST_KEY0Y 171 +#define ST_KEY1WIDTH ST_KEY0WIDTH +#define ST_KEY1X 239 +#define ST_KEY1Y 181 +#define ST_KEY2WIDTH ST_KEY0WIDTH +#define ST_KEY2X 239 +#define ST_KEY2Y 191 + +// Ammunition counter. +#define ST_AMMO0WIDTH 3 +#define ST_AMMO0HEIGHT 6 +#define ST_AMMO0X 288 +#define ST_AMMO0Y 173 +#define ST_AMMO1WIDTH ST_AMMO0WIDTH +#define ST_AMMO1X 288 +#define ST_AMMO1Y 179 +#define ST_AMMO2WIDTH ST_AMMO0WIDTH +#define ST_AMMO2X 288 +#define ST_AMMO2Y 191 +#define ST_AMMO3WIDTH ST_AMMO0WIDTH +#define ST_AMMO3X 288 +#define ST_AMMO3Y 185 + +// Indicate maximum ammunition. +// Only needed because backpack exists. +#define ST_MAXAMMO0WIDTH 3 +#define ST_MAXAMMO0HEIGHT 5 +#define ST_MAXAMMO0X 314 +#define ST_MAXAMMO0Y 173 +#define ST_MAXAMMO1WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO1X 314 +#define ST_MAXAMMO1Y 179 +#define ST_MAXAMMO2WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO2X 314 +#define ST_MAXAMMO2Y 191 +#define ST_MAXAMMO3WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO3X 314 +#define ST_MAXAMMO3Y 185 + +// pistol +#define ST_WEAPON0X 110 +#define ST_WEAPON0Y 172 + +// shotgun +#define ST_WEAPON1X 122 +#define ST_WEAPON1Y 172 + +// chain gun +#define ST_WEAPON2X 134 +#define ST_WEAPON2Y 172 + +// missile launcher +#define ST_WEAPON3X 110 +#define ST_WEAPON3Y 181 + +// plasma gun +#define ST_WEAPON4X 122 +#define ST_WEAPON4Y 181 + + // bfg +#define ST_WEAPON5X 134 +#define ST_WEAPON5Y 181 + +// WPNS title +#define ST_WPNSX 109 +#define ST_WPNSY 191 + + // DETH title +#define ST_DETHX 109 +#define ST_DETHY 191 + +//Incoming messages window location +#define ST_MSGTEXTX 0 +#define ST_MSGTEXTY 0 +// Dimensions given in characters. +#define ST_MSGWIDTH 52 +// Or shall I say, in lines? +#define ST_MSGHEIGHT 1 + +#define ST_OUTTEXTX 0 +#define ST_OUTTEXTY 6 + +// Width, in characters again. +#define ST_OUTWIDTH 52 + // Height, in lines. +#define ST_OUTHEIGHT 1 + +#define ST_MAPWIDTH (doom_strlen(mapnames[(gameepisode - 1) * 9 + (gamemap - 1)])) + +#define ST_MAPTITLEX (SCREENWIDTH - ST_MAPWIDTH * ST_CHATFONTWIDTH) + +#define ST_MAPTITLEY 0 +#define ST_MAPHEIGHT 1 + + +static player_t* plyr; // main player in game +static doom_boolean st_firsttime; // ST_Start() has just been called +static int veryfirsttime = 1; // used to execute ST_Init() only once +static int lu_palette; // lump number for PLAYPAL +static unsigned int st_clock; // used for timing +static int st_msgcounter = 0; // used for making messages go away +static st_chatstateenum_t st_chatstate; // used when in chat +static st_stateenum_t st_gamestate; // whether in automap or first-person +static doom_boolean st_statusbaron; // whether left-side main status bar is active +static doom_boolean st_chat; // whether status bar chat is active +static doom_boolean st_oldchat; // value of st_chat before message popped up +static doom_boolean st_cursoron; // whether chat window has the cursor on +static doom_boolean st_notdeathmatch; // !deathmatch +static doom_boolean st_armson; // !deathmatch && st_statusbaron +static doom_boolean st_fragson; // !deathmatch +static patch_t* sbar; // main bar left +static patch_t* tallnum[10]; // 0-9, tall numbers +static patch_t* tallpercent; // tall % sign +static patch_t* shortnum[10]; // 0-9, short, yellow (,different!) numbers +static patch_t* keys[NUMCARDS]; // 3 key-cards, 3 skulls +static patch_t* faces[ST_NUMFACES]; // face status patches +static patch_t* faceback; // face background +static patch_t* armsbg; // main bar right +static patch_t* arms[6][2]; // weapon ownership patches +static st_number_t w_ready; // ready-weapon widget +static st_number_t w_frags; // in deathmatch only, summary of frags stats +static st_percent_t w_health; // health widget +static st_binicon_t w_armsbg; // arms background +static st_multicon_t w_arms[6]; // weapon ownership widgets +static st_multicon_t w_faces; // face status widget +static st_multicon_t w_keyboxes[3]; // keycard widgets +static st_percent_t w_armor; // armor widget +static st_number_t w_ammo[4]; // ammo widgets +static st_number_t w_maxammo[4]; // max ammo widgets +static int st_fragscount; // number of frags so far in deathmatch +static int st_oldhealth = -1; // used to use appopriately pained face +static doom_boolean oldweaponsowned[NUMWEAPONS]; // used for evil grin +static int st_facecount = 0; // count until face changes +static int st_faceindex = 0; // current face index, used by w_faces +static int keyboxes[3]; // holds key-type for each key box on bar +static int st_randomnumber; // a random number per tick +static int st_palette = 0; +static doom_boolean st_stopped = true; + + +// Massive bunches of cheat shit +// to keep it from being easy to figure them out. +// Yeah, right... +unsigned char cheat_mus_seq[] = +{ + 0xb2, 0x26, 0xb6, 0xae, 0xea, 1, 0, 0, 0xff +}; + +unsigned char cheat_choppers_seq[] = +{ + 0xb2, 0x26, 0xe2, 0x32, 0xf6, 0x2a, 0x2a, 0xa6, 0x6a, 0xea, 0xff // id... +}; + +unsigned char cheat_god_seq[] = +{ + 0xb2, 0x26, 0x26, 0xaa, 0x26, 0xff // iddqd +}; + +unsigned char cheat_ammo_seq[] = +{ + 0xb2, 0x26, 0xf2, 0x66, 0xa2, 0xff // idkfa +}; + +unsigned char cheat_ammonokey_seq[] = +{ + 0xb2, 0x26, 0x66, 0xa2, 0xff // idfa +}; + +// Smashing Pumpkins Into Samml Piles Of Putried Debris. +unsigned char cheat_noclip_seq[] = +{ + 0xb2, 0x26, 0xea, 0x2a, 0xb2, // idspispopd + 0xea, 0x2a, 0xf6, 0x2a, 0x26, 0xff +}; + +// +unsigned char cheat_commercial_noclip_seq[] = +{ + 0xb2, 0x26, 0xe2, 0x36, 0xb2, 0x2a, 0xff // idclip +}; + +unsigned char cheat_powerup_seq[7][10] = +{ + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x6e, 0xff }, // beholdv + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xea, 0xff }, // beholds + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xb2, 0xff }, // beholdi + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x6a, 0xff }, // beholdr + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xa2, 0xff }, // beholda + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x36, 0xff }, // beholdl + { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xff } // behold +}; + +unsigned char cheat_clev_seq[] = +{ + 0xb2, 0x26, 0xe2, 0x36, 0xa6, 0x6e, 1, 0, 0, 0xff // idclev +}; + +// my position cheat +unsigned char cheat_mypos_seq[] = +{ + 0xb2, 0x26, 0xb6, 0xba, 0x2a, 0xf6, 0xea, 0xff // idmypos +}; + +// Now what? +cheatseq_t cheat_mus = { cheat_mus_seq, 0 }; +cheatseq_t cheat_god = { cheat_god_seq, 0 }; +cheatseq_t cheat_ammo = { cheat_ammo_seq, 0 }; +cheatseq_t cheat_ammonokey = { cheat_ammonokey_seq, 0 }; +cheatseq_t cheat_noclip = { cheat_noclip_seq, 0 }; +cheatseq_t cheat_commercial_noclip = { cheat_commercial_noclip_seq, 0 }; + +cheatseq_t cheat_powerup[7] = +{ + { cheat_powerup_seq[0], 0 }, + { cheat_powerup_seq[1], 0 }, + { cheat_powerup_seq[2], 0 }, + { cheat_powerup_seq[3], 0 }, + { cheat_powerup_seq[4], 0 }, + { cheat_powerup_seq[5], 0 }, + { cheat_powerup_seq[6], 0 } +}; + +cheatseq_t cheat_choppers = { cheat_choppers_seq, 0 }; +cheatseq_t cheat_clev = { cheat_clev_seq, 0 }; +cheatseq_t cheat_mypos = { cheat_mypos_seq, 0 }; + + +extern char* mapnames[]; +extern int doom_flags; + + +void ST_Stop(void); + + +// +// STATUS BAR CODE +// + +void ST_refreshBackground(void) +{ + if (st_statusbaron) + { + V_DrawPatch(ST_X, 0, STLIB_BG, sbar); + + if (netgame) + V_DrawPatch(ST_FX, 0, STLIB_BG, faceback); + + V_CopyRect(ST_X, 0, STLIB_BG, ST_WIDTH, ST_HEIGHT, ST_X, ST_Y, STLIB_FG); + } +} + + +// Respond to keyboard input events, +// intercept cheats. +doom_boolean ST_Responder(event_t* ev) +{ + int i; + + // Filter automap on/off. + if (ev->type == ev_keyup + && ((ev->data1 & 0xffff0000) == AM_MSGHEADER)) + { + switch (ev->data1) + { + case AM_MSGENTERED: + st_gamestate = AutomapState; + st_firsttime = true; + break; + + case AM_MSGEXITED: + // doom_print("AM exited\n"); + st_gamestate = FirstPersonState; + break; + } + } + + // if a user keypress... + else if (ev->type == ev_keydown) + { + if (!netgame) + { + // b. - enabled for more debug fun. + // if (gameskill != sk_nightmare) { + + // 'dqd' cheat for toggleable god mode + if (cht_CheckCheat(&cheat_god, ev->data1)) + { + plyr->cheats ^= CF_GODMODE; + if (plyr->cheats & CF_GODMODE) + { + if (plyr->mo) + plyr->mo->health = 100; + + plyr->health = 100; + plyr->message = STSTR_DQDON; + } + else + plyr->message = STSTR_DQDOFF; + } + // 'fa' cheat for killer fucking arsenal + else if (cht_CheckCheat(&cheat_ammonokey, ev->data1)) + { + plyr->armorpoints = 200; + plyr->armortype = 2; + + for (i = 0; i < NUMWEAPONS; i++) + plyr->weaponowned[i] = true; + + for (i = 0; i < NUMAMMO; i++) + plyr->ammo[i] = plyr->maxammo[i]; + + plyr->message = STSTR_FAADDED; + } + // 'kfa' cheat for key full ammo + else if (cht_CheckCheat(&cheat_ammo, ev->data1)) + { + plyr->armorpoints = 200; + plyr->armortype = 2; + + for (i = 0; i < NUMWEAPONS; i++) + plyr->weaponowned[i] = true; + + for (i = 0; i < NUMAMMO; i++) + plyr->ammo[i] = plyr->maxammo[i]; + + for (i = 0; i < NUMCARDS; i++) + plyr->cards[i] = true; + + plyr->message = STSTR_KFAADDED; + } + // 'mus' cheat for changing music + else if (cht_CheckCheat(&cheat_mus, ev->data1)) + { + char buf[3]; + int musnum; + + plyr->message = STSTR_MUS; + cht_GetParam(&cheat_mus, buf); + + if (gamemode == commercial) + { + musnum = mus_runnin + (buf[0] - '0') * 10 + buf[1] - '0' - 1; + + if (((buf[0] - '0') * 10 + buf[1] - '0') > 35) + plyr->message = STSTR_NOMUS; + else + S_ChangeMusic(musnum, 1); + } + else + { + musnum = mus_e1m1 + (buf[0] - '1') * 9 + (buf[1] - '1'); + + if (((buf[0] - '1') * 9 + buf[1] - '1') > 31) + plyr->message = STSTR_NOMUS; + else + S_ChangeMusic(musnum, 1); + } + } + // Simplified, accepting both "noclip" and "idspispopd". + // no clipping mode cheat + else if (cht_CheckCheat(&cheat_noclip, ev->data1) + || cht_CheckCheat(&cheat_commercial_noclip, ev->data1)) + { + plyr->cheats ^= CF_NOCLIP; + + if (plyr->cheats & CF_NOCLIP) + plyr->message = STSTR_NCON; + else + plyr->message = STSTR_NCOFF; + } + // 'behold?' power-up cheats + for (i = 0; i < 6; i++) + { + if (cht_CheckCheat(&cheat_powerup[i], ev->data1)) + { + if (!plyr->powers[i]) + P_GivePower(plyr, i); + else if (i != pw_strength) + plyr->powers[i] = 1; + else + plyr->powers[i] = 0; + + plyr->message = STSTR_BEHOLDX; + } + } + + // 'behold' power-up menu + if (cht_CheckCheat(&cheat_powerup[6], ev->data1)) + { + plyr->message = STSTR_BEHOLD; + } + // 'choppers' invulnerability & chainsaw + else if (cht_CheckCheat(&cheat_choppers, ev->data1)) + { + plyr->weaponowned[wp_chainsaw] = true; + plyr->powers[pw_invulnerability] = true; + plyr->message = STSTR_CHOPPERS; + } + // 'mypos' for player position + else if (cht_CheckCheat(&cheat_mypos, ev->data1)) + { + static char buf[ST_MSGWIDTH]; + //doom_sprintf(buf, "ang=0x%x;x,y=(0x%x,0x%x)", + // players[consoleplayer].mo->angle, + // players[consoleplayer].mo->x, + // players[consoleplayer].mo->y); + doom_strcpy(buf, "ang=0x"); + doom_concat(buf, doom_itoa(players[consoleplayer].mo->angle, 16)); + doom_concat(buf, ";x,y=(0x"); + doom_concat(buf, doom_itoa(players[consoleplayer].mo->x, 16)); + doom_concat(buf, ",0x"); + doom_concat(buf, doom_itoa(players[consoleplayer].mo->y, 16)); + doom_concat(buf, ")"); + plyr->message = buf; + } + } + + // 'clev' change-level cheat + if (cht_CheckCheat(&cheat_clev, ev->data1)) + { + char buf[3]; + int epsd; + int map; + + cht_GetParam(&cheat_clev, buf); + + if (gamemode == commercial) + { + epsd = 0; + map = (buf[0] - '0') * 10 + buf[1] - '0'; + } + else + { + epsd = buf[0] - '0'; + map = buf[1] - '0'; + } + + // Catch invalid maps. + if (epsd < 1) + return false; + + if (map < 1) + return false; + + // Ohmygod - this is not going to work. + if ((gamemode == retail) + && ((epsd > 4) || (map > 9))) + return false; + + if ((gamemode == registered) + && ((epsd > 3) || (map > 9))) + return false; + + if ((gamemode == shareware) + && ((epsd > 1) || (map > 9))) + return false; + + if ((gamemode == commercial) + && ((epsd > 1) || (map > 34))) + return false; + + // So be it. + plyr->message = STSTR_CLEV; + G_DeferedInitNew(gameskill, epsd, map); + } + } + return false; +} + + +int ST_calcPainOffset(void) +{ + int health; + static int lastcalc; + static int oldhealth = -1; + + health = plyr->health > 100 ? 100 : plyr->health; + + if (health != oldhealth) + { + lastcalc = ST_FACESTRIDE * (((100 - health) * ST_NUMPAINFACES) / 101); + oldhealth = health; + } + return lastcalc; +} + + +// +// This is a not-very-pretty routine which handles +// the face states and their timing. +// the precedence of expressions is: +// dead > evil grin > turned head > straight ahead +// +void ST_updateFaceWidget(void) +{ + int i; + angle_t badguyangle; + angle_t diffang; + static int lastattackdown = -1; + static int priority = 0; + doom_boolean doevilgrin; + + if (priority < 10) + { + // dead + if (!plyr->health) + { + priority = 9; + st_faceindex = ST_DEADFACE; + st_facecount = 1; + } + } + + if (priority < 9) + { + if (plyr->bonuscount) + { + // picking up bonus + doevilgrin = false; + + for (i = 0; i < NUMWEAPONS; i++) + { + if (oldweaponsowned[i] != plyr->weaponowned[i]) + { + doevilgrin = true; + oldweaponsowned[i] = plyr->weaponowned[i]; + } + } + if (doevilgrin) + { + // evil grin if just picked up weapon + priority = 8; + st_facecount = ST_EVILGRINCOUNT; + st_faceindex = ST_calcPainOffset() + ST_EVILGRINOFFSET; + } + } + + } + + if (priority < 8) + { + if (plyr->damagecount + && plyr->attacker + && plyr->attacker != plyr->mo) + { + // being attacked + priority = 7; + + if (plyr->health - st_oldhealth > ST_MUCHPAIN) + { + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; + } + else + { + badguyangle = R_PointToAngle2(plyr->mo->x, + plyr->mo->y, + plyr->attacker->x, + plyr->attacker->y); + + if (badguyangle > plyr->mo->angle) + { + // whether right or left + diffang = badguyangle - plyr->mo->angle; + i = diffang > ANG180; + } + else + { + // whether left or right + diffang = plyr->mo->angle - badguyangle; + i = diffang <= ANG180; + } // confusing, aint it? + + + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset(); + + if (diffang < ANG45) + { + // head-on + st_faceindex += ST_RAMPAGEOFFSET; + } + else if (i) + { + // turn face right + st_faceindex += ST_TURNOFFSET; + } + else + { + // turn face left + st_faceindex += ST_TURNOFFSET + 1; + } + } + } + } + + if (priority < 7) + { + // getting hurt because of your own damn stupidity + if (plyr->damagecount) + { + if (plyr->health - st_oldhealth > ST_MUCHPAIN) + { + priority = 7; + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; + } + else + { + priority = 6; + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET; + } + + } + + } + + if (priority < 6) + { + // rapid firing + if (plyr->attackdown) + { + if (lastattackdown == -1) + lastattackdown = ST_RAMPAGEDELAY; + else if (!--lastattackdown) + { + priority = 5; + st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET; + st_facecount = 1; + lastattackdown = 1; + } + } + else + lastattackdown = -1; + + } + + if (priority < 5) + { + // invulnerability + if ((plyr->cheats & CF_GODMODE) + || plyr->powers[pw_invulnerability]) + { + priority = 4; + + st_faceindex = ST_GODFACE; + st_facecount = 1; + } + } + + // look left or look right if the facecount has timed out + if (!st_facecount) + { + st_faceindex = ST_calcPainOffset() + (st_randomnumber % 3); + st_facecount = ST_STRAIGHTFACECOUNT; + priority = 0; + } + + st_facecount--; +} + + +void ST_updateWidgets(void) +{ + static int largeammo = 1994; // means "n/a" + int i; + + if (weaponinfo[plyr->readyweapon].ammo == am_noammo) + w_ready.num = &largeammo; + else + w_ready.num = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo]; + + w_ready.data = plyr->readyweapon; + + // update keycard multiple widgets + for (i = 0; i < 3; i++) + { + keyboxes[i] = plyr->cards[i] ? i : -1; + + if (plyr->cards[i + 3]) + keyboxes[i] = i + 3; + } + + // refresh everything if this is him coming back to life + ST_updateFaceWidget(); + + // used by the w_armsbg widget + st_notdeathmatch = !deathmatch; + + // used by w_arms[] widgets + st_armson = st_statusbaron && !deathmatch; + + // used by w_frags widget + st_fragson = deathmatch && st_statusbaron; + st_fragscount = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (i != consoleplayer) + st_fragscount += plyr->frags[i]; + else + st_fragscount -= plyr->frags[i]; + } + + // get rid of chat window if up because of message + if (!--st_msgcounter) + st_chat = st_oldchat; +} + + +void ST_Ticker(void) +{ + st_clock++; + st_randomnumber = M_Random(); + ST_updateWidgets(); + st_oldhealth = plyr->health; +} + + +void ST_doPaletteStuff(void) +{ + int palette; + byte* pal; + int cnt; + int bzc; + + cnt = plyr->damagecount; + + if (plyr->powers[pw_strength]) + { + // slowly fade the berzerk out + bzc = 12 - (plyr->powers[pw_strength] >> 6); + + if (bzc > cnt) + cnt = bzc; + } + + if (cnt) + { + palette = (cnt + 7) >> 3; + + if (palette >= NUMREDPALS) + palette = NUMREDPALS - 1; + + palette += STARTREDPALS; + } + + else if (plyr->bonuscount) + { + palette = (plyr->bonuscount + 7) >> 3; + + if (palette >= NUMBONUSPALS) + palette = NUMBONUSPALS - 1; + + palette += STARTBONUSPALS; + } + + else if (plyr->powers[pw_ironfeet] > 4 * 32 + || plyr->powers[pw_ironfeet] & 8) + palette = RADIATIONPAL; + else + palette = 0; + + if (palette != st_palette) + { + st_palette = palette; + pal = (byte*)W_CacheLumpNum(lu_palette, PU_CACHE) + palette * 768; + I_SetPalette(pal); + } +} + + +void ST_drawWidgets(doom_boolean refresh) +{ + int i; + + // used by w_arms[] widgets + st_armson = st_statusbaron && !deathmatch; + + // used by w_frags widget + st_fragson = deathmatch && st_statusbaron; + + STlib_updateNum(&w_ready, refresh); + + for (i = 0; i < 4; i++) + { + STlib_updateNum(&w_ammo[i], refresh); + STlib_updateNum(&w_maxammo[i], refresh); + } + + STlib_updatePercent(&w_health, refresh); + STlib_updatePercent(&w_armor, refresh); + + STlib_updateBinIcon(&w_armsbg, refresh); + + for (i = 0; i < 6; i++) + STlib_updateMultIcon(&w_arms[i], refresh); + + STlib_updateMultIcon(&w_faces, refresh); + + for (i = 0; i < 3; i++) + STlib_updateMultIcon(&w_keyboxes[i], refresh); + + STlib_updateNum(&w_frags, refresh); +} + + +void ST_doRefresh(void) +{ + st_firsttime = false; + + // draw status bar background to off-screen buff + ST_refreshBackground(); + + // and refresh all widgets + ST_drawWidgets(true); +} + + +void ST_diffDraw(void) +{ + // update all widgets + ST_drawWidgets(false); +} + + +void ST_Drawer(doom_boolean fullscreen, doom_boolean refresh) +{ + st_statusbaron = (!fullscreen) || automapactive; + st_firsttime = st_firsttime || refresh; + + // Do red-/gold-shifts from damage/items + ST_doPaletteStuff(); + + // If just after ST_Start(), refresh all + if (doom_flags & DOOM_FLAG_MENU_DARKEN_BG) + { + ST_doRefresh(); + } + else + { + if (st_firsttime) ST_doRefresh(); + // Otherwise, update as little as possible + else ST_diffDraw(); + } +} + + +void ST_loadGraphics(void) +{ + int i; + int j; + int facenum; + + char namebuf[9]; + + // Load the numbers, tall and short + for (i = 0; i < 10; i++) + { + //doom_sprintf(namebuf, "STTNUM%d", i); + doom_strcpy(namebuf, "STTNUM"); + doom_concat(namebuf, doom_itoa(i, 10)); + tallnum[i] = (patch_t*)W_CacheLumpName(namebuf, PU_STATIC); + + //doom_sprintf(namebuf, "STYSNUM%d", i); + doom_strcpy(namebuf, "STYSNUM"); + doom_concat(namebuf, doom_itoa(i, 10)); + shortnum[i] = (patch_t*)W_CacheLumpName(namebuf, PU_STATIC); + } + + // Load percent key. + //Note: why not load STMINUS here, too? + tallpercent = (patch_t*)W_CacheLumpName("STTPRCNT", PU_STATIC); + + // key cards + for (i = 0; i < NUMCARDS; i++) + { + //doom_sprintf(namebuf, "STKEYS%d", i); + doom_strcpy(namebuf, "STKEYS"); + doom_concat(namebuf, doom_itoa(i, 10)); + keys[i] = (patch_t*)W_CacheLumpName(namebuf, PU_STATIC); + } + + // arms background + armsbg = (patch_t*)W_CacheLumpName("STARMS", PU_STATIC); + + // arms ownership widgets + for (i = 0; i < 6; i++) + { + //doom_sprintf(namebuf, "STGNUM%d", i + 2); + doom_strcpy(namebuf, "STGNUM"); + doom_concat(namebuf, doom_itoa(i + 2, 10)); + + // gray # + arms[i][0] = (patch_t*)W_CacheLumpName(namebuf, PU_STATIC); + + // yellow # + arms[i][1] = shortnum[i + 2]; + } + + // face backgrounds for different color players + //doom_sprintf(namebuf, "STFB%d", consoleplayer); + doom_strcpy(namebuf, "STFB"); + doom_concat(namebuf, doom_itoa(consoleplayer, 10)); + faceback = (patch_t*)W_CacheLumpName(namebuf, PU_STATIC); + + // status bar background bits + sbar = (patch_t*)W_CacheLumpName("STBAR", PU_STATIC); + + // face states + facenum = 0; + for (i = 0; i < ST_NUMPAINFACES; i++) + { + for (j = 0; j < ST_NUMSTRAIGHTFACES; j++) + { + //doom_sprintf(namebuf, "STFST%d%d", i, j); + doom_strcpy(namebuf, "STFST"); + doom_concat(namebuf, doom_itoa(i, 10)); + doom_concat(namebuf, doom_itoa(j, 10)); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + } + //doom_sprintf(namebuf, "STFTR%d0", i); // turn right + doom_strcpy(namebuf, "STFTR"); + doom_concat(namebuf, doom_itoa(i, 10)); + doom_concat(namebuf, "0"); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + //doom_sprintf(namebuf, "STFTL%d0", i); // turn left + doom_strcpy(namebuf, "STFTL"); + doom_concat(namebuf, doom_itoa(i, 10)); + doom_concat(namebuf, "0"); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + //doom_sprintf(namebuf, "STFOUCH%d", i); // ouch! + doom_strcpy(namebuf, "STFOUCH"); + doom_concat(namebuf, doom_itoa(i, 10)); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + //doom_sprintf(namebuf, "STFEVL%d", i); // evil grin ;) + doom_strcpy(namebuf, "STFEVL"); + doom_concat(namebuf, doom_itoa(i, 10)); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + //doom_sprintf(namebuf, "STFKILL%d", i); // pissed off + doom_strcpy(namebuf, "STFKILL"); + doom_concat(namebuf, doom_itoa(i, 10)); + faces[facenum++] = W_CacheLumpName(namebuf, PU_STATIC); + } + faces[facenum++] = W_CacheLumpName("STFGOD0", PU_STATIC); + faces[facenum++] = W_CacheLumpName("STFDEAD0", PU_STATIC); +} + +void ST_loadData(void) +{ + lu_palette = W_GetNumForName("PLAYPAL"); + ST_loadGraphics(); +} + +void ST_unloadGraphics(void) +{ + int i; + + // unload the numbers, tall and short + for (i = 0; i < 10; i++) + { + Z_ChangeTag(tallnum[i], PU_CACHE); + Z_ChangeTag(shortnum[i], PU_CACHE); + } + // unload tall percent + Z_ChangeTag(tallpercent, PU_CACHE); + + // unload arms background + Z_ChangeTag(armsbg, PU_CACHE); + + // unload gray #'s + for (i = 0; i < 6; i++) + Z_ChangeTag(arms[i][0], PU_CACHE); + + // unload the key cards + for (i = 0; i < NUMCARDS; i++) + Z_ChangeTag(keys[i], PU_CACHE); + + Z_ChangeTag(sbar, PU_CACHE); + Z_ChangeTag(faceback, PU_CACHE); + + for (i = 0; i < ST_NUMFACES; i++) + Z_ChangeTag(faces[i], PU_CACHE); + + // Note: nobody ain't seen no unloading + // of stminus yet. Dude. +} + +void ST_unloadData(void) +{ + ST_unloadGraphics(); +} + +void ST_initData(void) +{ + int i; + + st_firsttime = true; + plyr = &players[consoleplayer]; + + st_clock = 0; + st_chatstate = StartChatState; + st_gamestate = FirstPersonState; + + st_statusbaron = true; + st_oldchat = st_chat = false; + st_cursoron = false; + + st_faceindex = 0; + st_palette = -1; + + st_oldhealth = -1; + + for (i = 0; i < NUMWEAPONS; i++) + oldweaponsowned[i] = plyr->weaponowned[i]; + + for (i = 0; i < 3; i++) + keyboxes[i] = -1; + + STlib_init(); +} + +void ST_createWidgets(void) +{ + int i; + + // ready weapon ammo + STlib_initNum(&w_ready, + ST_AMMOX, + ST_AMMOY, + tallnum, + &plyr->ammo[weaponinfo[plyr->readyweapon].ammo], + &st_statusbaron, + ST_AMMOWIDTH); + + // the last weapon type + w_ready.data = plyr->readyweapon; + + // health percentage + STlib_initPercent(&w_health, + ST_HEALTHX, + ST_HEALTHY, + tallnum, + &plyr->health, + &st_statusbaron, + tallpercent); + + // arms background + STlib_initBinIcon(&w_armsbg, + ST_ARMSBGX, + ST_ARMSBGY, + armsbg, + &st_notdeathmatch, + &st_statusbaron); + + // weapons owned + for (i = 0; i < 6; i++) + { + STlib_initMultIcon(&w_arms[i], + ST_ARMSX + (i % 3) * ST_ARMSXSPACE, + ST_ARMSY + (i / 3) * ST_ARMSYSPACE, + arms[i], (int*)&plyr->weaponowned[i + 1], + &st_armson); + } + + // frags sum + STlib_initNum(&w_frags, + ST_FRAGSX, + ST_FRAGSY, + tallnum, + &st_fragscount, + &st_fragson, + ST_FRAGSWIDTH); + + // faces + STlib_initMultIcon(&w_faces, + ST_FACESX, + ST_FACESY, + faces, + &st_faceindex, + &st_statusbaron); + + // armor percentage - should be colored later + STlib_initPercent(&w_armor, + ST_ARMORX, + ST_ARMORY, + tallnum, + &plyr->armorpoints, + &st_statusbaron, tallpercent); + + // keyboxes 0-2 + STlib_initMultIcon(&w_keyboxes[0], + ST_KEY0X, + ST_KEY0Y, + keys, + &keyboxes[0], + &st_statusbaron); + + STlib_initMultIcon(&w_keyboxes[1], + ST_KEY1X, + ST_KEY1Y, + keys, + &keyboxes[1], + &st_statusbaron); + + STlib_initMultIcon(&w_keyboxes[2], + ST_KEY2X, + ST_KEY2Y, + keys, + &keyboxes[2], + &st_statusbaron); + + // ammo count (all four kinds) + STlib_initNum(&w_ammo[0], + ST_AMMO0X, + ST_AMMO0Y, + shortnum, + &plyr->ammo[0], + &st_statusbaron, + ST_AMMO0WIDTH); + + STlib_initNum(&w_ammo[1], + ST_AMMO1X, + ST_AMMO1Y, + shortnum, + &plyr->ammo[1], + &st_statusbaron, + ST_AMMO1WIDTH); + + STlib_initNum(&w_ammo[2], + ST_AMMO2X, + ST_AMMO2Y, + shortnum, + &plyr->ammo[2], + &st_statusbaron, + ST_AMMO2WIDTH); + + STlib_initNum(&w_ammo[3], + ST_AMMO3X, + ST_AMMO3Y, + shortnum, + &plyr->ammo[3], + &st_statusbaron, + ST_AMMO3WIDTH); + + // max ammo count (all four kinds) + STlib_initNum(&w_maxammo[0], + ST_MAXAMMO0X, + ST_MAXAMMO0Y, + shortnum, + &plyr->maxammo[0], + &st_statusbaron, + ST_MAXAMMO0WIDTH); + + STlib_initNum(&w_maxammo[1], + ST_MAXAMMO1X, + ST_MAXAMMO1Y, + shortnum, + &plyr->maxammo[1], + &st_statusbaron, + ST_MAXAMMO1WIDTH); + + STlib_initNum(&w_maxammo[2], + ST_MAXAMMO2X, + ST_MAXAMMO2Y, + shortnum, + &plyr->maxammo[2], + &st_statusbaron, + ST_MAXAMMO2WIDTH); + + STlib_initNum(&w_maxammo[3], + ST_MAXAMMO3X, + ST_MAXAMMO3Y, + shortnum, + &plyr->maxammo[3], + &st_statusbaron, + ST_MAXAMMO3WIDTH); +} + +void ST_Start(void) +{ + if (!st_stopped) + ST_Stop(); + + ST_initData(); + ST_createWidgets(); + st_stopped = false; +} + +void ST_Stop(void) +{ + if (st_stopped) + return; + + I_SetPalette(W_CacheLumpNum(lu_palette, PU_CACHE)); + + st_stopped = true; +} + +void ST_Init(void) +{ + veryfirsttime = 0; + ST_loadData(); + screens[4] = (byte*)Z_Malloc(ST_WIDTH * ST_HEIGHT, PU_STATIC, 0); +} diff --git a/src/DOOM/st_stuff.h b/src/DOOM/st_stuff.h new file mode 100644 index 0000000..61cbe9b --- /dev/null +++ b/src/DOOM/st_stuff.h @@ -0,0 +1,82 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Status bar code. +// Does the face/direction indicator animatin. +// Does palette indicators as well (red pain/berserk, bright pickup) +// +//----------------------------------------------------------------------------- + +#ifndef __STSTUFF_H__ +#define __STSTUFF_H__ + +#include "doomtype.h" +#include "d_event.h" + +// Size of statusbar. +// Now sensitive for scaling. +#define ST_HEIGHT (32 * SCREEN_MUL) +#define ST_WIDTH SCREENWIDTH +#define ST_Y (SCREENHEIGHT - ST_HEIGHT) + + +// +// STATUS BAR +// + +// Called by main loop. +doom_boolean ST_Responder(event_t* ev); + +// Called by main loop. +void ST_Ticker(void); + +// Called by main loop. +void ST_Drawer(doom_boolean fullscreen, doom_boolean refresh); + +// Called when the console player is spawned on each level. +void ST_Start(void); + +// Called by startup code. +void ST_Init(void); + + +// States for status bar code. +typedef enum +{ + AutomapState, + FirstPersonState +} st_stateenum_t; + + +// States for the chat code. +typedef enum +{ + StartChatState, + WaitDestState, + GetChatState +} st_chatstateenum_t; + + +doom_boolean ST_Responder(event_t* ev); + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/tables.c b/src/DOOM/tables.c new file mode 100644 index 0000000..1fc8251 --- /dev/null +++ b/src/DOOM/tables.c @@ -0,0 +1,2130 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Lookup tables. +// Do not try to look them up :-). +// In the order of appearance: +// +// int finetangent[4096] - Tangens LUT. +// Should work with BAM fairly well (12 of 16bit, +// effectively, by shifting). +// +// int finesine[10240] - Sine lookup. +// Guess what, serves as cosine, too. +// Remarkable thing is, how to use BAMs with this? +// +// int tantoangle[2049] - ArcTan LUT, +// maps tan(angle) to angle fast. Gotta search. +// +// +//----------------------------------------------------------------------------- + +#include "doom_config.h" + + + + + +#include "tables.h" + + + + +int +SlopeDiv +( unsigned num, + unsigned den) +{ + unsigned ans; + + if (den < 512) + return SLOPERANGE; + + ans = (num<<3)/(den>>8); + + return ans <= SLOPERANGE ? ans : SLOPERANGE; +} + + + + +int finetangent[4096] = +{ + -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 +}; + + +int finesine[10240] = +{ + 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 +}; + + + +angle_t tantoangle[2049] = +{ + 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/DOOM/tables.h b/src/DOOM/tables.h new file mode 100644 index 0000000..2668d93 --- /dev/null +++ b/src/DOOM/tables.h @@ -0,0 +1,92 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Lookup tables. +// Do not try to look them up :-). +// In the order of appearance: +// +// int finetangent[4096] - Tangens LUT. +// Should work with BAM fairly well (12 of 16bit, +// effectively, by shifting). +// +// int finesine[10240] - Sine lookup. +// Guess what, serves as cosine, too. +// Remarkable thing is, how to use BAMs with this? +// +// int tantoangle[2049] - ArcTan LUT, +// maps tan(angle) to angle fast. Gotta search. +// +//----------------------------------------------------------------------------- + +#ifndef __TABLES__ +#define __TABLES__ + + +#ifdef LINUX + +#else +#define PI 3.141592657 +#endif + +#include "m_fixed.h" + +#define FINEANGLES 8192 +#define FINEMASK (FINEANGLES-1) + +// 0x100000000 to 0x2000 +#define ANGLETOFINESHIFT 19 + +// Effective size is 10240. +extern fixed_t finesine[5 * FINEANGLES / 4]; + +// Re-use data, is just PI/2 pahse shift. +extern fixed_t* finecosine; + + +// Effective size is 4096. +extern fixed_t finetangent[FINEANGLES / 2]; + +// Binary Angle Measument, BAM. +#define ANG45 0x20000000 +#define ANG90 0x40000000 +#define ANG180 0x80000000 +#define ANG270 0xc0000000 + +#define SLOPERANGE 2048 +#define SLOPEBITS 11 +#define DBITS (FRACBITS - SLOPEBITS) + +typedef unsigned angle_t; + +// Effective size is 2049; +// The +1 size is to handle the case when x==y +// without additional checking. +extern angle_t tantoangle[SLOPERANGE + 1]; + + +// Utility function, +// called by R_PointToAngle. +int SlopeDiv(unsigned num, unsigned den); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/v_video.c b/src/DOOM/v_video.c new file mode 100644 index 0000000..3ef687d --- /dev/null +++ b/src/DOOM/v_video.c @@ -0,0 +1,462 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Gamma correction LUT stuff. +// Functions to draw patches (by post) directly to screen. +// Functions to blit a block to the screen. +// +//----------------------------------------------------------------------------- + +#include "doom_config.h" + +#include "i_system.h" +#include "r_local.h" +#include "doomdef.h" +#include "doomdata.h" +#include "m_bbox.h" +#include "m_swap.h" +#include "v_video.h" + + +// Each screen is [SCREENWIDTH*SCREENHEIGHT]; +byte* screens[5]; + +int dirtybox[4]; + +// Now where did these came from? +byte gammatable[5][256] = +{ + {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, + 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, + 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, + 65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96, + 97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112, + 113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}, + + {2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,24,25,26,27,29,30,31, + 32,33,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,54,55, + 56,57,58,59,60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76,77, + 78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98, + 99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114, + 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,129, + 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, + 146,147,148,148,149,150,151,152,153,154,155,156,157,158,159,160, + 161,162,163,163,164,165,166,167,168,169,170,171,172,173,174,175, + 175,176,177,178,179,180,181,182,183,184,185,186,186,187,188,189, + 190,191,192,193,194,195,196,196,197,198,199,200,201,202,203,204, + 205,205,206,207,208,209,210,211,212,213,214,214,215,216,217,218, + 219,220,221,222,222,223,224,225,226,227,228,229,230,230,231,232, + 233,234,235,236,237,237,238,239,240,241,242,243,244,245,245,246, + 247,248,249,250,251,252,252,253,254,255}, + + {4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,33,35,36,38,39,40,42, + 43,45,46,47,48,50,51,52,54,55,56,57,59,60,61,62,63,65,66,67,68,69, + 70,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,91,92,93, + 94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112, + 113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128, + 129,130,131,132,133,133,134,135,136,137,138,139,140,141,142,143,144, + 144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159, + 160,160,161,162,163,164,165,166,166,167,168,169,170,171,172,172,173, + 174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,187,188, + 188,189,190,191,192,193,193,194,195,196,197,197,198,199,200,201,201, + 202,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215, + 216,217,217,218,219,220,221,221,222,223,224,224,225,226,227,228,228, + 229,230,231,231,232,233,234,235,235,236,237,238,238,239,240,241,241, + 242,243,244,244,245,246,247,247,248,249,250,251,251,252,253,254,254, + 255}, + + {8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,47,49,50,52,53,55, + 57,58,60,61,63,64,65,67,68,70,71,72,74,75,76,77,79,80,81,82,84,85, + 86,87,88,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107, + 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124, + 125,126,127,128,129,130,131,132,133,134,135,135,136,137,138,139,140, + 141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155, + 155,156,157,158,159,160,160,161,162,163,164,165,165,166,167,168,169, + 169,170,171,172,173,173,174,175,176,176,177,178,179,180,180,181,182, + 183,183,184,185,186,186,187,188,189,189,190,191,192,192,193,194,195, + 195,196,197,197,198,199,200,200,201,202,202,203,204,205,205,206,207, + 207,208,209,210,210,211,212,212,213,214,214,215,216,216,217,218,219, + 219,220,221,221,222,223,223,224,225,225,226,227,227,228,229,229,230, + 231,231,232,233,233,234,235,235,236,237,237,238,238,239,240,240,241, + 242,242,243,244,244,245,246,246,247,247,248,249,249,250,251,251,252, + 253,253,254,254,255}, + + {16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,66,68,69,71,73,75,76, + 78,80,81,83,84,86,87,89,90,92,93,94,96,97,98,100,101,102,103,105,106, + 107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124, + 125,126,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141, + 142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155, + 156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169, + 169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,181, + 182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193, + 193,194,195,195,196,196,197,198,198,199,200,200,201,202,202,203,203, + 204,205,205,206,207,207,208,208,209,210,210,211,211,212,213,213,214, + 214,215,216,216,217,217,218,219,219,220,220,221,221,222,223,223,224, + 224,225,225,226,227,227,228,228,229,229,230,230,231,232,232,233,233, + 234,234,235,235,236,236,237,237,238,239,239,240,240,241,241,242,242, + 243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251, + 251,252,252,253,254,254,255,255} +}; + +int usegamma; + + +// +// V_MarkRect +// +void V_MarkRect(int x, int y, int width, int height) +{ + M_AddToBox(dirtybox, x, y); + M_AddToBox(dirtybox, x + width - 1, y + height - 1); +} + + +// +// V_CopyRect +// +void V_CopyRect(int srcx, + int srcy, + int srcscrn, + int width, + int height, + int destx, + int desty, + int destscrn) +{ + byte* src; + byte* dest; + +#ifdef RANGECHECK + if (srcx<0 + || srcx + width >SCREENWIDTH + || srcy<0 + || srcy + height>SCREENHEIGHT + || destx<0 || destx + width >SCREENWIDTH + || desty<0 + || desty + height>SCREENHEIGHT + || (unsigned)srcscrn > 4 + || (unsigned)destscrn > 4) + { + I_Error("Error: Bad V_CopyRect"); + } +#endif + V_MarkRect(destx, desty, width, height); + + src = screens[srcscrn] + SCREENWIDTH * srcy + srcx; + dest = screens[destscrn] + SCREENWIDTH * desty + destx; + + for (; height > 0; height--) + { + doom_memcpy(dest, src, width); + src += SCREENWIDTH; + dest += SCREENWIDTH; + } +} + + +// +// V_DrawPatch +// Masks a column based masked pic to the screen. +// +void V_DrawPatch(int x, int y, int scrn, patch_t* patch) +{ + int count; + int col; + column_t* column; + byte* desttop; + byte* dest; + byte* source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); +#ifdef RANGECHECK + if (x<0 + || x + SHORT(patch->width) >SCREENWIDTH + || y<0 + || y + SHORT(patch->height)>SCREENHEIGHT + || (unsigned)scrn > 4) + { + //doom_print("Patch at %d,%d exceeds LFB\n", x, y); + doom_print("Patch at "); + doom_print(doom_itoa(x, 10)); + doom_print(","); + doom_print(doom_itoa(y, 10)); + doom_print(" exceeds LFB\n"); + // No I_Error abort - what is up with TNT.WAD? + doom_print("V_DrawPatch: bad patch (ignored)\n"); + return; + } +#endif + + if (!scrn) + V_MarkRect(x, y, SHORT(patch->width), SHORT(patch->height)); + + col = 0; + desttop = screens[scrn] + y * SCREENWIDTH + x; + + w = SHORT(patch->width); + + for (; col < w; x++, col++, desttop++) + { + column = (column_t*)((byte*)patch + LONG(patch->columnofs[col])); + + // step through the posts in a column + while (column->topdelta != 0xff) + { + source = (byte*)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + column = (column_t*)((byte*)column + column->length + + 4); + } + } +} + + +// +// V_DrawPatchFlipped +// Masks a column based masked pic to the screen. +// Flips horizontally, e.g. to mirror face. +// +void V_DrawPatchFlipped(int x, int y, int scrn, patch_t* patch) +{ + int count; + int col; + column_t* column; + byte* desttop; + byte* dest; + byte* source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); +#ifdef RANGECHECK + if (x<0 + || x + SHORT(patch->width) >SCREENWIDTH + || y<0 + || y + SHORT(patch->height)>SCREENHEIGHT + || (unsigned)scrn > 4) + { + //doom_print("Patch origin %d,%d exceeds LFB\n", x, y); + doom_print("Patch origin "); + doom_print(doom_itoa(x, 10)); + doom_print(","); + doom_print(doom_itoa(y, 10)); + doom_print(" exceeds LFB\n"); + I_Error("Error: Bad V_DrawPatch in V_DrawPatchFlipped"); + } +#endif + + if (!scrn) + V_MarkRect(x, y, SHORT(patch->width), SHORT(patch->height)); + + col = 0; + desttop = screens[scrn] + y * SCREENWIDTH + x; + + w = SHORT(patch->width); + + for (; col < w; x++, col++, desttop++) + { + column = (column_t*)((byte*)patch + LONG(patch->columnofs[w - 1 - col])); + + // step through the posts in a column + while (column->topdelta != 0xff) + { + source = (byte*)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + column = (column_t*)((byte*)column + column->length + + 4); + } + } +} + + +void V_DrawPatchRectDirect(int x, int y, int scrn, patch_t* patch, int src_x, int src_w) +{ + int count; + int col; + column_t* column; + byte* desttop; + byte* dest; + byte* source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); +#ifdef RANGECHECK + if (x<0 + || x + SHORT(src_w) >SCREENWIDTH + || y<0 + || y + SHORT(patch->height)>SCREENHEIGHT + || (unsigned)scrn > 4) + { + //doom_print("Patch at %d,%d exceeds LFB\n", x, y); + doom_print("Patch at "); + doom_print(doom_itoa(x, 10)); + doom_print(","); + doom_print(doom_itoa(y, 10)); + doom_print(" exceeds LFB\n"); + // No I_Error abort - what is up with TNT.WAD? + doom_print("V_DrawPatch: bad patch (ignored)\n"); + return; + } +#endif + + if (!scrn) + V_MarkRect(x, y, SHORT(src_w), SHORT(patch->height)); + + col = 0; + desttop = screens[scrn] + y * SCREENWIDTH + x; + + w = SHORT(src_w); + + for (; col < w; x++, col++, desttop++) + { + column = (column_t*)((byte*)patch + LONG(patch->columnofs[col + src_x])); + + // step through the posts in a column + while (column->topdelta != 0xff) + { + source = (byte*)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + column = (column_t*)((byte*)column + column->length + + 4); + } + } +} + + +// +// V_DrawPatchDirect +// Draws directly to the screen on the pc. +// +void V_DrawPatchDirect(int x, int y, int scrn, patch_t* patch) +{ + V_DrawPatch(x, y, scrn, patch); +} + + +// +// V_DrawBlock +// Draw a linear block of pixels into the view buffer. +// +void V_DrawBlock(int x, int y, int scrn, int width, int height, byte* src) +{ + byte* dest; + +#ifdef RANGECHECK + if (x<0 + || x + width >SCREENWIDTH + || y<0 + || y + height>SCREENHEIGHT + || (unsigned)scrn > 4) + { + I_Error("Error: Bad V_DrawBlock"); + } +#endif + + V_MarkRect(x, y, width, height); + + dest = screens[scrn] + y * SCREENWIDTH + x; + + while (height--) + { + doom_memcpy(dest, src, width); + src += width; + dest += SCREENWIDTH; + } +} + + +// +// V_GetBlock +// Gets a linear block of pixels from the view buffer. +// +void V_GetBlock(int x, int y, int scrn, int width, int height, byte* dest) +{ + byte* src; + +#ifdef RANGECHECK + if (x<0 + || x + width >SCREENWIDTH + || y<0 + || y + height>SCREENHEIGHT + || (unsigned)scrn > 4) + { + I_Error("Error: Bad V_DrawBlock"); + } +#endif + + src = screens[scrn] + y * SCREENWIDTH + x; + + while (height--) + { + doom_memcpy(dest, src, width); + src += SCREENWIDTH; + dest += width; + } +} + + +// +// V_Init +// +void V_Init(void) +{ + int i; + byte* base; + + // stick these in low dos memory on PCs + + base = I_AllocLow(SCREENWIDTH * SCREENHEIGHT * 4); + + for (i = 0; i < 4; i++) + screens[i] = base + i * SCREENWIDTH * SCREENHEIGHT; +} diff --git a/src/DOOM/v_video.h b/src/DOOM/v_video.h new file mode 100644 index 0000000..bd0183b --- /dev/null +++ b/src/DOOM/v_video.h @@ -0,0 +1,97 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Gamma correction LUT. +// Functions to draw patches (by post) directly to screen. +// Functions to blit a block to the screen. +// +//----------------------------------------------------------------------------- + +#ifndef __V_VIDEO__ +#define __V_VIDEO__ + +#include "doomtype.h" +#include "doomdef.h" +#include "r_data.h" // Needed because we are refering to patches. + +// +// VIDEO +// + +#define CENTERY (SCREENHEIGHT / 2) + + +// Screen 0 is the screen updated by I_Update screen. +// Screen 1 is an extra buffer. +extern byte* screens[5]; +extern int dirtybox[4]; +extern byte gammatable[5][256]; +extern int usegamma; + + +// Allocates buffer screens, call before R_Init. +void V_Init(void); + +void V_CopyRect(int srcx, + int srcy, + int srcscrn, + int width, + int height, + int destx, + int desty, + int destscrn); + +void V_DrawPatch(int x, + int y, + int scrn, + patch_t* patch); + +void V_DrawPatchDirect(int x, + int y, + int scrn, + patch_t* patch); + +void V_DrawPatchRectDirect(int x, int y, int scrn, patch_t* patch, int src_x, int src_w); + +// Draw a linear block of pixels into the view buffer. +void V_DrawBlock(int x, + int y, + int scrn, + int width, + int height, + byte* src); + +// Reads a linear block of pixels into the view buffer. +void V_GetBlock(int x, + int y, + int scrn, + int width, + int height, + byte* dest); + +void V_MarkRect(int x, + int y, + int width, + int height); + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/w_wad.c b/src/DOOM/w_wad.c new file mode 100644 index 0000000..6122055 --- /dev/null +++ b/src/DOOM/w_wad.c @@ -0,0 +1,597 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Handles WAD file header, directory, lump I/O. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "doomtype.h" +#include "m_swap.h" +#include "i_system.h" +#include "z_zone.h" +#include "w_wad.h" + + +// #define strcmpi strcasecmp + + +// +// GLOBALS +// + +// Location of each lump on disk. +lumpinfo_t* lumpinfo; +int numlumps; +void** lumpcache; +int reloadlump; +char* reloadname; +int info[2500][10]; +int profilecount; + + +void doom_strupr(char* s) +{ + while (*s) + { + *s = doom_toupper(*s); s++; + } +} + + +void ExtractFileBase(char* path, char* dest) +{ + char* src; + int length; + + src = path + doom_strlen(path) - 1; + + // back up until a \ or the start + while (src != path + && *(src - 1) != '\\' + && *(src - 1) != '/') + { + src--; + } + + // copy up to eight characters + doom_memset(dest, 0, 8); + length = 0; + + while (*src && *src != '.') + { + if (++length == 9) + { + //I_Error("Error: Filename base of %s >8 chars", path); + doom_strcpy(error_buf, "Error: Filename base of "); + doom_concat(error_buf, path); + doom_concat(error_buf, " >8 chars"); + I_Error(error_buf); + } + + *dest++ = doom_toupper((int)*src++); + } +} + + +// +// LUMP BASED ROUTINES. +// + +// +// W_AddFile +// All files are optional, but at least one file must be +// found (PWAD, if all required lumps are present). +// Files with a .wad extension are wadlink files +// with multiple lumps. +// Other files are single lumps with the base filename +// for the lump name. +// +// If filename starts with a tilde, the file is handled +// specially to allow map reloads. +// But: the reload feature is a fragile hack... +void W_AddFile(char* filename) +{ + wadinfo_t header; + lumpinfo_t* lump_p; + unsigned i; + void* handle; + int length; + int startlump; + filelump_t* fileinfo; + filelump_t singleinfo; + void* storehandle; + void* allocated = 0; + + // open the file and add to directory + + // handle reload indicator. + if (filename[0] == '~') + { + filename++; + reloadname = filename; + reloadlump = numlumps; + } + + if ((handle = doom_open(filename, "rb")) == 0) + { + //doom_print(" couldn't open %s\n", filename); + doom_print(" couldn't open "); + doom_print(filename); + doom_print("\n"); + return; + } + + //doom_print(" adding %s\n", filename); + doom_print(" adding "); + doom_print(filename); + doom_print("\n"); + startlump = numlumps; + + if (doom_strcasecmp(filename + doom_strlen(filename) - 3, "wad")) + { + // single lump file + fileinfo = &singleinfo; + singleinfo.filepos = 0; + //singleinfo.size = LONG(filelength(handle)); + doom_seek(handle, 0, DOOM_SEEK_END); + singleinfo.size = doom_tell(handle); + doom_seek(handle, 0, DOOM_SEEK_SET); + ExtractFileBase(filename, singleinfo.name); + numlumps++; + } + else + { + // WAD file + doom_read(handle, &header, sizeof(header)); + if (doom_strncmp(header.identification, "IWAD", 4)) + { + // Homebrew levels? + if (doom_strncmp(header.identification, "PWAD", 4)) + { + //I_Error("Error: Wad file %s doesn't have IWAD " + // "or PWAD id\n", filename); + doom_strcpy(error_buf, "Error: Wad file "); + doom_concat(error_buf, filename); + doom_concat(error_buf, " doesn't have IWAD or PWAD id\n"); + I_Error(error_buf); + } + + // ???modifiedgame = true; + } + header.numlumps = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + length = header.numlumps * sizeof(filelump_t); + fileinfo = allocated = doom_malloc(length); + doom_seek(handle, header.infotableofs, DOOM_SEEK_SET); + doom_read(handle, fileinfo, length); + numlumps += header.numlumps; + } + + + // Fill in lumpinfo + static int previous_realloc_size = 1; + void* new_lumpinfo = doom_malloc(numlumps * sizeof(lumpinfo_t)); + doom_memcpy(new_lumpinfo, lumpinfo, previous_realloc_size); + previous_realloc_size = numlumps * sizeof(lumpinfo_t); + lumpinfo = new_lumpinfo; + + if (!lumpinfo) + I_Error("Error: Couldn't realloc lumpinfo"); + + lump_p = &lumpinfo[startlump]; + + storehandle = reloadname ? 0 : handle; + + for (i = startlump; i < (unsigned)numlumps; i++, lump_p++, fileinfo++) + { + lump_p->handle = storehandle; + lump_p->position = LONG(fileinfo->filepos); + lump_p->size = LONG(fileinfo->size); + doom_strncpy(lump_p->name, fileinfo->name, 8); + } + + if (reloadname) + doom_close(handle); + + if (allocated) doom_free(allocated); +} + + +// +// W_Reload +// Flushes any of the reloadable lumps in memory +// and reloads the directory. +// +void W_Reload(void) +{ + wadinfo_t header; + int lumpcount; + lumpinfo_t* lump_p; + unsigned i; + void* handle; + int length; + filelump_t* fileinfo; + + if (!reloadname) + return; + + if ((handle = doom_open(reloadname, "rb")) == 0) + { + //I_Error("Error: W_Reload: couldn't open %s", reloadname); + doom_strcpy(error_buf, "Error: W_Reload: couldn't open "); + doom_concat(error_buf, reloadname); + I_Error(error_buf); + } + + doom_read(handle, &header, sizeof(header)); + lumpcount = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + length = lumpcount * sizeof(filelump_t); + fileinfo = doom_malloc(length); + doom_seek(handle, header.infotableofs, DOOM_SEEK_SET); + doom_read(handle, fileinfo, length); + + // Fill in lumpinfo + lump_p = &lumpinfo[reloadlump]; + + for (i = reloadlump; + i < (unsigned)(reloadlump + lumpcount); + i++, lump_p++, fileinfo++) + { + if (lumpcache[i]) + Z_Free(lumpcache[i]); + + lump_p->position = LONG(fileinfo->filepos); + lump_p->size = LONG(fileinfo->size); + } + + doom_close(handle); + + doom_free(fileinfo); +} + + +// +// W_InitMultipleFiles +// Pass a null terminated list of files to use. +// All files are optional, but at least one file +// must be found. +// Files with a .wad extension are idlink files +// with multiple lumps. +// Other files are single lumps with the base filename +// for the lump name. +// Lump names can appear multiple times. +// The name searcher looks backwards, so a later file +// does override all earlier ones. +// +void W_InitMultipleFiles(char** filenames) +{ + int size; + + // open all the files, load headers, and count lumps + numlumps = 0; + + // will be realloced as lumps are added + lumpinfo = doom_malloc(1); + + for (; *filenames; filenames++) + W_AddFile(*filenames); + + if (!numlumps) + I_Error("Error: W_InitFiles: no files found"); + + // set up caching + size = numlumps * sizeof(*lumpcache); + lumpcache = doom_malloc(size); + + if (!lumpcache) + I_Error("Error: Couldn't allocate lumpcache"); + + doom_memset(lumpcache, 0, size); +} + + +// +// W_InitFile +// Just initialize from a single file. +// +void W_InitFile(char* filename) +{ + char* names[2]; + + names[0] = filename; + names[1] = 0; + W_InitMultipleFiles(names); +} + + +// +// W_NumLumps +// +int W_NumLumps(void) +{ + return numlumps; +} + + +// +// W_CheckNumForName +// Returns -1 if name not found. +// +int W_CheckNumForName(char* name) +{ + union + { + char s[9]; + int x[2]; + } name8; + + int v1; + int v2; + lumpinfo_t* lump_p; + + // make the name into two integers for easy compares + doom_strncpy(name8.s, name, 8); + + // in case the name was a fill 8 chars + name8.s[8] = 0; + + // case insensitive + doom_strupr(name8.s); + + v1 = name8.x[0]; + v2 = name8.x[1]; + + + // scan backwards so patch lump files take precedence + lump_p = lumpinfo + numlumps; + + while (lump_p-- != lumpinfo) + { + if (*(int*)lump_p->name == v1 + && *(int*)&lump_p->name[4] == v2) + { + return (int)(lump_p - lumpinfo); + } + } + + // TFB. Not found. + return -1; +} + + +// +// W_GetNumForName +// Calls W_CheckNumForName, but bombs out if not found. +// +int W_GetNumForName(char* name) +{ + int i; + + i = W_CheckNumForName(name); + + if (i == -1) + { + if (doom_strcmp(name, "HELP2") == 0) + { + name = "HELP1"; // Ultimate Doom EXE was modified to use this instead + i = W_CheckNumForName(name); + } + if (i == -1) + { + //I_Error("Error: W_GetNumForName, %s not found!", name); + doom_strcpy(error_buf, "Error: W_GetNumForName, "); + doom_concat(error_buf, name); + doom_concat(error_buf, " not found!"); + I_Error(error_buf); + } + } + + return i; +} + + +// +// W_LumpLength +// Returns the buffer size needed to load the given lump. +// +int W_LumpLength(int lump) +{ + if (lump >= numlumps) + { + //I_Error("Error: W_LumpLength: %i >= numlumps", lump); + doom_strcpy(error_buf, "Error: W_LumpLength: "); + doom_concat(error_buf, doom_itoa(lump, 10)); + doom_concat(error_buf, " >= numlumps"); + I_Error(error_buf); + } + + return lumpinfo[lump].size; +} + + +// +// W_ReadLump +// Loads the lump into the given buffer, +// which must be >= W_LumpLength(). +// +void W_ReadLump(int lump, void* dest) +{ + int c; + lumpinfo_t* l; + void* handle; + + if (lump >= numlumps) + { + //I_Error("Error: W_ReadLump: %i >= numlumps", lump); + doom_strcpy(error_buf, "Error: W_ReadLump: "); + doom_concat(error_buf, doom_itoa(lump, 10)); + doom_concat(error_buf, " >= numlump"); + I_Error(error_buf); + } + + l = lumpinfo + lump; + + // ??? I_BeginRead (); + + if (l->handle == 0) + { + // reloadable file, so use open / read / close + if ((handle = doom_open(reloadname, "rb")) == 0) + { + //I_Error("Error: W_ReadLump: couldn't open %s", reloadname); + doom_strcpy(error_buf, "Error: W_ReadLump: couldn't open "); + doom_concat(error_buf, reloadname); + I_Error(error_buf); + } + } + else + handle = l->handle; + + doom_seek(handle, l->position, DOOM_SEEK_SET); + c = doom_read(handle, dest, l->size); + + if (c < l->size) + { + //I_Error("Error: W_ReadLump: only read %i of %i on lump %i", + // c, l->size, lump); + doom_strcpy(error_buf, "Error: W_ReadLump: only read "); + doom_concat(error_buf, doom_itoa(c, 10)); + doom_concat(error_buf, " of "); + doom_concat(error_buf, doom_itoa(l->size, 10)); + doom_concat(error_buf, " on lump "); + doom_concat(error_buf, doom_itoa(lump, 10)); + I_Error(error_buf); + } + + if (l->handle == 0) + doom_close(handle); + + // ??? I_EndRead (); +} + + +// +// W_CacheLumpNum +// +void* W_CacheLumpNum(int lump, int tag) +{ + byte* ptr; + + if ((unsigned)lump >= (unsigned)numlumps) + { + //I_Error("Error: W_CacheLumpNum: %i >= numlumps", lump); + doom_strcpy(error_buf, "Error: W_CacheLumpNum: "); + doom_concat(error_buf, doom_itoa(lump, 10)); + doom_concat(error_buf, " >= numlumps"); + I_Error(error_buf); + } + + if (!lumpcache[lump]) + { + // read the lump in + + //doom_print ("cache miss on lump %i\n",lump); + ptr = Z_Malloc(W_LumpLength(lump), tag, &lumpcache[lump]); + W_ReadLump(lump, lumpcache[lump]); + } + else + { + //doom_print ("cache hit on lump %i\n",lump); + Z_ChangeTag(lumpcache[lump], tag); + } + + return lumpcache[lump]; +} + + +// +// W_CacheLumpName +// +void* W_CacheLumpName(char* name, int tag) +{ + return W_CacheLumpNum(W_GetNumForName(name), tag); +} + + +// +// W_Profile +// +void W_Profile(void) +{ + int i; + memblock_t* block; + void* ptr; + char ch; + void* f; + int j; + char name[9]; + + for (i = 0; i < numlumps; i++) + { + ptr = lumpcache[i]; + if (!ptr) + { + ch = ' '; + continue; + } + else + { + block = (memblock_t*)((byte*)ptr - sizeof(memblock_t)); + if (block->tag < PU_PURGELEVEL) + ch = 'S'; + else + ch = 'P'; + } + info[i][profilecount] = ch; + } + profilecount++; + + f = doom_open("waddump.txt", "w"); + name[8] = 0; + + for (i = 0; i < numlumps; i++) + { + doom_memcpy(name, lumpinfo[i].name, 8); + + for (j = 0; j < 8; j++) + if (!name[j]) + break; + + for (; j < 8; j++) + name[j] = ' '; + + doom_fprint(f, name); + doom_fprint(f, " "); + + for (j = 0; j < profilecount; j++) + { + //fprintf(f, " %c", info[i][j]); + doom_fprint(f, " "); + doom_fprint(f, doom_ctoa(info[i][j])); + } + + doom_fprint(f, "\n"); + } + + doom_close(f); +} diff --git a/src/DOOM/w_wad.h b/src/DOOM/w_wad.h new file mode 100644 index 0000000..9b086cb --- /dev/null +++ b/src/DOOM/w_wad.h @@ -0,0 +1,81 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// WAD I/O functions. +// +//----------------------------------------------------------------------------- + +#ifndef __W_WAD__ +#define __W_WAD__ + + +// +// TYPES +// +typedef struct +{ + // Should be "IWAD" or "PWAD". + char identification[4]; + int numlumps; + int infotableofs; +} wadinfo_t; + + +typedef struct +{ + int filepos; + int size; + char name[8]; +} filelump_t; + + +// +// WADFILE I/O related stuff. +// +typedef struct +{ + char name[8]; + void* handle; + int position; + int size; +} lumpinfo_t; + + +extern void** lumpcache; +extern lumpinfo_t* lumpinfo; +extern int numlumps; + +void W_InitMultipleFiles(char** filenames); +void W_Reload(void); + +int W_CheckNumForName(char* name); +int W_GetNumForName(char* name); + +int W_LumpLength(int lump); +void W_ReadLump(int lump, void* dest); + +void* W_CacheLumpNum(int lump, int tag); +void* W_CacheLumpName(char* name, int tag); + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/wi_stuff.c b/src/DOOM/wi_stuff.c new file mode 100644 index 0000000..e9752ab --- /dev/null +++ b/src/DOOM/wi_stuff.c @@ -0,0 +1,1797 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Intermission screens. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + + + +#include "z_zone.h" +#include "m_random.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "g_game.h" +#include "r_local.h" +#include "s_sound.h" +#include "doomstat.h" +#include "sounds.h" // Data. +#include "v_video.h" // Needs access to LFB. +#include "wi_stuff.h" + + +// +// Data needed to add patches to full screen intermission pics. +// Patches are statistics messages, and animations. +// Loads of by-pixel layout and placement, offsets etc. +// + + +// +// Different vetween registered DOOM (1994) and +// Ultimate DOOM - Final edition (retail, 1995?). +// This is supposedly ignored for commercial +// release (aka DOOM II), which had 34 maps +// in one episode. So there. +#define NUMEPISODES 4 +#define NUMMAPS 9 + +// GLOBAL LOCATIONS +#define WI_TITLEY 2 +#define WI_SPACINGY 33 + +// SINGPLE-PLAYER STUFF +#define SP_STATSX 50 +#define SP_STATSY 50 + +#define SP_TIMEX 16 +#define SP_TIMEY (SCREENHEIGHT-32) + +// NET GAME STUFF +#define NG_STATSY 50 +#define NG_STATSX (32 + SHORT(star->width)/2 + 32*!dofrags) + +#define NG_SPACINGX 64 + +// DEATHMATCH STUFF +#define DM_MATRIXX 42 +#define DM_MATRIXY 68 + +#define DM_SPACINGX 40 + +#define DM_TOTALSX 269 + +#define DM_KILLERSX 10 +#define DM_KILLERSY 100 +#define DM_VICTIMSX 5 +#define DM_VICTIMSY 50 + +#define FB 0 + +// States for single-player +#define SP_KILLS 0 +#define SP_ITEMS 2 +#define SP_SECRET 4 +#define SP_FRAGS 6 +#define SP_TIME 8 +#define SP_PAR ST_TIME + +#define SP_PAUSE 1 + +// in seconds +#define SHOWNEXTLOCDELAY 4 +//#define SHOWLASTLOCDELAY SHOWNEXTLOCDELAY + + +typedef enum +{ + ANIM_ALWAYS, + ANIM_RANDOM, + ANIM_LEVEL +} animenum_t; + + +typedef struct +{ + int x; + int y; +} point_t; + + +// +// Animation. +// There is another anim_t used in p_spec. +// +typedef struct +{ + animenum_t type; + + // period in tics between animations + int period; + + // number of animation frames + int nanims; + + // location of animation + point_t loc; + + // ALWAYS: n/a, + // RANDOM: period deviation (<256), + // LEVEL: level + int data1; + + // ALWAYS: n/a, + // RANDOM: random base period, + // LEVEL: n/a + int data2; + + // actual graphics for frames of animations + patch_t* p[3]; + + // following must be initialized to zero before use! + + // next value of bcnt (used in conjunction with period) + int nexttic; + + // last drawn animation frame + int lastdrawn; + + // next frame number to animate + int ctr; + + // used by RANDOM and LEVEL when animating + int state; +} anim_t_wi_stuff; + + +static point_t lnodes[NUMEPISODES][NUMMAPS] = +{ + // Episode 0 World Map + { + { 185, 164 }, // location of level 0 (CJ) + { 148, 143 }, // location of level 1 (CJ) + { 69, 122 }, // location of level 2 (CJ) + { 209, 102 }, // location of level 3 (CJ) + { 116, 89 }, // location of level 4 (CJ) + { 166, 55 }, // location of level 5 (CJ) + { 71, 56 }, // location of level 6 (CJ) + { 135, 29 }, // location of level 7 (CJ) + { 71, 24 } // location of level 8 (CJ) + }, + + // Episode 1 World Map should go here + { + { 254, 25 }, // location of level 0 (CJ) + { 97, 50 }, // location of level 1 (CJ) + { 188, 64 }, // location of level 2 (CJ) + { 128, 78 }, // location of level 3 (CJ) + { 214, 92 }, // location of level 4 (CJ) + { 133, 130 }, // location of level 5 (CJ) + { 208, 136 }, // location of level 6 (CJ) + { 148, 140 }, // location of level 7 (CJ) + { 235, 158 } // location of level 8 (CJ) + }, + + // Episode 2 World Map should go here + { + { 156, 168 }, // location of level 0 (CJ) + { 48, 154 }, // location of level 1 (CJ) + { 174, 95 }, // location of level 2 (CJ) + { 265, 75 }, // location of level 3 (CJ) + { 130, 48 }, // location of level 4 (CJ) + { 279, 23 }, // location of level 5 (CJ) + { 198, 48 }, // location of level 6 (CJ) + { 140, 25 }, // location of level 7 (CJ) + { 281, 136 } // location of level 8 (CJ) + } +}; + + +// +// Animation locations for episode 0 (1). +// Using patches saves a lot of space, +// as they replace 320x200 full screen frames. +// +static anim_t_wi_stuff epsd0animinfo[] = +{ + { ANIM_ALWAYS, TICRATE / 3, 3, { 224, 104 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 184, 160 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 112, 136 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 72, 112 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 88, 96 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 64, 48 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 192, 40 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 136, 16 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 80, 16 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 64, 24 } } +}; + +static anim_t_wi_stuff epsd1animinfo[] = +{ + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 1 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 2 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 3 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 4 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 5 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 6 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 7 }, + { ANIM_LEVEL, TICRATE / 3, 3, { 192, 144 }, 8 }, + { ANIM_LEVEL, TICRATE / 3, 1, { 128, 136 }, 8 } +}; + +static anim_t_wi_stuff epsd2animinfo[] = +{ + { ANIM_ALWAYS, TICRATE / 3, 3, { 104, 168 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 40, 136 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 160, 96 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 104, 80 } }, + { ANIM_ALWAYS, TICRATE / 3, 3, { 120, 32 } }, + { ANIM_ALWAYS, TICRATE / 4, 3, { 40, 0 } } +}; + +static int NUMANIMS[NUMEPISODES] = +{ + sizeof(epsd0animinfo) / sizeof(anim_t_wi_stuff), + sizeof(epsd1animinfo) / sizeof(anim_t_wi_stuff), + sizeof(epsd2animinfo) / sizeof(anim_t_wi_stuff) +}; + +static anim_t_wi_stuff* anims_wi_stuff[NUMEPISODES] = +{ + epsd0animinfo, + epsd1animinfo, + epsd2animinfo +}; + + +// +// GENERAL DATA +// + +// +// Locally used stuff. +// + +// used to accelerate or skip a stage +static int acceleratestage; + +// wbs->pnum +static int me; + +// specifies current state +static stateenum_t state; + +// contains information passed into intermission +static wbstartstruct_t* wbs; + +static wbplayerstruct_t* plrs; // wbs->plyr[] + +// used for general timing +static int cnt; + +// used for timing of background animation +static int bcnt; + +// signals to refresh everything for one frame +static int firstrefresh; + +static int cnt_kills[MAXPLAYERS]; +static int cnt_items[MAXPLAYERS]; +static int cnt_secret[MAXPLAYERS]; +static int cnt_time; +static int cnt_par; +static int cnt_pause; + +// # of commercial levels +static int NUMCMAPS; + + +// +// GRAPHICS +// + +// background (map of levels). +static patch_t* bg; + +// You Are Here graphic +static patch_t* yah[2]; + +// splat +static patch_t* splat; + +// %, : graphics +static patch_t* percent; +static patch_t* colon; + +// 0-9 graphic +static patch_t* num[10]; + +// minus sign +static patch_t* wiminus; + +// "Finished!" graphics +static patch_t* finished; + +// "Entering" graphic +static patch_t* entering; + +// "secret" +static patch_t* sp_secret; + +// "Kills", "Scrt", "Items", "Frags" +static patch_t* kills; +static patch_t* secret; +static patch_t* items; +static patch_t* frags; + +// Time sucks. +static patch_t* time_patch; +static patch_t* par; +static patch_t* sucks; + +// "killers", "victims" +static patch_t* killers; +static patch_t* victims; + +// "Total", your face, your dead face +static patch_t* total; +static patch_t* star; +static patch_t* bstar; + +// "red P[1..MAXPLAYERS]" +static patch_t* p[MAXPLAYERS]; + +// "gray P[1..MAXPLAYERS]" +static patch_t* bp[MAXPLAYERS]; + +// Name graphics of each level (centered) +static patch_t** lnames; + +static doom_boolean snl_pointeron = false; +static int dm_state; +static int dm_frags[MAXPLAYERS][MAXPLAYERS]; +static int dm_totals[MAXPLAYERS]; +static int cnt_frags[MAXPLAYERS]; +static int dofrags; +static int ng_state; +static int sp_state; + + +// +// CODE +// + +void WI_slamBackground(void) +{ + doom_memcpy(screens[0], screens[1], SCREENWIDTH * SCREENHEIGHT); + V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT); +} + + +// The ticker is used to detect keys +// because of timing issues in netgames. +doom_boolean WI_Responder(event_t* ev) +{ + return false; +} + + +// Draws " Finished!" +void WI_drawLF(void) +{ + int y = WI_TITLEY; + + // draw + V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->last]->width)) / 2, + y, FB, lnames[wbs->last]); + + // draw "Finished!" + y += (5 * SHORT(lnames[wbs->last]->height)) / 4; + + V_DrawPatch((SCREENWIDTH - SHORT(finished->width)) / 2, + y, FB, finished); +} + + +// Draws "Entering " +void WI_drawEL(void) +{ + int y = WI_TITLEY; + + // draw "Entering" + V_DrawPatch((SCREENWIDTH - SHORT(entering->width)) / 2, + y, FB, entering); + + // draw level + y += (5 * SHORT(lnames[wbs->next]->height)) / 4; + + V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->next]->width)) / 2, + y, FB, lnames[wbs->next]); +} + + +void WI_drawOnLnode(int n, patch_t* c[]) +{ + int i; + int left; + int top; + int right; + int bottom; + doom_boolean fits = false; + + i = 0; + do + { + left = lnodes[wbs->epsd][n].x - SHORT(c[i]->leftoffset); + top = lnodes[wbs->epsd][n].y - SHORT(c[i]->topoffset); + right = left + SHORT(c[i]->width); + bottom = top + SHORT(c[i]->height); + + if (left >= 0 + && right < SCREENWIDTH + && top >= 0 + && bottom < SCREENHEIGHT) + { + fits = true; + } + else + { + i++; + } + } while (!fits && i != 2); + + if (fits && i < 2) + { + V_DrawPatch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y, + FB, c[i]); + } + else + { + // DEBUG + //doom_print("Could not place patch on level %d", n + 1); + doom_print("Could not place patch on level "); + doom_print(doom_itoa(n + 1, 10)); + } +} + + +void WI_initAnimatedBack(void) +{ + int i; + anim_t_wi_stuff* a; + + if (gamemode == commercial) + return; + + if (wbs->epsd > 2) + return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims_wi_stuff[wbs->epsd][i]; + + // init variables + a->ctr = -1; + + // specify the next time to draw it + if (a->type == ANIM_ALWAYS) + a->nexttic = bcnt + 1 + (M_Random() % a->period); + else if (a->type == ANIM_RANDOM) + a->nexttic = bcnt + 1 + a->data2 + (M_Random() % a->data1); + else if (a->type == ANIM_LEVEL) + a->nexttic = bcnt + 1; + } +} + + +void WI_updateAnimatedBack(void) +{ + int i; + anim_t_wi_stuff* a; + + if (gamemode == commercial) + return; + + if (wbs->epsd > 2) + return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims_wi_stuff[wbs->epsd][i]; + + if (bcnt == a->nexttic) + { + switch (a->type) + { + case ANIM_ALWAYS: + if (++a->ctr >= a->nanims) a->ctr = 0; + a->nexttic = bcnt + a->period; + break; + + case ANIM_RANDOM: + a->ctr++; + if (a->ctr == a->nanims) + { + a->ctr = -1; + a->nexttic = bcnt + a->data2 + (M_Random() % a->data1); + } + else a->nexttic = bcnt + a->period; + break; + + case ANIM_LEVEL: + // gawd-awful hack for level anims + if (!(state == StatCount && i == 7) + && wbs->next == a->data1) + { + a->ctr++; + if (a->ctr == a->nanims) a->ctr--; + a->nexttic = bcnt + a->period; + } + break; + } + } + } +} + + +void WI_drawAnimatedBack(void) +{ + int i; + anim_t_wi_stuff* a; + + if (commercial) + return; + + if (wbs->epsd > 2) + return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims_wi_stuff[wbs->epsd][i]; + + if (a->ctr >= 0) + V_DrawPatch(a->loc.x, a->loc.y, FB, a->p[a->ctr]); + } +} + + +// +// Draws a number. +// If digits > 0, then use that many digits minimum, +// otherwise only use as many as necessary. +// Returns new x position. +// +int WI_drawNum(int x, int y, int n, int digits) +{ + int fontwidth = SHORT(num[0]->width); + int neg; + int temp; + + if (digits < 0) + { + if (!n) + { + // make variable-length zeros 1 digit long + digits = 1; + } + else + { + // figure out # of digits in # + digits = 0; + temp = n; + + while (temp) + { + temp /= 10; + digits++; + } + } + } + + neg = n < 0; + if (neg) + n = -n; + + // if non-number, do not draw it + if (n == 1994) + return 0; + + // draw the new number + while (digits--) + { + x -= fontwidth; + V_DrawPatch(x, y, FB, num[n % 10]); + n /= 10; + } + + // draw a minus sign if necessary + if (neg) + V_DrawPatch(x -= 8, y, FB, wiminus); + + return x; +} + + +void WI_drawPercent(int x, int y, int p) +{ + if (p < 0) + return; + + V_DrawPatch(x, y, FB, percent); + WI_drawNum(x, y, p, -1); +} + + +// +// Display level completion time and par, +// or "sucks" message if overflow. +// +void WI_drawTime(int x, int y, int t) +{ + int div; + int n; + + if (t < 0) + return; + + if (t <= 61 * 59) + { + div = 1; + + do + { + n = (t / div) % 60; + x = WI_drawNum(x, y, n, 2) - SHORT(colon->width); + div *= 60; + + // draw + if (div == 60 || t / div) + V_DrawPatch(x, y, FB, colon); + + } while (t / div); + } + else + { + // "sucks" + V_DrawPatch(x - SHORT(sucks->width), y, FB, sucks); + } +} + + +void WI_End(void) +{ + void WI_unloadData(void); + WI_unloadData(); +} + + +void WI_initNoState(void) +{ + state = NoState; + acceleratestage = 0; + cnt = 10; +} + + +void WI_updateNoState(void) +{ + WI_updateAnimatedBack(); + + if (!--cnt) + { + WI_End(); + G_WorldDone(); + } +} + + +void WI_initShowNextLoc(void) +{ + state = ShowNextLoc; + acceleratestage = 0; + cnt = SHOWNEXTLOCDELAY * TICRATE; + + WI_initAnimatedBack(); +} + + +void WI_updateShowNextLoc(void) +{ + WI_updateAnimatedBack(); + + if (!--cnt || acceleratestage) + WI_initNoState(); + else + snl_pointeron = (cnt & 31) < 20; +} + + +void WI_drawShowNextLoc(void) +{ + int i; + int last; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + if (gamemode != commercial) + { + if (wbs->epsd > 2) + { + WI_drawEL(); + return; + } + + last = (wbs->last == 8) ? wbs->next - 1 : wbs->last; + + // draw a splat on taken cities. + for (i = 0; i <= last; i++) + WI_drawOnLnode(i, &splat); + + // splat the secret level? + if (wbs->didsecret) + WI_drawOnLnode(8, &splat); + + // draw flashing ptr + if (snl_pointeron) + WI_drawOnLnode(wbs->next, yah); + } + + // draws which level you are entering.. + if ((gamemode != commercial) + || wbs->next != 30) + WI_drawEL(); +} + + +void WI_drawNoState(void) +{ + snl_pointeron = true; + WI_drawShowNextLoc(); +} + + +int WI_fragSum(int playernum) +{ + int i; + int frags = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] + && i != playernum) + { + frags += plrs[playernum].frags[i]; + } + } + + // JDC hack - negative frags. + frags -= plrs[playernum].frags[playernum]; + // UNUSED if (frags < 0) + // frags = 0; + + return frags; +} + + +void WI_initDeathmatchStats(void) +{ + int i; + int j; + + state = StatCount; + acceleratestage = 0; + dm_state = 1; + + cnt_pause = TICRATE; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + if (playeringame[j]) + dm_frags[i][j] = 0; + + dm_totals[i] = 0; + } + } + + WI_initAnimatedBack(); +} + + +void WI_updateDeathmatchStats(void) +{ + int i; + int j; + + doom_boolean stillticking; + + WI_updateAnimatedBack(); + + if (acceleratestage && dm_state != 4) + { + acceleratestage = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + if (playeringame[j]) + dm_frags[i][j] = plrs[i].frags[j]; + + dm_totals[i] = WI_fragSum(i); + } + } + + + S_StartSound(0, sfx_barexp); + dm_state = 4; + } + + + if (dm_state == 2) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + { + if (playeringame[j] + && dm_frags[i][j] != plrs[i].frags[j]) + { + if (plrs[i].frags[j] < 0) + dm_frags[i][j]--; + else + dm_frags[i][j]++; + + if (dm_frags[i][j] > 99) + dm_frags[i][j] = 99; + + if (dm_frags[i][j] < -99) + dm_frags[i][j] = -99; + + stillticking = true; + } + } + dm_totals[i] = WI_fragSum(i); + + if (dm_totals[i] > 99) + dm_totals[i] = 99; + + if (dm_totals[i] < -99) + dm_totals[i] = -99; + } + + } + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + dm_state++; + } + + } + else if (dm_state == 4) + { + if (acceleratestage) + { + S_StartSound(0, sfx_slop); + + if (gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (dm_state & 1) + { + if (!--cnt_pause) + { + dm_state++; + cnt_pause = TICRATE; + } + } +} + + +void WI_drawDeathmatchStats(void) +{ + int i; + int j; + int x; + int y; + int w; + + int lh; // line height + + lh = WI_SPACINGY; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + WI_drawLF(); + + // draw stat titles (top line) + V_DrawPatch(DM_TOTALSX - SHORT(total->width) / 2, + DM_MATRIXY - WI_SPACINGY + 10, + FB, + total); + + V_DrawPatch(DM_KILLERSX, DM_KILLERSY, FB, killers); + V_DrawPatch(DM_VICTIMSX, DM_VICTIMSY, FB, victims); + + // draw P? + x = DM_MATRIXX + DM_SPACINGX; + y = DM_MATRIXY; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + V_DrawPatch(x - SHORT(p[i]->width) / 2, + DM_MATRIXY - WI_SPACINGY, + FB, + p[i]); + + V_DrawPatch(DM_MATRIXX - SHORT(p[i]->width) / 2, + y, + FB, + p[i]); + + if (i == me) + { + V_DrawPatch(x - SHORT(p[i]->width) / 2, + DM_MATRIXY - WI_SPACINGY, + FB, + bstar); + + V_DrawPatch(DM_MATRIXX - SHORT(p[i]->width) / 2, + y, + FB, + star); + } + } + 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 += DM_SPACINGX; + y += WI_SPACINGY; + } + + // draw stats + y = DM_MATRIXY + 10; + w = SHORT(num[0]->width); + + for (i = 0; i < MAXPLAYERS; i++) + { + x = DM_MATRIXX + DM_SPACINGX; + + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + { + if (playeringame[j]) + WI_drawNum(x + w, y, dm_frags[i][j], 2); + + x += DM_SPACINGX; + } + WI_drawNum(DM_TOTALSX + w, y, dm_totals[i], 2); + } + y += WI_SPACINGY; + } +} + + +void WI_initNetgameStats(void) +{ + int i; + + state = StatCount; + acceleratestage = 0; + ng_state = 1; + + cnt_pause = TICRATE; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0; + + dofrags += WI_fragSum(i); + } + + dofrags = !!dofrags; + + WI_initAnimatedBack(); +} + + +void WI_updateNetgameStats(void) +{ + int i; + int fsum; + + doom_boolean stillticking; + + WI_updateAnimatedBack(); + + if (acceleratestage && ng_state != 10) + { + acceleratestage = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; + + if (dofrags) + cnt_frags[i] = WI_fragSum(i); + } + S_StartSound(0, sfx_barexp); + ng_state = 10; + } + + if (ng_state == 2) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_kills[i] += 2; + + if (cnt_kills[i] >= (plrs[i].skills * 100) / wbs->maxkills) + cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state++; + } + } + else if (ng_state == 4) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_items[i] += 2; + if (cnt_items[i] >= (plrs[i].sitems * 100) / wbs->maxitems) + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + else + stillticking = true; + } + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state++; + } + } + else if (ng_state == 6) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_secret[i] += 2; + + if (cnt_secret[i] >= (plrs[i].ssecret * 100) / wbs->maxsecret) + cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state += 1 + 2 * !dofrags; + } + } + else if (ng_state == 8) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_frags[i] += 1; + + if (cnt_frags[i] >= (fsum = WI_fragSum(i))) + cnt_frags[i] = fsum; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_pldeth); + ng_state++; + } + } + else if (ng_state == 10) + { + if (acceleratestage) + { + S_StartSound(0, sfx_sgcock); + if (gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = TICRATE; + } + } +} + + +void WI_drawNetgameStats(void) +{ + int i; + int x; + int y; + int pwidth = SHORT(percent->width); + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + WI_drawLF(); + + // draw stat titles (top line) + V_DrawPatch(NG_STATSX + NG_SPACINGX - SHORT(kills->width), + NG_STATSY, FB, kills); + + V_DrawPatch(NG_STATSX + 2 * NG_SPACINGX - SHORT(items->width), + NG_STATSY, FB, items); + + V_DrawPatch(NG_STATSX + 3 * NG_SPACINGX - SHORT(secret->width), + NG_STATSY, FB, secret); + + if (dofrags) + V_DrawPatch(NG_STATSX + 4 * NG_SPACINGX - SHORT(frags->width), + NG_STATSY, FB, frags); + + // draw stats + y = NG_STATSY + SHORT(kills->height); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + x = NG_STATSX; + V_DrawPatch(x - SHORT(p[i]->width), y, FB, p[i]); + + if (i == me) + V_DrawPatch(x - SHORT(p[i]->width), y, FB, star); + + x += NG_SPACINGX; + WI_drawPercent(x - pwidth, y + 10, cnt_kills[i]); x += NG_SPACINGX; + WI_drawPercent(x - pwidth, y + 10, cnt_items[i]); x += NG_SPACINGX; + WI_drawPercent(x - pwidth, y + 10, cnt_secret[i]); x += NG_SPACINGX; + + if (dofrags) + WI_drawNum(x, y + 10, cnt_frags[i], -1); + + y += WI_SPACINGY; + } +} + + +void WI_initStats(void) +{ + state = StatCount; + acceleratestage = 0; + sp_state = 1; + cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; + cnt_time = cnt_par = -1; + cnt_pause = TICRATE; + + WI_initAnimatedBack(); +} + + +void WI_updateStats(void) +{ + WI_updateAnimatedBack(); + + if (acceleratestage && sp_state != 10) + { + acceleratestage = 0; + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; + cnt_time = plrs[me].stime / TICRATE; + cnt_par = wbs->partime / TICRATE; + S_StartSound(0, sfx_barexp); + sp_state = 10; + } + + if (sp_state == 2) + { + cnt_kills[0] += 2; + + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) + { + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 4) + { + cnt_items[0] += 2; + + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems) + { + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 6) + { + cnt_secret[0] += 2; + + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs->maxsecret) + { + cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + + else if (sp_state == 8) + { + if (!(bcnt & 3)) + S_StartSound(0, sfx_pistol); + + cnt_time += 3; + + if (cnt_time >= plrs[me].stime / TICRATE) + cnt_time = plrs[me].stime / TICRATE; + + cnt_par += 3; + + if (cnt_par >= wbs->partime / TICRATE) + { + cnt_par = wbs->partime / TICRATE; + + if (cnt_time >= plrs[me].stime / TICRATE) + { + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + } + else if (sp_state == 10) + { + if (acceleratestage) + { + S_StartSound(0, sfx_sgcock); + + if (gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (sp_state & 1) + { + if (!--cnt_pause) + { + sp_state++; + cnt_pause = TICRATE; + } + } +} + + +void WI_drawStats(void) +{ + // line height + int lh; + + lh = (3 * SHORT(num[0]->height)) / 2; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + WI_drawLF(); + + V_DrawPatch(SP_STATSX, SP_STATSY, FB, kills); + WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]); + + V_DrawPatch(SP_STATSX, SP_STATSY + lh, FB, items); + WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY + lh, cnt_items[0]); + + V_DrawPatch(SP_STATSX, SP_STATSY + 2 * lh, FB, sp_secret); + WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY + 2 * lh, cnt_secret[0]); + + V_DrawPatch(SP_TIMEX, SP_TIMEY, FB, time_patch); + WI_drawTime(SCREENWIDTH / 2 - SP_TIMEX, SP_TIMEY, cnt_time); + + if (wbs->epsd < 3) + { + V_DrawPatch(SCREENWIDTH / 2 + SP_TIMEX, SP_TIMEY, FB, par); + WI_drawTime(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par); + } +} + + +void WI_checkForAccelerate(void) +{ + int i; + player_t* player; + + // check for button presses to skip delays + for (i = 0, player = players; i < MAXPLAYERS; i++, player++) + { + if (playeringame[i]) + { + if (player->cmd.buttons & BT_ATTACK) + { + if (!player->attackdown) + acceleratestage = 1; + player->attackdown = true; + } + else + player->attackdown = false; + if (player->cmd.buttons & BT_USE) + { + if (!player->usedown) + acceleratestage = 1; + player->usedown = true; + } + else + player->usedown = false; + } + } +} + + +// Updates stuff each tick +void WI_Ticker(void) +{ + // counter for general background animation + bcnt++; + + if (bcnt == 1) + { + // intermission music + if (gamemode == commercial) + S_ChangeMusic(mus_dm2int, true); + else + S_ChangeMusic(mus_inter, true); + } + + WI_checkForAccelerate(); + + switch (state) + { + case StatCount: + if (deathmatch) WI_updateDeathmatchStats(); + else if (netgame) WI_updateNetgameStats(); + else WI_updateStats(); + break; + + case ShowNextLoc: + WI_updateShowNextLoc(); + break; + + case NoState: + WI_updateNoState(); + break; + } +} + + +void WI_loadData(void) +{ + int i; + int j; + char name[9]; + anim_t_wi_stuff* a; + + if (gamemode == commercial) + doom_strcpy(name, "INTERPIC"); + else + { + //doom_sprintf(name, "WIMAP%d", wbs->epsd); + doom_strcpy(name, "WIMAP"); + doom_concat(name, doom_itoa(wbs->epsd, 10)); + } + + if (gamemode == retail) + { + if (wbs->epsd == 3) + doom_strcpy(name, "INTERPIC"); + } + + // background + bg = W_CacheLumpName(name, PU_CACHE); + V_DrawPatch(0, 0, 1, bg); + + if (gamemode == commercial) + { + NUMCMAPS = 32; + lnames = (patch_t**)Z_Malloc(sizeof(patch_t*) * NUMCMAPS, + PU_STATIC, 0); + for (i = 0; i < NUMCMAPS; i++) + { + //doom_sprintf(name, "CWILV%2.2d", i); + doom_strcpy(name, "CWILV"); + if (i < 10) doom_concat(name, "0"); + doom_concat(name, doom_itoa(i, 10)); + lnames[i] = W_CacheLumpName(name, PU_STATIC); + } + } + else + { + lnames = (patch_t**)Z_Malloc(sizeof(patch_t*) * NUMMAPS, + PU_STATIC, 0); + for (i = 0; i < NUMMAPS; i++) + { + //doom_sprintf(name, "WILV%d%d", wbs->epsd, i); + doom_strcpy(name, "WILV"); + doom_concat(name, doom_itoa(wbs->epsd, 10)); + doom_concat(name, doom_itoa(i, 10)); + lnames[i] = W_CacheLumpName(name, PU_STATIC); + } + + // you are here + yah[0] = W_CacheLumpName("WIURH0", PU_STATIC); + + // you are here (alt.) + yah[1] = W_CacheLumpName("WIURH1", PU_STATIC); + + // splat + splat = W_CacheLumpName("WISPLAT", PU_STATIC); + + if (wbs->epsd < 3) + { + for (j = 0; j < NUMANIMS[wbs->epsd]; j++) + { + a = &anims_wi_stuff[wbs->epsd][j]; + for (i = 0; i < a->nanims; i++) + { + // MONDO HACK! + if (wbs->epsd != 1 || j != 8) + { + // animations + //doom_sprintf(name, "WIA%d%.2d%.2d", wbs->epsd, j, i); + doom_strcpy(name, "WIA"); + doom_concat(name, doom_itoa(wbs->epsd, 10)); + if (j < 10) doom_concat(name, "0"); + doom_concat(name, doom_itoa(j, 10)); + if (i < 10) doom_concat(name, "0"); + doom_concat(name, doom_itoa(i, 10)); + a->p[i] = W_CacheLumpName(name, PU_STATIC); + } + else + { + // HACK ALERT! + a->p[i] = anims_wi_stuff[1][4].p[i]; + } + } + } + } + } + + // More hacks on minus sign. + wiminus = W_CacheLumpName("WIMINUS", PU_STATIC); + + for (i = 0; i < 10; i++) + { + // numbers 0-9 + //doom_sprintf(name, "WINUM%d", i); + doom_strcpy(name, "WINUM"); + doom_concat(name, doom_itoa(i, 10)); + num[i] = W_CacheLumpName(name, PU_STATIC); + } + + // percent sign + percent = W_CacheLumpName("WIPCNT", PU_STATIC); + + // "finished" + finished = W_CacheLumpName("WIF", PU_STATIC); + + // "entering" + entering = W_CacheLumpName("WIENTER", PU_STATIC); + + // "kills" + kills = W_CacheLumpName("WIOSTK", PU_STATIC); + + // "scrt" + secret = W_CacheLumpName("WIOSTS", PU_STATIC); + + // "secret" + sp_secret = W_CacheLumpName("WISCRT2", PU_STATIC); + + // Yuck. + if (french) + { + // "items" + if (netgame && !deathmatch) + items = W_CacheLumpName("WIOBJ", PU_STATIC); + else + items = W_CacheLumpName("WIOSTI", PU_STATIC); + } + else + items = W_CacheLumpName("WIOSTI", PU_STATIC); + + // "frgs" + frags = W_CacheLumpName("WIFRGS", PU_STATIC); + + // ":" + colon = W_CacheLumpName("WICOLON", PU_STATIC); + + // "time" + time_patch = W_CacheLumpName("WITIME", PU_STATIC); + + // "sucks" + sucks = W_CacheLumpName("WISUCKS", PU_STATIC); + + // "par" + par = W_CacheLumpName("WIPAR", PU_STATIC); + + // "killers" (vertical) + killers = W_CacheLumpName("WIKILRS", PU_STATIC); + + // "victims" (horiz) + victims = W_CacheLumpName("WIVCTMS", PU_STATIC); + + // "total" + total = W_CacheLumpName("WIMSTT", PU_STATIC); + + // your face + star = W_CacheLumpName("STFST01", PU_STATIC); + + // dead face + bstar = W_CacheLumpName("STFDEAD0", PU_STATIC); + + for (i = 0; i < MAXPLAYERS; i++) + { + // "1,2,3,4" + //doom_sprintf(name, "STPB%d", i); + doom_strcpy(name, "STPB"); + doom_concat(name, doom_itoa(i, 10)); + p[i] = W_CacheLumpName(name, PU_STATIC); + + // "1,2,3,4" + //doom_sprintf(name, "WIBP%d", i + 1); + doom_strcpy(name, "WIBP"); + doom_concat(name, doom_itoa(i + 1, 10)); + bp[i] = W_CacheLumpName(name, PU_STATIC); + } +} + + +void WI_unloadData(void) +{ + int i; + int j; + + Z_ChangeTag(wiminus, PU_CACHE); + + for (i = 0; i < 10; i++) + Z_ChangeTag(num[i], PU_CACHE); + + if (gamemode == commercial) + { + for (i = 0; i < NUMCMAPS; i++) + Z_ChangeTag(lnames[i], PU_CACHE); + } + else + { + Z_ChangeTag(yah[0], PU_CACHE); + Z_ChangeTag(yah[1], PU_CACHE); + + Z_ChangeTag(splat, PU_CACHE); + + for (i = 0; i < NUMMAPS; i++) + Z_ChangeTag(lnames[i], PU_CACHE); + + if (wbs->epsd < 3) + { + for (j = 0; j < NUMANIMS[wbs->epsd]; j++) + { + if (wbs->epsd != 1 || j != 8) + for (i = 0; i < anims_wi_stuff[wbs->epsd][j].nanims; i++) + Z_ChangeTag(anims_wi_stuff[wbs->epsd][j].p[i], PU_CACHE); + } + } + } + + Z_Free(lnames); + + Z_ChangeTag(percent, PU_CACHE); + Z_ChangeTag(colon, PU_CACHE); + Z_ChangeTag(finished, PU_CACHE); + Z_ChangeTag(entering, PU_CACHE); + Z_ChangeTag(kills, PU_CACHE); + Z_ChangeTag(secret, PU_CACHE); + Z_ChangeTag(sp_secret, PU_CACHE); + Z_ChangeTag(items, PU_CACHE); + Z_ChangeTag(frags, PU_CACHE); + Z_ChangeTag(time_patch, PU_CACHE); + Z_ChangeTag(sucks, PU_CACHE); + Z_ChangeTag(par, PU_CACHE); + + Z_ChangeTag(victims, PU_CACHE); + Z_ChangeTag(killers, PU_CACHE); + Z_ChangeTag(total, PU_CACHE); + // Z_ChangeTag(star, PU_CACHE); + // Z_ChangeTag(bstar, PU_CACHE); + + for (i = 0; i < MAXPLAYERS; i++) + Z_ChangeTag(p[i], PU_CACHE); + + for (i = 0; i < MAXPLAYERS; i++) + Z_ChangeTag(bp[i], PU_CACHE); +} + + +void WI_Drawer(void) +{ + switch (state) + { + case StatCount: + if (deathmatch) + WI_drawDeathmatchStats(); + else if (netgame) + WI_drawNetgameStats(); + else + WI_drawStats(); + break; + + case ShowNextLoc: + WI_drawShowNextLoc(); + break; + + case NoState: + WI_drawNoState(); + break; + } +} + + +void WI_initVariables(wbstartstruct_t* wbstartstruct) +{ + wbs = wbstartstruct; + +#ifdef RANGECHECKING + if (gamemode != commercial) + { + if (gamemode == retail) + RNGCHECK(wbs->epsd, 0, 3); + else + RNGCHECK(wbs->epsd, 0, 2); + } + else + { + RNGCHECK(wbs->last, 0, 8); + RNGCHECK(wbs->next, 0, 8); + } + RNGCHECK(wbs->pnum, 0, MAXPLAYERS); + RNGCHECK(wbs->pnum, 0, MAXPLAYERS); +#endif + + acceleratestage = 0; + cnt = bcnt = 0; + firstrefresh = 1; + me = wbs->pnum; + plrs = wbs->plyr; + + if (!wbs->maxkills) + wbs->maxkills = 1; + + if (!wbs->maxitems) + wbs->maxitems = 1; + + if (!wbs->maxsecret) + wbs->maxsecret = 1; + + if (gamemode != retail) + if (wbs->epsd > 2) + wbs->epsd -= 3; +} + + +void WI_Start(wbstartstruct_t* wbstartstruct) +{ + + WI_initVariables(wbstartstruct); + WI_loadData(); + + if (deathmatch) + WI_initDeathmatchStats(); + else if (netgame) + WI_initNetgameStats(); + else + WI_initStats(); +} diff --git a/src/DOOM/wi_stuff.h b/src/DOOM/wi_stuff.h new file mode 100644 index 0000000..c9aff0a --- /dev/null +++ b/src/DOOM/wi_stuff.h @@ -0,0 +1,51 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Intermission. +// +//----------------------------------------------------------------------------- + +#ifndef __WI_STUFF__ +#define __WI_STUFF__ + +#include "doomdef.h" + +// States for the intermission +typedef enum +{ + NoState = -1, + StatCount, + ShowNextLoc +} stateenum_t; + +// Called by main loop, animate the intermission. +void WI_Ticker(void); + +// Called by main loop, +// draws the intermission directly into the screen buffer. +void WI_Drawer(void); + +// Setup for an intermission screen. +void WI_Start(wbstartstruct_t* wbstartstruct); + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/src/DOOM/z_zone.c b/src/DOOM/z_zone.c new file mode 100644 index 0000000..5313f91 --- /dev/null +++ b/src/DOOM/z_zone.c @@ -0,0 +1,480 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Zone Memory Allocation. Neat. +// +//----------------------------------------------------------------------------- + + +#include "doom_config.h" + +#include "z_zone.h" +#include "i_system.h" +#include "doomdef.h" + + +// +// ZONE MEMORY ALLOCATION +// +// There is never any space between memblocks, +// and there will never be two contiguous free memblocks. +// The rover can be left pointing at a non-empty block. +// +// It is of no value to free a cachable block, +// because it will get overwritten automatically if needed. +// + + +#define ZONEID 0x1d4a11 +#define MINFRAGMENT 64 + + +typedef struct +{ + // total bytes malloced, including header + int size; + + // start / end cap for linked list + memblock_t blocklist; + + memblock_t* rover; +} memzone_t; + + +memzone_t* mainzone; + + +// +// Z_ClearZone +// +void Z_ClearZone(memzone_t* zone) +{ + memblock_t* block; + + // set the entire zone to one free block + zone->blocklist.next = + zone->blocklist.prev = + block = (memblock_t*)((byte*)zone + sizeof(memzone_t)); + + zone->blocklist.user = (void*)zone; + zone->blocklist.tag = PU_STATIC; + zone->rover = block; + + block->prev = block->next = &zone->blocklist; + + // 0 indicates a free block. + block->user = 0; + + block->size = zone->size - sizeof(memzone_t); +} + + +// +// Z_Init +// +void Z_Init(void) +{ + memblock_t* block; + int size; + + mainzone = (memzone_t*)I_ZoneBase(&size); + mainzone->size = size; + + // set the entire zone to one free block + mainzone->blocklist.next = + mainzone->blocklist.prev = + block = (memblock_t*)((byte*)mainzone + sizeof(memzone_t)); + + mainzone->blocklist.user = (void*)mainzone; + mainzone->blocklist.tag = PU_STATIC; + mainzone->rover = block; + + block->prev = block->next = &mainzone->blocklist; + + // 0 indicates a free block. + block->user = 0; + + block->size = mainzone->size - sizeof(memzone_t); +} + + +// +// Z_Free +// +void Z_Free(void* ptr) +{ + memblock_t* block; + memblock_t* other; + + block = (memblock_t*)((byte*)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + I_Error("Error: Z_Free: freed a pointer without ZONEID"); + + if (block->user > (void**)0x100) + { + // smaller values are not pointers + // Note: OS-dependend? + + // clear the user's mark + *block->user = 0; + } + + // mark as free + block->user = 0; + block->tag = 0; + block->id = 0; + + other = block->prev; + + if (!other->user) + { + // merge with previous free block + other->size += block->size; + other->next = block->next; + other->next->prev = other; + + if (block == mainzone->rover) + mainzone->rover = other; + + block = other; + } + + other = block->next; + if (!other->user) + { + // merge the next free block onto the end + block->size += other->size; + block->next = other->next; + block->next->prev = block; + + if (other == mainzone->rover) + mainzone->rover = block; + } +} + + +// +// Z_Malloc +// You can pass a 0 user if the tag is < PU_PURGELEVEL. +// +void* Z_Malloc(int size, int tag, void* user) +{ + int extra; + memblock_t* start; + memblock_t* rover; + memblock_t* newblock; + memblock_t* base; + + size = (size + 3) & ~3; + + // scan through the block list, + // looking for the first free block + // of sufficient size, + // throwing out any purgable blocks along the way. + + // account for size of block header + size += sizeof(memblock_t); + + // if there is a free block behind the rover, + // back up over them + base = mainzone->rover; + + if (!base->prev->user) + base = base->prev; + + rover = base; + start = base->prev; + + do + { + if (rover == start) + { + // scanned all the way around the list + //I_Error("Error: Z_Malloc: failed on allocation of %i bytes", size); + doom_strcpy(error_buf, "Error: Z_Malloc: failed on allocation of "); + doom_concat(error_buf, doom_itoa(size, 10)); + doom_concat(error_buf, " bytes"); + I_Error(error_buf); + } + + if (rover->user) + { + if (rover->tag < PU_PURGELEVEL) + { + // hit a block that can't be purged, + // so move base past it + base = rover = rover->next; + } + else + { + // free the rover block (adding the size to base) + + // the rover can be the base block + base = base->prev; + Z_Free((byte*)rover + sizeof(memblock_t)); + base = base->next; + rover = base->next; + } + } + else + rover = rover->next; + } while (base->user || base->size < size); + + + // found a block big enough + extra = base->size - size; + + if (extra > MINFRAGMENT) + { + // there will be a free fragment after the allocated block + newblock = (memblock_t*)((byte*)base + size); + newblock->size = extra; + + // 0 indicates free block. + newblock->user = 0; + newblock->tag = 0; + newblock->prev = base; + newblock->next = base->next; + newblock->next->prev = newblock; + + base->next = newblock; + base->size = size; + } + + if (user) + { + // mark as an in use block + base->user = user; + *(void**)user = (void*)((byte*)base + sizeof(memblock_t)); + } + else + { + if (tag >= PU_PURGELEVEL) + I_Error("Error: Z_Malloc: an owner is required for purgable blocks"); + + // mark as in use, but unowned + base->user = (void*)2; + } + base->tag = tag; + + // next allocation will start looking here + mainzone->rover = base->next; + + base->id = ZONEID; + + return (void*)((byte*)base + sizeof(memblock_t)); +} + + +// +// Z_FreeTags +// +void Z_FreeTags(int lowtag, int hightag) +{ + memblock_t* block; + memblock_t* next; + + for (block = mainzone->blocklist.next; + block != &mainzone->blocklist; + block = next) + { + // get link before freeing + next = block->next; + + // free block? + if (!block->user) + continue; + + if (block->tag >= lowtag && block->tag <= hightag) + Z_Free((byte*)block + sizeof(memblock_t)); + } +} + + +// +// Z_DumpHeap +// Note: TFileDumpHeap( stdout ) ? +// +void Z_DumpHeap(int lowtag, int hightag) +{ + memblock_t* block; + + //doom_print("zone size: %i location: %p\n", + // mainzone->size, mainzone); + doom_print("zone size: "); + doom_print(doom_itoa(mainzone->size, 10)); + doom_print(" location: "); + doom_print(doom_ptoa(mainzone)); + doom_print("\n"); + + //doom_print("tag range: %i to %i\n", + // lowtag, hightag); + doom_print("tag range: "); + doom_print(doom_itoa(lowtag, 10)); + doom_print(" to "); + doom_print(doom_itoa(hightag, 10)); + doom_print("\n"); + + for (block = mainzone->blocklist.next; ; block = block->next) + { + if (block->tag >= lowtag && block->tag <= hightag) + { + //doom_print("block:%p size:%7i user:%p tag:%3i\n", block, block->size, block->user, block->tag); + doom_print("block"); + doom_print(doom_ptoa(block)); + doom_print(" size:"); + doom_print(doom_itoa(block->size, 10)); + doom_print(" user:"); + doom_print(doom_ptoa(block->user)); + doom_print(" tag:"); + doom_print(doom_itoa(block->tag, 10)); + doom_print("\n"); + } + + if (block->next == &mainzone->blocklist) + { + // all blocks have been hit + break; + } + + if ((byte*)block + block->size != (byte*)block->next) + doom_print("ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + doom_print("ERROR: next block doesn't have proper back link\n"); + + if (!block->user && !block->next->user) + doom_print("ERROR: two consecutive free blocks\n"); + } +} + + +// +// Z_FileDumpHeap +// +void Z_FileDumpHeap(void* f) +{ + memblock_t* block; + + //fprintf(f, "zone size: %i location: %p\n", mainzone->size, mainzone); + doom_fprint(f, "zone size: "); + doom_fprint(f, doom_itoa(mainzone->size, 10)); + doom_fprint(f, " location: "); + doom_fprint(f, doom_ptoa(mainzone)); + doom_fprint(f, "\n"); + + for (block = mainzone->blocklist.next; ; block = block->next) + { + //fprintf(f, "block:%p size:%7i user:%p tag:%3i\n", + // block, block->size, block->user, block->tag); + doom_fprint(f, "block:"); + doom_fprint(f, doom_ptoa(block)); + doom_fprint(f, " size:"); + doom_fprint(f, doom_itoa(block->size, 10)); // Supposed to alignt 7, this is not worth the trouble + doom_fprint(f, " user:"); + doom_fprint(f, doom_ptoa(block->user)); + doom_fprint(f, " tag:"); + doom_fprint(f, doom_itoa(block->tag, 10)); + doom_fprint(f, "\n"); + + + if (block->next == &mainzone->blocklist) + { + // all blocks have been hit + break; + } + + if ((byte*)block + block->size != (byte*)block->next) + doom_fprint(f, "ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + doom_fprint(f, "ERROR: next block doesn't have proper back link\n"); + + if (!block->user && !block->next->user) + doom_fprint(f, "ERROR: two consecutive free blocks\n"); + } +} + + +// +// Z_CheckHeap +// +void Z_CheckHeap(void) +{ + memblock_t* block; + + for (block = mainzone->blocklist.next; ; block = block->next) + { + if (block->next == &mainzone->blocklist) + { + // all blocks have been hit + break; + } + + if ((byte*)block + block->size != (byte*)block->next) + I_Error("Error: Z_CheckHeap: block size does not touch the next block\n"); + + if (block->next->prev != block) + I_Error("Error: Z_CheckHeap: next block doesn't have proper back link\n"); + + if (!block->user && !block->next->user) + I_Error("Error: Z_CheckHeap: two consecutive free blocks\n"); + } +} + + +// +// Z_ChangeTag +// +void Z_ChangeTag2(void* ptr, int tag) +{ + memblock_t* block; + + block = (memblock_t*)((byte*)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + I_Error("Error: Z_ChangeTag: freed a pointer without ZONEID"); + + if (tag >= PU_PURGELEVEL && (unsigned long long)block->user < 0x100) + I_Error("Error: Z_ChangeTag: an owner is required for purgable blocks"); + + block->tag = tag; +} + + +// +// Z_FreeMemory +// +int Z_FreeMemory(void) +{ + memblock_t* block; + int free; + + free = 0; + + for (block = mainzone->blocklist.next; + block != &mainzone->blocklist; + block = block->next) + { + if (!block->user || block->tag >= PU_PURGELEVEL) + free += block->size; + } + return free; +} diff --git a/src/DOOM/z_zone.h b/src/DOOM/z_zone.h new file mode 100644 index 0000000..1375953 --- /dev/null +++ b/src/DOOM/z_zone.h @@ -0,0 +1,91 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Zone Memory Allocation, perhaps NeXT ObjectiveC inspired. +// Remark: this was the only stuff that, according +// to John Carmack, might have been useful for +// Quake. +// +//--------------------------------------------------------------------- + +#ifndef __Z_ZONE__ +#define __Z_ZONE__ + + + + +// +// ZONE MEMORY +// PU - purge tags. +// Tags < 100 are not overwritten until freed. +#define PU_STATIC 1 // static entire execution time +#define PU_SOUND 2 // static while playing +#define PU_MUSIC 3 // static while playing +#define PU_DAVE 4 // anything else Dave wants static +#define PU_LEVEL 50 // static until level exited +#define PU_LEVSPEC 51 // a special thinker in a level +// Tags >= 100 are purgable whenever needed. +#define PU_PURGELEVEL 100 +#define PU_CACHE 101 + + +void Z_Init(void); +void* Z_Malloc(int size, int tag, void* ptr); +void Z_Free(void* ptr); +void Z_FreeTags(int lowtag, int hightag); +void Z_DumpHeap(int lowtag, int hightag); +void Z_FileDumpHeap(void* f); +void Z_CheckHeap(void); +void Z_ChangeTag2(void* ptr, int tag); +int Z_FreeMemory(void); + + +typedef struct memblock_s +{ + int size; // including the header and possibly tiny fragments + void** user; // 0 if a free block + int tag; // purgelevel + int id; // should be ZONEID + struct memblock_s* next; + struct memblock_s* prev; +} memblock_t; + +// +// This is used to get the local FILE:LINE info from CPP +// prior to really call the function in question. +// +#define Z_ChangeTag(p,t) \ +{ \ + if (( (memblock_t *)( (byte *)(p) - sizeof(memblock_t)))->id!=0x1d4a11) \ + { \ + /*I_Error("Error: Z_CT at "__FILE__":%i",__LINE__);*/ \ + char buf[260]; \ + doom_strcpy(buf, "Error: Z_CT at " __FILE__ ":"); \ + doom_concat(buf, doom_itoa(__LINE__, 10)); \ + I_Error(buf); \ + } \ + Z_ChangeTag2(p,t); \ +}; + + +#endif + +//----------------------------------------------------------------------------- +// +// $Log:$ +// +//----------------------------------------------------------------------------- diff --git a/thirdparty/SDL b/thirdparty/SDL new file mode 160000 index 0000000..effed5b --- /dev/null +++ b/thirdparty/SDL @@ -0,0 +1 @@ +Subproject commit effed5b7539ffb65d9611998be1f6a7aaa1a359f diff --git a/thirdparty/SFML b/thirdparty/SFML new file mode 160000 index 0000000..3af9168 --- /dev/null +++ b/thirdparty/SFML @@ -0,0 +1 @@ +Subproject commit 3af9168d032c6f9c28418ad333690e88239e9f34 diff --git a/tools/gen_single_header.py b/tools/gen_single_header.py new file mode 100644 index 0000000..3e48a0a --- /dev/null +++ b/tools/gen_single_header.py @@ -0,0 +1,103 @@ +# This scripts generates single header doom. + + +import glob +import re +import os + + +files = [] +code_files = [] +done_files = [] +final:str = "" + + +class File: + def __init__(self, filename) -> None: + with open(filename, "r") as file: + self.content = file.read() + + # Find headers + self.headers = re.findall(r'#include \"(\w+\.h)\"', self.content) + + # Comment out includes + # Special handling is needed for DOOM.c. It includes system headers, and + # we can't comment those out. For DOOM.c, only comment out quoted + # includes. Otherwise, comment all of them. + if os.path.basename(filename) == "DOOM.c": + self.content = re.sub(r'#include[ \t]+\"', '//#include "', self.content) + else: + self.content = re.sub(r'#include', '//#include', self.content) + + # Remove copyrighted block of text at the top (We include copyright at the top of the PureDOOM.h) + # This is done by finding the first non-empty line that doesn't start with `//` + # Note the use of `:=`: https://docs.python.org/3/whatsnew/3.8.html#assignment-expressions + if (match := re.search(r'(^|\n)(?!//|\n)', self.content)) is not None: + # Get rid of everything up to and including the first match + # This works because the match is for the character preceeding where + # we should start - either `^` or the preceeding newline. + self.content = self.content[match.end():] + + # Clean up some "logs" at the end of the file + self.content = re.sub(r'\/\/-----------------------------------------------------------------------------\n\/\/\n\/\/ \$Log\:\$\n\/\/\n\/\/-----------------------------------------------------------------------------', '', self.content) + + self.name = os.path.basename(filename) + + def __repr__(self) -> str: + return self.name + " -> " + str(self.headers) + + +# Collect headers +headers = glob.glob(r'../src/DOOM/*.h') +headers.sort() # To make it predictable +for header in headers: + if header.endswith("DOOM.h") or header.endswith("d_french.h"): + continue # We'll do that one manually later, and first + files.append(File(header)) + +# Collect code files +codes = glob.glob(r'../src/DOOM/*.c') +codes.sort() # To make it predictable +for code in codes: + code_files.append(File(code)) + +# Start with DOOM.h +with open("../src/DOOM/DOOM.h", "r") as header: + final += header.read() +print("Concat: DOOM.h") +done_files.append("DOOM.h") + +# Concat files as long as there are to concat +while len(files) > 0: + # Find the first file where it's dependencies have been done + found = False + for file in files: + has_dependencies = False + for header in file.headers: + if header not in done_files: + has_dependencies = True + break + if has_dependencies: + continue # Pick a different file + found = True + print("Concat: " + file.name) + final += file.content + done_files.append(file.name) + files.remove(file) + break + if not found: + print("Cycling includes: " + str(files)) + exit(1) + +final += "\n#if defined(DOOM_IMPLEMENTATION)\n" + +# Append all code files after +for file in code_files: + print("Concat: " + file.name) + final += file.content + +final += "\n#endif // DOOM_IMPLEMENTATION\n" + +# Output single header +with open("../PureDOOM.h", "w") as file: + file.write(final)