diff --git a/.codespell-ignore-lines b/.codespell-ignore-lines index b001b634303..92f702a35d5 100644 --- a/.codespell-ignore-lines +++ b/.codespell-ignore-lines @@ -8,3 +8,8 @@ auto certificate = Controlse::CCertificate( * |---------- [-rw-r--r-- 15] afile.txt g_afile.name = "afile.txt"; + "*.deh", "*.hhe", "*.seh", NULL); + MUSIC("introa"), MUSIC("runnin"), MUSIC("stalks"), MUSIC("countd"), + MUSIC("betwee"), MUSIC("doom"), MUSIC("the_da"), MUSIC("shawn"), + MUSIC("messag"), MUSIC("count2"), MUSIC("ddtbl3"), MUSIC("ampie"), + MUSIC("tense"), MUSIC("shawn3"), MUSIC("openin"), MUSIC("evil"), diff --git a/.codespellrc b/.codespellrc index 651916c6a19..743ab2a7567 100644 --- a/.codespellrc +++ b/.codespellrc @@ -9,6 +9,8 @@ exclude-file = .codespell-ignore-lines skip = LICENSE, examples/webpanel/content/www/xterm.min.js, + **/games/NXDoom/src/doom/d_englsh.h, + **/games/NXDoom/src/doom/d_french.h # Ignore words list (FTP protocol commands and technical terms) ignore-words-list = ALLO, ARCHTYPE, parm, shiftIn diff --git a/games/NXDoom/.gitignore b/games/NXDoom/.gitignore new file mode 100644 index 00000000000..58cf415e60f --- /dev/null +++ b/games/NXDoom/.gitignore @@ -0,0 +1,70 @@ +CMDLINE +INSTALL +Makefile.in +Make.dep +TAGS +aclocal.m4 +autom4te.cache +autotools +bin +config.hin +config.log +config.status +configure +lib +obj +rpm.spec +stamp-h +stamp-h.in +stamp-h1 +tags +\#*\# +DOOM*.png +HTIC*.png +HEXEN*.png +STRIFE*.png +DOOM*.pcx +HTIC*.pcx +HEXEN*.pcx +STRIFE*.pcx + +# CMake-specific ignores +/.vs +/build* +/CMakeSettings.json +/out + +# These are the default patterns globally ignored by Subversion: +.depend +.built +*.o +*.gcno +*.gcda +*.lo +*.la +*.al +.libs +*.so +*.so.[0-9]* +*.a +*.pyc +*.pyo +*.rej +*~ +.#* +.*.swp +.DS_store + +# Ignore GNU Global tags and html files +GPATH +GRTAGS +GTAGS +/HTML/ + +# VSCode settings + +/.vscode/ + +# clangd settings +/.cache/clangd +compile_commands.json diff --git a/games/NXDoom/AUTHORS b/games/NXDoom/AUTHORS new file mode 100644 index 00000000000..6324caae4a9 --- /dev/null +++ b/games/NXDoom/AUTHORS @@ -0,0 +1,7 @@ +Simon Howard +James Haley +Samuel Villarreal +Fabian Greffrath +Jonathan Dowland +Alexey Khokholov +Turo Lamminen diff --git a/games/NXDoom/COPYING.md b/games/NXDoom/COPYING.md new file mode 100644 index 00000000000..28fbecabf5c --- /dev/null +++ b/games/NXDoom/COPYING.md @@ -0,0 +1,361 @@ +### 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. + +### 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. + + one line to give the program's name and an idea of what it does. + Copyright (C) yyyy name of author + + 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. + + signature of Ty Coon, 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](https://www.gnu.org/licenses/lgpl.html) instead of this +License. diff --git a/games/NXDoom/Kconfig b/games/NXDoom/Kconfig new file mode 100644 index 00000000000..a30b6d8e6e5 --- /dev/null +++ b/games/NXDoom/Kconfig @@ -0,0 +1,187 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config GAMES_NXDOOM + bool "NXDoom" + default n + depends on ALLOW_GPL_COMPONENTS + depends on VIDEO_FB + depends on CRYPTO + ---help--- + Play DOOM on NuttX! + +if GAMES_NXDOOM + +comment "Program options" + +config GAMES_NXDOOM_PRIORITY + int "Priority" + default 100 + ---help--- + The task priority for the NXDoom application. + +config GAMES_NXDOOM_STACKSIZE + int "Stack size" + default 4096 + ---help--- + The stack size for the NXDoom application. + +comment "General game options" + +choice # Language + prompt "Game language" + default GAMES_NXDOOM_LANG_EN + + config GAMES_NXDOOM_LANG_EN + bool "English" + ---help--- + English language support (original). + + config GAMES_NXDOOM_LANG_FR + bool "French" + ---help--- + French language support. +endchoice # Language + +config GAMES_NXDOOM_PREFDIR + string "DOOM data directory" + default "/data" + ---help--- + Directory where DOOM WAD files are stored. + +config GAMES_NXDOOM_ENDOOM + bool "Show ENDOOM" + default n + ---help--- + Show the ENDOOM screen (https://www.doomwiki.org/wiki/ENDOOM). + +comment "Networking options" + +config GAMES_NXDOOM_NET + bool "Enable network features" + default n + ---help--- + Not ported! Will not work. + +if GAMES_NXDOOM_NET + +config GAMES_NXDOOM_NET_LOGS + bool "Enable network logging" + default n + ---help--- + Enables log information from the network features. + +config GAMES_NXDOOM_NET_MAXPLAYERS + int "Maximum number of players" + default 6 + ---help--- + The maximum number of players, multiplayer/networking. + This is the maximum supported by the networking code; individual games + have their own values for MAXPLAYERS that can be smaller. + ChocolateDOOM default was 8. + +config GAMES_NXDOOM_NET_BACKUPTICS + int "Maximum number of backup tics" + default 32 + ---help--- + The maximum number of backup tics to store locally during a net game. + ChocalateDOOM default was 128. + +config GAMES_NXDOOM_NET_MAXPLAYERNAME + int "Maximum player name length" + default 16 + ---help--- + The maximum number of characters in a player name for netgames. + ChocalateDOOM default was 30. + +endif # GAMES_NXDOOM_NET + +comment "Sound options" + +config GAMES_NXDOOM_SOUND + bool "Enable sound features" + default n + ---help--- + Not ported! Will not work. + +comment "Graphics options" + +config GAMES_NXDOOM_FBPATH + string "Framebuffer path" + default "/dev/fb0" + ---help--- + Path to the framebuffer character driver to use for rendering. + +comment "Input options" + +choice # Input method + prompt "NXDoom input method" + default GAMES_NXDOOM_KEYBOARD + + config GAMES_NXDOOM_KEYBOARD + bool "Keyboard" + ---help--- + Use a NuttX keyboard character driver. + +endchoice # Input method + +if GAMES_NXDOOM_KEYBOARD + +config GAMES_NXDOOM_KBDPATH + string "Keyboard device path" + default "/dev/kbd0" + ---help--- + Path to the keyboard device to use as input. + +endif # GAMES_NXDOOM_KEYBOARD + +comment "Optimizations" + +config GAMES_NXDOOM_MAXVISPLANES + int "Maximum number of visplanes" + default 96 + ---help--- + DOOM allocates a static number of 'visplanes' to use for gameplay. The + original ChocolateDOOM source uses 128 of these. They take a significant + amount of memory, so you may reduce the number. However, too few planes + may cause 'visplane overflow' errors, which is more common in complex + levels. + +config GAMES_NXDOOM_MAXOPENINGS + int "Maximum number of openings" + default 32 + ---help--- + DOOM allocates a static number of 'openings' to use for gameplay. These + are defined as multiples of the SCREENWIDTH (320). The original + ChocolateDOOM source uses 64 as the multiplier. They take a significant + amount of memory, so you may reduce the number. However, too few + openings will cause 'opening overflow' errors. + +config GAMES_NXDOOM_MAXVISSPRITES + int "Maximum number of visible sprites" + default 96 + ---help--- + DOOM allocates a static number of 'vissprites' to use for gameplay. The + original ChocolateDOOM source uses 128. They take a significant amount + of memory, so you may reduce the number. However, too few will cause + overflow errors. + +config GAMES_NXDOOM_MAXDRAWSEGS + int "Maximum number of drawsegs" + default 192 + ---help--- + DOOM allocates a static number of 'drawsegs' to use for gameplay. The + original ChocolateDOOM source uses 256. They take a significant amount of + memory, so you may reduce the number. However, too few will cause + rendering issues (overflow is checked to avoid crashes). + +config GAMES_NXDOOM_RANGECHECK + bool "Perform range checks" + default y + ---help--- + The ChocolateDOOM source includes the ability to optionally exclude + range checking on some array operations. + +endif # GAMES_NXDOOM diff --git a/games/NXDoom/Make.defs b/games/NXDoom/Make.defs new file mode 100644 index 00000000000..b0fa11c3fb5 --- /dev/null +++ b/games/NXDoom/Make.defs @@ -0,0 +1,3 @@ +ifneq ($(CONFIG_GAMES_NXDOOM),) +CONFIGURED_APPS += $(APPDIR)/games/NXDoom +endif diff --git a/games/NXDoom/Makefile b/games/NXDoom/Makefile new file mode 100644 index 00000000000..45df1a6de3e --- /dev/null +++ b/games/NXDoom/Makefile @@ -0,0 +1,203 @@ +include $(APPDIR)/Make.defs + +# Program options + +MODULE = $(CONFIG_GAMES_NXDOOM) +PRIORITY = $(CONFIG_GAMES_NXDOOM_PRIORITY) +STACKSIZE = $(CONFIG_GAMES_NXDOOM_STACKSIZE) +PROGNAME = nxdoom + +# Source files + +COMMONSRCDIR = src +TEXTSCREENSRCDIR = textscreen +DOOMSRCDIR = $(COMMONSRCDIR)/doom +PCSOUNDSRCDIR = pcsound + +# Includes + +CFLAGS += -I$(COMMONSRCDIR) -I$(DOOMSRCDIR) -I$(TEXTSCREENSRCDIR) +CFLAGS += -I$(PCSOUNDSRCDIR) + +# Source files + +COMMONSRCS = i_system.c m_argv.c m_misc.c # Excluding i_main.c +COMMONSRCS += z_native.c # Native memory allocation + +DEHACKEDSRCS = deh_io.c deh_main.c deh_mapping.c deh_text.c + +BASESRCS = \ + aes_prng.c \ + d_event.c \ + d_iwad.c \ + d_loop.c \ + d_mode.c \ + deh_str.c \ + gusconf.c \ + i_glob.c \ + i_input.c \ + i_joystick.c \ + i_timer.c \ + i_video.c \ + m_bbox.c \ + m_cheat.c \ + m_config.c \ + m_controls.c \ + m_fixed.c \ + p_rejectpad.c \ + memio.c \ + tables.c \ + v_diskicon.c \ + v_video.c \ + w_checksum.c \ + w_main.c \ + w_wad.c \ + w_file.c \ + w_file_stdc.c \ + w_merge.c \ + +# Only add ENDOOM stuff if the user wants it + +ifeq ($(CONFIG_GAMES_NXDOOM_ENDOOM),y) +BASESRCS += i_endoom.c +endif + +NETSRCS = \ + net_client.c \ + net_common.c \ + net_dedicated.c \ + net_gui.c \ + net_io.c \ + net_loop.c \ + net_packet.c \ + net_query.c \ + net_sdl.c \ + net_server.c \ + net_structrw.c \ + +DOOMSRCS = \ + am_map.c \ + deh_ammo.c \ + deh_bexstr.c \ + deh_cheat.c \ + deh_doom.c \ + deh_frame.c \ + deh_misc.c \ + deh_ptr.c \ + deh_thing.c \ + deh_weapon.c \ + d_items.c \ + d_main.c \ + d_net.c \ + doomdef.c \ + doomstat.c \ + dstrings.c \ + f_finale.c \ + f_wipe.c \ + g_game.c \ + hu_lib.c \ + hu_stuff.c \ + info.c \ + m_menu.c \ + m_random.c \ + p_ceilng.c \ + p_doors.c \ + p_enemy.c \ + p_floor.c \ + p_inter.c \ + p_lights.c \ + p_map.c \ + p_maputl.c \ + p_mobj.c \ + p_plats.c \ + p_pspr.c \ + p_saveg.c \ + p_setup.c \ + p_sight.c \ + p_spec.c \ + p_switch.c \ + p_telept.c \ + p_tick.c \ + p_user.c \ + r_bsp.c \ + r_data.c \ + r_draw.c \ + r_main.c \ + r_plane.c \ + r_segs.c \ + r_sky.c \ + r_things.c \ + statdump.c \ + st_lib.c \ + st_stuff.c \ + wi_stuff.c + +TXTSCREENSRCS = \ + txt_conditional.c \ + txt_checkbox.c \ + txt_desktop.c \ + txt_dropdown.c \ + txt_fileselect.c \ + txt_gui.c \ + txt_inputbox.c \ + txt_io.c \ + txt_button.c \ + txt_label.c \ + txt_radiobutton.c \ + txt_scrollpane.c \ + txt_separator.c \ + txt_spinctrl.c \ + txt_sdl.c \ + txt_strut.c \ + txt_table.c \ + txt_utf8.c \ + txt_widget.c \ + txt_window.c \ + txt_window_action.c + +CSRCS += $(patsubst %,$(COMMONSRCDIR)/%,$(COMMONSRCS)) +CSRCS += $(patsubst %,$(COMMONSRCDIR)/%,$(DEHACKEDSRCS)) +CSRCS += $(patsubst %,$(COMMONSRCDIR)/%,$(BASESRCS)) +CSRCS += $(patsubst %,$(DOOMSRCDIR)/%,$(DOOMSRCS)) + +# Textscreen stuff only needs to be included for networking stuff +# or if the user wants to see ENDOOM + +ifeq ($(CONFIG_GAMES_NXDOOM_ENDOOM),y) +ADDTXTSCREEN = y +endif + +ifeq ($(CONFIG_GAMES_NXDOOM_NET),y) +ADDTXTSCREEN = y +endif + +# Only include textscreen code if needed + +ifeq ($(ADDTXTSCREEN),y) +CSRCS += $(patsubst %,$(TEXTSCREENSRCDIR)/%,$(TXTSCREENSRCS)) +endif + +# Only include networking logic if enabled + +ifeq ($(CONFIG_GAMES_NXDOOM_NET),y) +CSRCS += $(patsubst %,$(COMMONSRCDIR)/%,$(NETSRCS)) +endif + +# Only include sound logic if enabled + +ifeq ($(CONFIG_GAMES_NXDOOM_SOUND),y) +CSRCS += $(COMMONSRCDIR)/i_musicpack.c +CSRCS += $(COMMONSRCDIR)/i_pcsound.c +CSRCS += $(COMMONSRCDIR)/i_sdlmusic.c +CSRCS += $(COMMONSRCDIR)/i_sound.c +CSRCS += $(COMMONSRCDIR)/midifile.c +CSRCS += $(COMMONSRCDIR)/mus2mid.c + +CSRCS += $(DOOMSRCDIR)/sounds.c +CSRCS += $(DOOMSRCDIR)/s_sound.c +CSRCS += $(DOOMSRCDIR)/deh_sound.c +endif + +MAINSRC = $(COMMONSRCDIR)/i_main.c + +include $(APPDIR)/Application.mk diff --git a/games/NXDoom/pcsound/.gitignore b/games/NXDoom/pcsound/.gitignore new file mode 100644 index 00000000000..022c663ee09 --- /dev/null +++ b/games/NXDoom/pcsound/.gitignore @@ -0,0 +1,8 @@ +Makefile.in +Makefile +.deps +libpcsound.a +*.rc +tags +TAGS + diff --git a/games/NXDoom/pcsound/pcsound.c b/games/NXDoom/pcsound/pcsound.c new file mode 100644 index 00000000000..8e7b73bd6e1 --- /dev/null +++ b/games/NXDoom/pcsound/pcsound.c @@ -0,0 +1,129 @@ +/**************************************************************************** + * apps/games/NXDoom/pcsound/pcsound.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * PC speaker interface. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "config.h" +#include "pcsound.h" +#include "pcsound_internal.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static pcsound_driver_t *g_drivers[] = +{ +#ifdef HAVE_LINUX_KD_H + &pcsound_linux_driver, +#endif + NULL, +}; + +static pcsound_driver_t *g_pcsound_driver = NULL; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int pcsound_sample_rate; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void pc_sound_set_sample_rate(int rate) +{ + pcsound_sample_rate = rate; +} + +int pc_sound_init(pcsound_callback_func callback_func) +{ + char *driver_name; + int i; + + if (g_pcsound_driver != NULL) + { + return 1; + } + + /* Check if the environment variable is set */ + + driver_name = getenv("PCSOUND_DRIVER"); + + if (driver_name != NULL) + { + for (i = 0; g_drivers[i] != NULL; ++i) + { + if (!strcmp(g_drivers[i]->name, driver_name)) + { + /* Found the driver! */ + + if (g_drivers[i]->init_func(callback_func)) + { + g_pcsound_driver = g_drivers[i]; + } + else + { + printf("Failed to initialize PC sound driver: %s\n", + g_drivers[i]->name); + break; + } + } + } + } + else + { + /* Try all drivers until we find a working one */ + + for (i = 0; g_drivers[i] != NULL; ++i) + { + if (g_drivers[i]->init_func(callback_func)) + { + g_pcsound_driver = g_drivers[i]; + break; + } + } + } + + if (g_pcsound_driver != NULL) + { + printf("Using PC sound driver: %s\n", g_pcsound_driver->name); + return 1; + } + else + { + printf("Failed to find a working PC sound driver.\n"); + return 0; + } +} + +void pc_sound_shutdown(void) +{ + g_pcsound_driver->shutdown_func(); + g_pcsound_driver = NULL; +} diff --git a/games/NXDoom/pcsound/pcsound.h b/games/NXDoom/pcsound/pcsound.h new file mode 100644 index 00000000000..0d1e0f3ba75 --- /dev/null +++ b/games/NXDoom/pcsound/pcsound.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * apps/games/NXDoom/pcsound/pcsound.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * PC speaker interface. + * + ****************************************************************************/ + +#ifndef PCSOUND_H +#define PCSOUND_H + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*pcsound_callback_func)(int *duration, int *frequency); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Initialise the PC speaker subsystem. The given function is called + * periodically to request more sound data to play. + */ + +int pc_sound_init(pcsound_callback_func callback_func); + +/* Shut down the PC speaker subsystem. */ + +void pc_sound_shutdown(void); + +/* Set the preferred output sample rate when emulating a PC speaker. + * This must be called before pc_sound_init. + */ + +void pc_sound_set_sample_rate(int rate); + +#endif /* PCSOUND_H */ diff --git a/games/NXDoom/pcsound/pcsound_internal.h b/games/NXDoom/pcsound/pcsound_internal.h new file mode 100644 index 00000000000..295ad6266be --- /dev/null +++ b/games/NXDoom/pcsound/pcsound_internal.h @@ -0,0 +1,65 @@ +/**************************************************************************** + * apps/games/NXDoom/pcsound/pcsound_internal.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * PC speaker interface. + * + ****************************************************************************/ + +#ifndef PCSOUND_INTERNAL_H +#define PCSOUND_INTERNAL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "pcsound.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PCSOUND_8253_FREQUENCY (1193280) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct pcsound_driver_s pcsound_driver_t; +typedef int (*pcsound_init_func)(pcsound_callback_func callback); +typedef void (*pcsound_shutdown_func)(void); + +struct pcsound_driver_s +{ + const char *name; + pcsound_init_func init_func; + pcsound_shutdown_func shutdown_func; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int pcsound_sample_rate; + +#ifdef HAVE_LINUX_KD_H +extern pcsound_driver_t pcsound_linux_driver; +#endif + +extern pcsound_driver_t pcsound_sdl_driver; + +#endif /* PCSOUND_INTERNAL_H */ diff --git a/games/NXDoom/src/.gitignore b/games/NXDoom/src/.gitignore new file mode 100644 index 00000000000..a3127e6bd85 --- /dev/null +++ b/games/NXDoom/src/.gitignore @@ -0,0 +1,22 @@ +Makefile +Makefile.in +.deps +*.rc +chocolate-doom +chocolate-heretic +chocolate-hexen +chocolate-server +chocolate-strife +chocolate-doom-setup +chocolate-heretic-setup +chocolate-hexen-setup +chocolate-strife-setup +chocolate-setup +*.cfg +*.exe +*.desktop +*.txt +!CMakeLists.txt +*.metainfo.xml +tags +TAGS diff --git a/games/NXDoom/src/aes_prng.c b/games/NXDoom/src/aes_prng.c new file mode 100644 index 00000000000..04de172ef12 --- /dev/null +++ b/games/NXDoom/src/aes_prng.c @@ -0,0 +1,930 @@ +/**************************************************************************** + * apps/games/NXDoom/src/aes_prng.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * + * This implements a cryptographically secure pseudorandom number + * generator for implementing secure demos. The approach taken is to + * use the AES (Rijndael) stream cipher in "counter" mode, encrypting + * an incrementing counter. The cipher key acts as the random seed. + * Cryptanalysis of AES used in this way has shown it to be an + * effective PRNG (see: Empirical Evidence concerning AES, Hellekalek + * & Wegenkittl, 2003). + * + * AES implementation is taken from the Linux kernel's AES + * implementation, found in crypto/aes_generic.c. It has been hacked + * up to work independently. + * + * AES Cipher Algorithm. + * + * Based on Brian Gladman's code. + * + * Linux developers: + * Alexander Kjeldaas + * Herbert Valerio Riedel + * Kyle McMartin + * Adam J. Richter (conversion to 2.5 API). + * + * 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. + * + * Copyright (c) 2002, Dr Brian Gladman , Worcester, UK. + * All rights reserved. + * + * LICENSE TERMS + * + * The free distribution and use of this software in both source and binary + * form is allowed (with or without changes) provided that: + * + * 1. distributions of this source code include the above copyright + * notice, this list of conditions and the following disclaimer; + * + * 2. distributions in binary form include the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other associated materials; + * + * 3. the copyright holder's name is not used to endorse products + * built using this software without specific written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL), in which case the provisions of the GPL apply INSTEAD OF + * those given above. + * + * DISCLAIMER + * + * This software is provided 'as is' with no explicit or implied warranties + * in respect of its properties, including, but not limited to, correctness + * and/or fitness for purpose. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "aes_prng.h" +#include "doomtype.h" +#include "i_swap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define AES_MIN_KEY_SIZE 16 +#define AES_MAX_KEY_SIZE 32 +#define AES_KEYSIZE_128 16 +#define AES_KEYSIZE_192 24 +#define AES_KEYSIZE_256 32 +#define AES_BLOCK_SIZE 16 +#define AES_MAX_KEYLENGTH (15 * 16) +#define AES_MAX_KEYLENGTH_U32 (AES_MAX_KEYLENGTH / sizeof(uint32_t)) + +#define cpu_to_le32(x) htole32(x) +#define le32_to_cpu(x) le32toh(x) + +#define star_x(x) \ + (((x) & 0x7f7f7f7f) << 1) ^ ((((x) & 0x80808080) >> 7) * 0x1b) + +#define imix_col(y, x) \ + do \ + { \ + u = star_x(x); \ + v = star_x(u); \ + w = star_x(v); \ + t = w ^ (x); \ + (y) = u ^ v ^ w; \ + (y) ^= aes_ror32(u ^ t, 8) ^ aes_ror32(v ^ t, 16) ^ aes_ror32(t, 24); \ + } \ + while (0) + +#define ls_box(x) \ + crypto_fl_tab[0][get_byte(x, 0)] ^ crypto_fl_tab[1][get_byte(x, 1)] ^ \ + crypto_fl_tab[2][get_byte(x, 2)] ^ crypto_fl_tab[3][get_byte(x, 3)] + +#define loop4(i) \ + do \ + { \ + t = aes_ror32(t, 8); \ + t = ls_box(t) ^ rco_tab[i]; \ + t ^= ctx->key_enc[4 * i]; \ + ctx->key_enc[4 * i + 4] = t; \ + t ^= ctx->key_enc[4 * i + 1]; \ + ctx->key_enc[4 * i + 5] = t; \ + t ^= ctx->key_enc[4 * i + 2]; \ + ctx->key_enc[4 * i + 6] = t; \ + t ^= ctx->key_enc[4 * i + 3]; \ + ctx->key_enc[4 * i + 7] = t; \ + } \ + while (0) + +#define loop6(i) \ + do \ + { \ + t = aes_ror32(t, 8); \ + t = ls_box(t) ^ rco_tab[i]; \ + t ^= ctx->key_enc[6 * i]; \ + ctx->key_enc[6 * i + 6] = t; \ + t ^= ctx->key_enc[6 * i + 1]; \ + ctx->key_enc[6 * i + 7] = t; \ + t ^= ctx->key_enc[6 * i + 2]; \ + ctx->key_enc[6 * i + 8] = t; \ + t ^= ctx->key_enc[6 * i + 3]; \ + ctx->key_enc[6 * i + 9] = t; \ + t ^= ctx->key_enc[6 * i + 4]; \ + ctx->key_enc[6 * i + 10] = t; \ + t ^= ctx->key_enc[6 * i + 5]; \ + ctx->key_enc[6 * i + 11] = t; \ + } \ + while (0) + +#define loop8tophalf(i) \ + do \ + { \ + t = aes_ror32(t, 8); \ + t = ls_box(t) ^ rco_tab[i]; \ + t ^= ctx->key_enc[8 * i]; \ + ctx->key_enc[8 * i + 8] = t; \ + t ^= ctx->key_enc[8 * i + 1]; \ + ctx->key_enc[8 * i + 9] = t; \ + t ^= ctx->key_enc[8 * i + 2]; \ + ctx->key_enc[8 * i + 10] = t; \ + t ^= ctx->key_enc[8 * i + 3]; \ + ctx->key_enc[8 * i + 11] = t; \ + } \ + while (0) + +#define loop8(i) \ + do \ + { \ + loop8tophalf(i); \ + t = ctx->key_enc[8 * i + 4] ^ ls_box(t); \ + ctx->key_enc[8 * i + 12] = t; \ + t ^= ctx->key_enc[8 * i + 5]; \ + ctx->key_enc[8 * i + 13] = t; \ + t ^= ctx->key_enc[8 * i + 6]; \ + ctx->key_enc[8 * i + 14] = t; \ + t ^= ctx->key_enc[8 * i + 7]; \ + ctx->key_enc[8 * i + 15] = t; \ + } \ + while (0) + +/* encrypt a block of text */ + +#define f_rn(bo, bi, n, k) \ + do \ + { \ + bo[n] = crypto_ft_tab[0][get_byte(bi[n], 0)] ^ \ + crypto_ft_tab[1][get_byte(bi[(n + 1) & 3], 1)] ^ \ + crypto_ft_tab[2][get_byte(bi[(n + 2) & 3], 2)] ^ \ + crypto_ft_tab[3][get_byte(bi[(n + 3) & 3], 3)] ^ *(k + n); \ + } \ + while (0) + +#define f_nround(bo, bi, k) \ + do \ + { \ + f_rn(bo, bi, 0, k); \ + f_rn(bo, bi, 1, k); \ + f_rn(bo, bi, 2, k); \ + f_rn(bo, bi, 3, k); \ + k += 4; \ + } \ + while (0) + +#define f_rl(bo, bi, n, k) \ + do \ + { \ + bo[n] = crypto_fl_tab[0][get_byte(bi[n], 0)] ^ \ + crypto_fl_tab[1][get_byte(bi[(n + 1) & 3], 1)] ^ \ + crypto_fl_tab[2][get_byte(bi[(n + 2) & 3], 2)] ^ \ + crypto_fl_tab[3][get_byte(bi[(n + 3) & 3], 3)] ^ *(k + n); \ + } \ + while (0) + +#define f_lround(bo, bi, k) \ + do \ + { \ + f_rl(bo, bi, 0, k); \ + f_rl(bo, bi, 1, k); \ + f_rl(bo, bi, 2, k); \ + f_rl(bo, bi, 3, k); \ + } \ + while (0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Please ensure that the first two fields are 16-byte aligned + * relative to the start of the structure, i.e., don't move them! + */ + +typedef struct +{ + uint32_t key_enc[AES_MAX_KEYLENGTH_U32]; + uint32_t key_dec[AES_MAX_KEYLENGTH_U32]; + uint32_t key_length; +} aes_context_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static boolean prng_enabled = false; +static aes_context_t prng_context; +static uint32_t prng_input_counter; +static uint32_t prng_values[4]; +static unsigned int prng_value_index = 0; + +static const uint32_t rco_tab[10] = +{ + 1, 2, 4, 8, 16, 32, 64, 128, 27, 54, +}; + +static const uint32_t crypto_ft_tab[4][256] = +{ + { + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, + 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, + 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, + 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, + 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, + 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, + 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, + 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, + 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, + 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, + 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, + 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, + 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, + 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, + 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, + 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, + 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, + 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, + 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, + 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, + 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, + 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, + 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, + 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, + 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, + 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, + 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, + 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, + 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, + 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, + 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, + 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, + 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, + 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, + 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, + 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, + 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, + 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, + 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, + 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, + 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, + 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, + 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, + 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, + 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, + 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, + 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, + 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, + 0x3a16162c, + }, + { + 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, + 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, + 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, + 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, + 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, + 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, + 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, + 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, + 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, + 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, + 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, + 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, + 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, + 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, + 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, + 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, + 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, + 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, + 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, + 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, + 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, + 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, + 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, + 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, + 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, + 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, + 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, + 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, + 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, + 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, + 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, + 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, + 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, + 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, + 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, + 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, + 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, + 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, + 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, + 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, + 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, + 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, + 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, + 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, + 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, + 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, + 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, + 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, + 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, + 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, + 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, + 0x16162c3a, + }, + { + 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, + 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, + 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, + 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, + 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, + 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, + 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, + 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, + 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, + 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, + 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, + 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, + 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, + 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, + 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, + 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, + 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, + 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, + 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, + 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, + 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, + 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, + 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, + 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, + 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, + 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, + 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, + 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, + 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, + 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, + 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, + 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, + 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, + 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, + 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, + 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, + 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, + 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, + 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, + 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, + 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, + 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, + 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, + 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, + 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, + 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, + 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, + 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, + 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, + 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, + 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, + 0x162c3a16, + }, + { + 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, + 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, + 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, + 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, + 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, + 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, + 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, + 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, + 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, + 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, + 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, + 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, + 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, + 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, + 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, + 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, + 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, + 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, + 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, + 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, + 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, + 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, + 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, + 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, + 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, + 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, + 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, + 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, + 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, + 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, + 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, + 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, + 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, + 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, + 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, + 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, + 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, + 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, + 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, + 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, + 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, + 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, + 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, + 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, + 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, + 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, + 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, + 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, + 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, + 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, + 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, + 0x2c3a1616, + }, +}; + +static const uint32_t crypto_fl_tab[4][256] = +{ + { + 0x00000063, 0x0000007c, 0x00000077, 0x0000007b, 0x000000f2, + 0x0000006b, 0x0000006f, 0x000000c5, 0x00000030, 0x00000001, + 0x00000067, 0x0000002b, 0x000000fe, 0x000000d7, 0x000000ab, + 0x00000076, 0x000000ca, 0x00000082, 0x000000c9, 0x0000007d, + 0x000000fa, 0x00000059, 0x00000047, 0x000000f0, 0x000000ad, + 0x000000d4, 0x000000a2, 0x000000af, 0x0000009c, 0x000000a4, + 0x00000072, 0x000000c0, 0x000000b7, 0x000000fd, 0x00000093, + 0x00000026, 0x00000036, 0x0000003f, 0x000000f7, 0x000000cc, + 0x00000034, 0x000000a5, 0x000000e5, 0x000000f1, 0x00000071, + 0x000000d8, 0x00000031, 0x00000015, 0x00000004, 0x000000c7, + 0x00000023, 0x000000c3, 0x00000018, 0x00000096, 0x00000005, + 0x0000009a, 0x00000007, 0x00000012, 0x00000080, 0x000000e2, + 0x000000eb, 0x00000027, 0x000000b2, 0x00000075, 0x00000009, + 0x00000083, 0x0000002c, 0x0000001a, 0x0000001b, 0x0000006e, + 0x0000005a, 0x000000a0, 0x00000052, 0x0000003b, 0x000000d6, + 0x000000b3, 0x00000029, 0x000000e3, 0x0000002f, 0x00000084, + 0x00000053, 0x000000d1, 0x00000000, 0x000000ed, 0x00000020, + 0x000000fc, 0x000000b1, 0x0000005b, 0x0000006a, 0x000000cb, + 0x000000be, 0x00000039, 0x0000004a, 0x0000004c, 0x00000058, + 0x000000cf, 0x000000d0, 0x000000ef, 0x000000aa, 0x000000fb, + 0x00000043, 0x0000004d, 0x00000033, 0x00000085, 0x00000045, + 0x000000f9, 0x00000002, 0x0000007f, 0x00000050, 0x0000003c, + 0x0000009f, 0x000000a8, 0x00000051, 0x000000a3, 0x00000040, + 0x0000008f, 0x00000092, 0x0000009d, 0x00000038, 0x000000f5, + 0x000000bc, 0x000000b6, 0x000000da, 0x00000021, 0x00000010, + 0x000000ff, 0x000000f3, 0x000000d2, 0x000000cd, 0x0000000c, + 0x00000013, 0x000000ec, 0x0000005f, 0x00000097, 0x00000044, + 0x00000017, 0x000000c4, 0x000000a7, 0x0000007e, 0x0000003d, + 0x00000064, 0x0000005d, 0x00000019, 0x00000073, 0x00000060, + 0x00000081, 0x0000004f, 0x000000dc, 0x00000022, 0x0000002a, + 0x00000090, 0x00000088, 0x00000046, 0x000000ee, 0x000000b8, + 0x00000014, 0x000000de, 0x0000005e, 0x0000000b, 0x000000db, + 0x000000e0, 0x00000032, 0x0000003a, 0x0000000a, 0x00000049, + 0x00000006, 0x00000024, 0x0000005c, 0x000000c2, 0x000000d3, + 0x000000ac, 0x00000062, 0x00000091, 0x00000095, 0x000000e4, + 0x00000079, 0x000000e7, 0x000000c8, 0x00000037, 0x0000006d, + 0x0000008d, 0x000000d5, 0x0000004e, 0x000000a9, 0x0000006c, + 0x00000056, 0x000000f4, 0x000000ea, 0x00000065, 0x0000007a, + 0x000000ae, 0x00000008, 0x000000ba, 0x00000078, 0x00000025, + 0x0000002e, 0x0000001c, 0x000000a6, 0x000000b4, 0x000000c6, + 0x000000e8, 0x000000dd, 0x00000074, 0x0000001f, 0x0000004b, + 0x000000bd, 0x0000008b, 0x0000008a, 0x00000070, 0x0000003e, + 0x000000b5, 0x00000066, 0x00000048, 0x00000003, 0x000000f6, + 0x0000000e, 0x00000061, 0x00000035, 0x00000057, 0x000000b9, + 0x00000086, 0x000000c1, 0x0000001d, 0x0000009e, 0x000000e1, + 0x000000f8, 0x00000098, 0x00000011, 0x00000069, 0x000000d9, + 0x0000008e, 0x00000094, 0x0000009b, 0x0000001e, 0x00000087, + 0x000000e9, 0x000000ce, 0x00000055, 0x00000028, 0x000000df, + 0x0000008c, 0x000000a1, 0x00000089, 0x0000000d, 0x000000bf, + 0x000000e6, 0x00000042, 0x00000068, 0x00000041, 0x00000099, + 0x0000002d, 0x0000000f, 0x000000b0, 0x00000054, 0x000000bb, + 0x00000016, + }, + { + 0x00006300, 0x00007c00, 0x00007700, 0x00007b00, 0x0000f200, + 0x00006b00, 0x00006f00, 0x0000c500, 0x00003000, 0x00000100, + 0x00006700, 0x00002b00, 0x0000fe00, 0x0000d700, 0x0000ab00, + 0x00007600, 0x0000ca00, 0x00008200, 0x0000c900, 0x00007d00, + 0x0000fa00, 0x00005900, 0x00004700, 0x0000f000, 0x0000ad00, + 0x0000d400, 0x0000a200, 0x0000af00, 0x00009c00, 0x0000a400, + 0x00007200, 0x0000c000, 0x0000b700, 0x0000fd00, 0x00009300, + 0x00002600, 0x00003600, 0x00003f00, 0x0000f700, 0x0000cc00, + 0x00003400, 0x0000a500, 0x0000e500, 0x0000f100, 0x00007100, + 0x0000d800, 0x00003100, 0x00001500, 0x00000400, 0x0000c700, + 0x00002300, 0x0000c300, 0x00001800, 0x00009600, 0x00000500, + 0x00009a00, 0x00000700, 0x00001200, 0x00008000, 0x0000e200, + 0x0000eb00, 0x00002700, 0x0000b200, 0x00007500, 0x00000900, + 0x00008300, 0x00002c00, 0x00001a00, 0x00001b00, 0x00006e00, + 0x00005a00, 0x0000a000, 0x00005200, 0x00003b00, 0x0000d600, + 0x0000b300, 0x00002900, 0x0000e300, 0x00002f00, 0x00008400, + 0x00005300, 0x0000d100, 0x00000000, 0x0000ed00, 0x00002000, + 0x0000fc00, 0x0000b100, 0x00005b00, 0x00006a00, 0x0000cb00, + 0x0000be00, 0x00003900, 0x00004a00, 0x00004c00, 0x00005800, + 0x0000cf00, 0x0000d000, 0x0000ef00, 0x0000aa00, 0x0000fb00, + 0x00004300, 0x00004d00, 0x00003300, 0x00008500, 0x00004500, + 0x0000f900, 0x00000200, 0x00007f00, 0x00005000, 0x00003c00, + 0x00009f00, 0x0000a800, 0x00005100, 0x0000a300, 0x00004000, + 0x00008f00, 0x00009200, 0x00009d00, 0x00003800, 0x0000f500, + 0x0000bc00, 0x0000b600, 0x0000da00, 0x00002100, 0x00001000, + 0x0000ff00, 0x0000f300, 0x0000d200, 0x0000cd00, 0x00000c00, + 0x00001300, 0x0000ec00, 0x00005f00, 0x00009700, 0x00004400, + 0x00001700, 0x0000c400, 0x0000a700, 0x00007e00, 0x00003d00, + 0x00006400, 0x00005d00, 0x00001900, 0x00007300, 0x00006000, + 0x00008100, 0x00004f00, 0x0000dc00, 0x00002200, 0x00002a00, + 0x00009000, 0x00008800, 0x00004600, 0x0000ee00, 0x0000b800, + 0x00001400, 0x0000de00, 0x00005e00, 0x00000b00, 0x0000db00, + 0x0000e000, 0x00003200, 0x00003a00, 0x00000a00, 0x00004900, + 0x00000600, 0x00002400, 0x00005c00, 0x0000c200, 0x0000d300, + 0x0000ac00, 0x00006200, 0x00009100, 0x00009500, 0x0000e400, + 0x00007900, 0x0000e700, 0x0000c800, 0x00003700, 0x00006d00, + 0x00008d00, 0x0000d500, 0x00004e00, 0x0000a900, 0x00006c00, + 0x00005600, 0x0000f400, 0x0000ea00, 0x00006500, 0x00007a00, + 0x0000ae00, 0x00000800, 0x0000ba00, 0x00007800, 0x00002500, + 0x00002e00, 0x00001c00, 0x0000a600, 0x0000b400, 0x0000c600, + 0x0000e800, 0x0000dd00, 0x00007400, 0x00001f00, 0x00004b00, + 0x0000bd00, 0x00008b00, 0x00008a00, 0x00007000, 0x00003e00, + 0x0000b500, 0x00006600, 0x00004800, 0x00000300, 0x0000f600, + 0x00000e00, 0x00006100, 0x00003500, 0x00005700, 0x0000b900, + 0x00008600, 0x0000c100, 0x00001d00, 0x00009e00, 0x0000e100, + 0x0000f800, 0x00009800, 0x00001100, 0x00006900, 0x0000d900, + 0x00008e00, 0x00009400, 0x00009b00, 0x00001e00, 0x00008700, + 0x0000e900, 0x0000ce00, 0x00005500, 0x00002800, 0x0000df00, + 0x00008c00, 0x0000a100, 0x00008900, 0x00000d00, 0x0000bf00, + 0x0000e600, 0x00004200, 0x00006800, 0x00004100, 0x00009900, + 0x00002d00, 0x00000f00, 0x0000b000, 0x00005400, 0x0000bb00, + 0x00001600, + }, + { + 0x00630000, 0x007c0000, 0x00770000, 0x007b0000, 0x00f20000, + 0x006b0000, 0x006f0000, 0x00c50000, 0x00300000, 0x00010000, + 0x00670000, 0x002b0000, 0x00fe0000, 0x00d70000, 0x00ab0000, + 0x00760000, 0x00ca0000, 0x00820000, 0x00c90000, 0x007d0000, + 0x00fa0000, 0x00590000, 0x00470000, 0x00f00000, 0x00ad0000, + 0x00d40000, 0x00a20000, 0x00af0000, 0x009c0000, 0x00a40000, + 0x00720000, 0x00c00000, 0x00b70000, 0x00fd0000, 0x00930000, + 0x00260000, 0x00360000, 0x003f0000, 0x00f70000, 0x00cc0000, + 0x00340000, 0x00a50000, 0x00e50000, 0x00f10000, 0x00710000, + 0x00d80000, 0x00310000, 0x00150000, 0x00040000, 0x00c70000, + 0x00230000, 0x00c30000, 0x00180000, 0x00960000, 0x00050000, + 0x009a0000, 0x00070000, 0x00120000, 0x00800000, 0x00e20000, + 0x00eb0000, 0x00270000, 0x00b20000, 0x00750000, 0x00090000, + 0x00830000, 0x002c0000, 0x001a0000, 0x001b0000, 0x006e0000, + 0x005a0000, 0x00a00000, 0x00520000, 0x003b0000, 0x00d60000, + 0x00b30000, 0x00290000, 0x00e30000, 0x002f0000, 0x00840000, + 0x00530000, 0x00d10000, 0x00000000, 0x00ed0000, 0x00200000, + 0x00fc0000, 0x00b10000, 0x005b0000, 0x006a0000, 0x00cb0000, + 0x00be0000, 0x00390000, 0x004a0000, 0x004c0000, 0x00580000, + 0x00cf0000, 0x00d00000, 0x00ef0000, 0x00aa0000, 0x00fb0000, + 0x00430000, 0x004d0000, 0x00330000, 0x00850000, 0x00450000, + 0x00f90000, 0x00020000, 0x007f0000, 0x00500000, 0x003c0000, + 0x009f0000, 0x00a80000, 0x00510000, 0x00a30000, 0x00400000, + 0x008f0000, 0x00920000, 0x009d0000, 0x00380000, 0x00f50000, + 0x00bc0000, 0x00b60000, 0x00da0000, 0x00210000, 0x00100000, + 0x00ff0000, 0x00f30000, 0x00d20000, 0x00cd0000, 0x000c0000, + 0x00130000, 0x00ec0000, 0x005f0000, 0x00970000, 0x00440000, + 0x00170000, 0x00c40000, 0x00a70000, 0x007e0000, 0x003d0000, + 0x00640000, 0x005d0000, 0x00190000, 0x00730000, 0x00600000, + 0x00810000, 0x004f0000, 0x00dc0000, 0x00220000, 0x002a0000, + 0x00900000, 0x00880000, 0x00460000, 0x00ee0000, 0x00b80000, + 0x00140000, 0x00de0000, 0x005e0000, 0x000b0000, 0x00db0000, + 0x00e00000, 0x00320000, 0x003a0000, 0x000a0000, 0x00490000, + 0x00060000, 0x00240000, 0x005c0000, 0x00c20000, 0x00d30000, + 0x00ac0000, 0x00620000, 0x00910000, 0x00950000, 0x00e40000, + 0x00790000, 0x00e70000, 0x00c80000, 0x00370000, 0x006d0000, + 0x008d0000, 0x00d50000, 0x004e0000, 0x00a90000, 0x006c0000, + 0x00560000, 0x00f40000, 0x00ea0000, 0x00650000, 0x007a0000, + 0x00ae0000, 0x00080000, 0x00ba0000, 0x00780000, 0x00250000, + 0x002e0000, 0x001c0000, 0x00a60000, 0x00b40000, 0x00c60000, + 0x00e80000, 0x00dd0000, 0x00740000, 0x001f0000, 0x004b0000, + 0x00bd0000, 0x008b0000, 0x008a0000, 0x00700000, 0x003e0000, + 0x00b50000, 0x00660000, 0x00480000, 0x00030000, 0x00f60000, + 0x000e0000, 0x00610000, 0x00350000, 0x00570000, 0x00b90000, + 0x00860000, 0x00c10000, 0x001d0000, 0x009e0000, 0x00e10000, + 0x00f80000, 0x00980000, 0x00110000, 0x00690000, 0x00d90000, + 0x008e0000, 0x00940000, 0x009b0000, 0x001e0000, 0x00870000, + 0x00e90000, 0x00ce0000, 0x00550000, 0x00280000, 0x00df0000, + 0x008c0000, 0x00a10000, 0x00890000, 0x000d0000, 0x00bf0000, + 0x00e60000, 0x00420000, 0x00680000, 0x00410000, 0x00990000, + 0x002d0000, 0x000f0000, 0x00b00000, 0x00540000, 0x00bb0000, + 0x00160000, + }, + { + 0x63000000, 0x7c000000, 0x77000000, 0x7b000000, 0xf2000000, + 0x6b000000, 0x6f000000, 0xc5000000, 0x30000000, 0x01000000, + 0x67000000, 0x2b000000, 0xfe000000, 0xd7000000, 0xab000000, + 0x76000000, 0xca000000, 0x82000000, 0xc9000000, 0x7d000000, + 0xfa000000, 0x59000000, 0x47000000, 0xf0000000, 0xad000000, + 0xd4000000, 0xa2000000, 0xaf000000, 0x9c000000, 0xa4000000, + 0x72000000, 0xc0000000, 0xb7000000, 0xfd000000, 0x93000000, + 0x26000000, 0x36000000, 0x3f000000, 0xf7000000, 0xcc000000, + 0x34000000, 0xa5000000, 0xe5000000, 0xf1000000, 0x71000000, + 0xd8000000, 0x31000000, 0x15000000, 0x04000000, 0xc7000000, + 0x23000000, 0xc3000000, 0x18000000, 0x96000000, 0x05000000, + 0x9a000000, 0x07000000, 0x12000000, 0x80000000, 0xe2000000, + 0xeb000000, 0x27000000, 0xb2000000, 0x75000000, 0x09000000, + 0x83000000, 0x2c000000, 0x1a000000, 0x1b000000, 0x6e000000, + 0x5a000000, 0xa0000000, 0x52000000, 0x3b000000, 0xd6000000, + 0xb3000000, 0x29000000, 0xe3000000, 0x2f000000, 0x84000000, + 0x53000000, 0xd1000000, 0x00000000, 0xed000000, 0x20000000, + 0xfc000000, 0xb1000000, 0x5b000000, 0x6a000000, 0xcb000000, + 0xbe000000, 0x39000000, 0x4a000000, 0x4c000000, 0x58000000, + 0xcf000000, 0xd0000000, 0xef000000, 0xaa000000, 0xfb000000, + 0x43000000, 0x4d000000, 0x33000000, 0x85000000, 0x45000000, + 0xf9000000, 0x02000000, 0x7f000000, 0x50000000, 0x3c000000, + 0x9f000000, 0xa8000000, 0x51000000, 0xa3000000, 0x40000000, + 0x8f000000, 0x92000000, 0x9d000000, 0x38000000, 0xf5000000, + 0xbc000000, 0xb6000000, 0xda000000, 0x21000000, 0x10000000, + 0xff000000, 0xf3000000, 0xd2000000, 0xcd000000, 0x0c000000, + 0x13000000, 0xec000000, 0x5f000000, 0x97000000, 0x44000000, + 0x17000000, 0xc4000000, 0xa7000000, 0x7e000000, 0x3d000000, + 0x64000000, 0x5d000000, 0x19000000, 0x73000000, 0x60000000, + 0x81000000, 0x4f000000, 0xdc000000, 0x22000000, 0x2a000000, + 0x90000000, 0x88000000, 0x46000000, 0xee000000, 0xb8000000, + 0x14000000, 0xde000000, 0x5e000000, 0x0b000000, 0xdb000000, + 0xe0000000, 0x32000000, 0x3a000000, 0x0a000000, 0x49000000, + 0x06000000, 0x24000000, 0x5c000000, 0xc2000000, 0xd3000000, + 0xac000000, 0x62000000, 0x91000000, 0x95000000, 0xe4000000, + 0x79000000, 0xe7000000, 0xc8000000, 0x37000000, 0x6d000000, + 0x8d000000, 0xd5000000, 0x4e000000, 0xa9000000, 0x6c000000, + 0x56000000, 0xf4000000, 0xea000000, 0x65000000, 0x7a000000, + 0xae000000, 0x08000000, 0xba000000, 0x78000000, 0x25000000, + 0x2e000000, 0x1c000000, 0xa6000000, 0xb4000000, 0xc6000000, + 0xe8000000, 0xdd000000, 0x74000000, 0x1f000000, 0x4b000000, + 0xbd000000, 0x8b000000, 0x8a000000, 0x70000000, 0x3e000000, + 0xb5000000, 0x66000000, 0x48000000, 0x03000000, 0xf6000000, + 0x0e000000, 0x61000000, 0x35000000, 0x57000000, 0xb9000000, + 0x86000000, 0xc1000000, 0x1d000000, 0x9e000000, 0xe1000000, + 0xf8000000, 0x98000000, 0x11000000, 0x69000000, 0xd9000000, + 0x8e000000, 0x94000000, 0x9b000000, 0x1e000000, 0x87000000, + 0xe9000000, 0xce000000, 0x55000000, 0x28000000, 0xdf000000, + 0x8c000000, 0xa1000000, 0x89000000, 0x0d000000, 0xbf000000, + 0xe6000000, 0x42000000, 0x68000000, 0x41000000, 0x99000000, + 0x2d000000, 0x0f000000, 0xb0000000, 0x54000000, 0xbb000000, + 0x16000000, + }, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline uint8_t get_byte(const uint32_t x, const unsigned n) +{ + return x >> (n << 3); +} + +/* initialise the key schedule from the user supplied key */ + +static uint32_t aes_ror32(uint32_t word, unsigned int shift) +{ + return (word >> shift) | (word << (32 - shift)); +} + +/* aes_expand_key - Expands the AES key as described in FIPS-197 + * @ctx: The location where the computed key will be stored. + * @in_key: The supplied key. + * @key_len: The length of the supplied key. + * + * Returns 0 on success. The function fails only if an invalid key size (or + * pointer) is supplied. + * + * The expanded key size is 240 bytes (max of 14 rounds with a unique 16 + * bytes key schedule plus a 16 bytes key which is used before the first + * round). The decryption key is prepared for the "Equivalent Inverse Cipher" + * as described in FIPS-197. The first slot (16 bytes) of each key (enc or + * dec) is for the initial combination, the second slot for the first round + * and so on. + */ + +static int aes_expand_key(aes_context_t *ctx, const uint8_t *in_key, + unsigned int key_len) +{ + const uint32_t *key = (const uint32_t *)in_key; + uint32_t i; + uint32_t t; + uint32_t u; + uint32_t v; + uint32_t w; + uint32_t j; + + if (key_len != AES_KEYSIZE_128 && key_len != AES_KEYSIZE_192 && + key_len != AES_KEYSIZE_256) + { + return -1; + } + + ctx->key_length = key_len; + + ctx->key_dec[key_len + 24] = ctx->key_enc[0] = le32_to_cpu(key[0]); + ctx->key_dec[key_len + 25] = ctx->key_enc[1] = le32_to_cpu(key[1]); + ctx->key_dec[key_len + 26] = ctx->key_enc[2] = le32_to_cpu(key[2]); + ctx->key_dec[key_len + 27] = ctx->key_enc[3] = le32_to_cpu(key[3]); + + switch (key_len) + { + case AES_KEYSIZE_128: + t = ctx->key_enc[3]; + for (i = 0; i < 10; ++i) + loop4(i); + break; + + case AES_KEYSIZE_192: + ctx->key_enc[4] = le32_to_cpu(key[4]); + t = ctx->key_enc[5] = le32_to_cpu(key[5]); + for (i = 0; i < 8; ++i) + loop6(i); + break; + + case AES_KEYSIZE_256: + ctx->key_enc[4] = le32_to_cpu(key[4]); + ctx->key_enc[5] = le32_to_cpu(key[5]); + ctx->key_enc[6] = le32_to_cpu(key[6]); + t = ctx->key_enc[7] = le32_to_cpu(key[7]); + for (i = 0; i < 6; ++i) + loop8(i); + loop8tophalf(i); + break; + } + + ctx->key_dec[0] = ctx->key_enc[key_len + 24]; + ctx->key_dec[1] = ctx->key_enc[key_len + 25]; + ctx->key_dec[2] = ctx->key_enc[key_len + 26]; + ctx->key_dec[3] = ctx->key_enc[key_len + 27]; + + for (i = 4; i < key_len + 24; ++i) + { + j = key_len + 24 - (i & ~3) + (i & 3); + imix_col(ctx->key_dec[j], ctx->key_enc[i]); + } + + return 0; +} + +/* aes_set_key - Set the AES key. + * @ctx: AES context struct. + * @in_key: The input key. + * @key_len: The size of the key. + * + * Returns 0 on success, on failure -1 is returned. + * The function uses aes_expand_key() to expand the key. + */ + +static int aes_set_key(aes_context_t *ctx, const uint8_t *in_key, + unsigned int key_len) +{ + int ret; + + ret = aes_expand_key(ctx, in_key, key_len); + if (!ret) return 0; + + return -1; +} + +static void aes_encrypt(aes_context_t *ctx, uint8_t *out, const uint8_t *in) +{ + const uint32_t *src = (const uint32_t *)in; + uint32_t *dst = (uint32_t *)out; + uint32_t b0[4]; + uint32_t b1[4]; + const uint32_t *kp = ctx->key_enc + 4; + const int key_len = ctx->key_length; + + b0[0] = le32_to_cpu(src[0]) ^ ctx->key_enc[0]; + b0[1] = le32_to_cpu(src[1]) ^ ctx->key_enc[1]; + b0[2] = le32_to_cpu(src[2]) ^ ctx->key_enc[2]; + b0[3] = le32_to_cpu(src[3]) ^ ctx->key_enc[3]; + + if (key_len > 24) + { + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + } + + if (key_len > 16) + { + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + } + + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + f_nround(b1, b0, kp); + f_lround(b0, b1, kp); + + dst[0] = cpu_to_le32(b0[0]); + dst[1] = cpu_to_le32(b0[1]); + dst[2] = cpu_to_le32(b0[2]); + dst[3] = cpu_to_le32(b0[3]); +} + +/* Generate a set of new PRNG values by encrypting a new block. */ + +static void prng_generate(void) +{ + byte input[16]; + byte output[16]; + unsigned int i; + + /* Input for the cipher is a consecutively increasing 32-bit counter. */ + + for (i = 0; i < 4; ++i) + { + input[4 * i] = prng_input_counter & 0xff; + input[4 * i + 1] = (prng_input_counter >> 8) & 0xff; + input[4 * i + 2] = (prng_input_counter >> 16) & 0xff; + input[4 * i + 3] = (prng_input_counter >> 24) & 0xff; + ++prng_input_counter; + } + + aes_encrypt(&prng_context, output, input); + + for (i = 0; i < 4; ++i) + { + prng_values[i] = output[4 * i] | (output[4 * i + 1] << 8) | + (output[4 * i + 2] << 16) | (output[4 * i + 3] << 24); + } + + prng_value_index = 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Initialize Pseudo-RNG using the specified 128-bit key. */ + +void prng_start(prng_seed_t key) +{ + aes_set_key(&prng_context, key, sizeof(prng_seed_t)); + prng_value_index = 4; + prng_input_counter = 0; + prng_enabled = true; +} + +void prng_stop(void) +{ + prng_enabled = false; +} + +/* Read a random 32-bit integer from the PRNG. */ + +unsigned int prng_random(void) +{ + unsigned int result; + + if (!prng_enabled) + { + return 0; + } + + if (prng_value_index >= 4) + { + prng_generate(); + } + + result = prng_values[prng_value_index]; + ++prng_value_index; + + return result; +} diff --git a/games/NXDoom/src/aes_prng.h b/games/NXDoom/src/aes_prng.h new file mode 100644 index 00000000000..eed8dd78c60 --- /dev/null +++ b/games/NXDoom/src/aes_prng.h @@ -0,0 +1,44 @@ +/**************************************************************************** + * apps/games/NXDoom/src/aes_prng.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Pseudo-random number generator for secure demos. + * + ****************************************************************************/ + +#ifndef __AES_PRNG_H__ +#define __AES_PRNG_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/* Nonce value used as random seed for secure demos. */ + +typedef byte prng_seed_t[16]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void prng_start(prng_seed_t seed); +void prng_stop(void); +unsigned int prng_random(void); + +#endif /* __AES_PRNG_H__ */ diff --git a/games/NXDoom/src/config.h b/games/NXDoom/src/config.h new file mode 100644 index 00000000000..b6427530015 --- /dev/null +++ b/games/NXDoom/src/config.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * apps/games/NXDoom/src/config.h + * + * SPDX-License-Identifer: GPLv2 + * + * NuttX compatible config.h file. + * + * TODO: can we take a cleaner approach later by baking this into logic? + * + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HAVE_DECL_STRCASECMP 1 +#define HAVE_DECL_STRNCASECMP 1 + +#define PACKAGE_TARNAME "nxdoom" +#define PACKAGE_NAME "NXDoom" +#define PACKAGE_STRING "NXDoom v0.0.0" +#define PROGRAM_PREFIX "" + +#define DISABLE_SDL2MIXER +#define DISABLE_SDL2NET diff --git a/games/NXDoom/src/d_dedicated.c b/games/NXDoom/src/d_dedicated.c new file mode 100644 index 00000000000..307010f34e6 --- /dev/null +++ b/games/NXDoom/src/d_dedicated.c @@ -0,0 +1,60 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_dedicated.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Code specific to the standalone dedicated server. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "config.h" + +#include "m_argv.h" +#include "net_defs.h" + +#include "net_dedicated.h" +#include "net_server.h" +#include "z_zone.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void net_cl_run(void) +{ + /* No client present :-) + * + * This is here because the server code sometimes runs this + * to let the client do some processing if it needs to. + * In a standalone dedicated server, we don't have a client. + */ +} + +void d_doom_main(void) +{ + printf(PACKAGE_NAME " standalone dedicated server\n"); + + z_init(); + + net_dedicated_server(); +} diff --git a/games/NXDoom/src/d_event.c b/games/NXDoom/src/d_event.c new file mode 100644 index 00000000000..0b2a29e6ce3 --- /dev/null +++ b/games/NXDoom/src/d_event.c @@ -0,0 +1,81 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_event.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: Event handling. + * + * Events are asynchronous inputs generally generated by the game user. + * Events can be discarded if no responder claims them + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAXEVENTS 64 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static event_t events[MAXEVENTS]; +static int eventhead; +static int eventtail; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* d_post_event + * Called by the I/O functions when input is detected + */ + +void d_post_event(event_t *ev) +{ + events[eventhead] = *ev; + eventhead = (eventhead + 1) % MAXEVENTS; +} + +/* Read an event from the queue. */ + +event_t *d_pop_event(void) +{ + event_t *result; + + /* No more events waiting. */ + + if (eventtail == eventhead) + { + return NULL; + } + + result = &events[eventtail]; + + /* Advance to the next event in the queue. */ + + eventtail = (eventtail + 1) % MAXEVENTS; + + return result; +} diff --git a/games/NXDoom/src/d_event.h b/games/NXDoom/src/d_event.h new file mode 100644 index 00000000000..f1f43133ff9 --- /dev/null +++ b/games/NXDoom/src/d_event.h @@ -0,0 +1,192 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_event.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * + ****************************************************************************/ + +#ifndef __D_EVENT__ +#define __D_EVENT__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Event handling. */ + +/* Input event types. */ + +typedef enum +{ + /* Key press/release events. + * data1: Key code (from doomkeys.h) of the key that was + * pressed or released. This is the key as it appears + * on a US keyboard layout, and does not change with + * layout. + * For ev_keydown only: + * data2: ASCII representation of the key that was pressed that + * changes with the keyboard layout; eg. if 'Z' is + * pressed on a German keyboard, data1='y',data2='z'. + * Not affected by modifier keys. + * data3: ASCII input, fully modified according to keyboard + * layout and any modifier keys that are held down. + * Only set if i_start_text_input() has been called. + */ + + ev_keydown, + ev_keyup, + + /* Mouse movement event. + * data1: Bitfield of buttons currently held down. + * (bit 0 = left; bit 1 = right; bit 2 = middle). + * data2: X axis mouse movement (turn). + * data3: Y axis mouse movement (forward/backward). + */ + + ev_mouse, + + /* Joystick state. + * data1: Bitfield of buttons currently pressed. + * data2: X axis mouse movement (turn). + * data3: Y axis mouse movement (forward/backward). + * data4: Third axis mouse movement (strafe). + * data5: Fourth axis mouse movement (look) + * data6: Dpad and analog stick direction. + */ + + ev_joystick, + + /* Quit event. Triggered when the user clicks the "close" button + * to terminate the application. + */ + + ev_quit +} evtype_t; + +/* Event structure. */ + +typedef struct +{ + evtype_t type; + + /* Event-specific data; see the descriptions given above. */ + + int data1; + int data2; + int data3; + int data4; + int data5; + int data6; +} event_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; + +/* villsa [STRIFE] Strife specific buttons + * TODO - not finished + */ + +typedef enum +{ + /* Player view look up */ + + BT2_LOOKUP = 1, + + /* Player view look down */ + + BT2_LOOKDOWN = 2, + + /* Center player's view */ + + BT2_CENTERVIEW = 4, + + /* Use inventory item */ + + BT2_INVUSE = 8, + + /* Drop inventory item */ + + BT2_INVDROP = 16, + + /* Jump up and down */ + + BT2_JUMP = 32, + + /* Use medkit */ + + BT2_HEALTH = 128, +} buttoncode2_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Called by IO functions when input is detected. */ + +void d_post_event(event_t *ev); + +/* Read an event from the event queue */ + +event_t *d_pop_event(void); + +#endif /* __D_EVENT__ */ diff --git a/games/NXDoom/src/d_iwad.c b/games/NXDoom/src/d_iwad.c new file mode 100644 index 00000000000..a6f1cd9453d --- /dev/null +++ b/games/NXDoom/src/d_iwad.c @@ -0,0 +1,539 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_iwad.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Search for and locate an IWAD file, and initialize according + * to the IWAD type. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "d_iwad.h" +#include "deh_str.h" +#include "doomkeys.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Array of locations to search for IWAD files + * + * "128 IWAD search directories should be enough for anybody". + */ + +#define MAX_IWAD_DIRS 128 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const iwad_t g_iwads[] = +{ + {"doom2.wad", doom2, commercial, "Doom II"}, + {"plutonia.wad", pack_plut, commercial, "Final Doom: Plutonia Experiment"}, + {"tnt.wad", pack_tnt, commercial, "Final Doom: TNT: Evilution"}, + {"doom.wad", doom, retail, "Doom"}, + {"doom1.wad", doom, shareware, "Doom Shareware"}, + {"doom2f.wad", doom2, commercial, "Doom II: L'Enfer sur Terre"}, + {"chex.wad", pack_chex, retail, "Chex Quest"}, + {"hacx.wad", pack_hacx, commercial, "Hacx"}, + {"freedoom2.wad", doom2, commercial, "Freedoom: Phase 2"}, + {"freedoom1.wad", doom, retail, "Freedoom: Phase 1"}, + {"freedm.wad", doom2, commercial, "FreeDM"}, + {"heretic.wad", heretic, retail, "Heretic"}, + {"heretic1.wad", heretic, shareware, "Heretic Shareware"}, + {"hexen.wad", hexen, commercial, "Hexen"}, + {"strife1.wad", strife, commercial, "Strife"}, +}; + +static boolean iwad_dirs_built = false; +static const char *iwad_dirs[MAX_IWAD_DIRS]; +static int num_iwad_dirs = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void add_iwad_dir(const char *dir) +{ + if (num_iwad_dirs < MAX_IWAD_DIRS) + { + iwad_dirs[num_iwad_dirs] = dir; + ++num_iwad_dirs; + } +} + +/* Returns true if the specified path is a path to a file + * of the specified name. + */ + +static boolean dir_is_file(const char *path, const char *filename) +{ + return strchr(path, DIR_SEPARATOR) != NULL && + !strcasecmp(m_base_name(path), filename); +} + +/* Check if the specified directory contains the specified IWAD + * file, returning the full path to the IWAD if found, or NULL + * if not found. + */ + +static char *check_dir_has_iwad(const char *dir, const char *iwadname) +{ + char *filename; + char *probe; + + /* As a special case, the "directory" may refer directly to an + * IWAD file if the path comes from DOOMWADDIR or DOOMWADPATH. + */ + + probe = m_file_case_exists(dir); + if (dir_is_file(dir, iwadname) && probe != NULL) + { + return probe; + } + + /* Construct the full path to the IWAD if it is located in + * this directory, and check if it exists. + */ + + if (!strcmp(dir, ".")) + { + filename = m_string_duplicate(iwadname); + } + else + { + filename = m_string_join(dir, DIR_SEPARATOR_S, iwadname, NULL); + } + + free(probe); + probe = m_file_case_exists(filename); + free(filename); + + if (probe != NULL) + { + return probe; + } + + return NULL; +} + +/* Search a directory to try to find an IWAD + * Returns the location of the IWAD if found, otherwise NULL. + */ + +static char *search_dir_for_iwad(const char *dir, int mask, + gamemission_t *mission) +{ + char *filename; + size_t i; + + for (i = 0; i < arrlen(g_iwads); ++i) + { + if (((1 << g_iwads[i].mission) & mask) == 0) + { + continue; + } + + filename = check_dir_has_iwad(dir, (g_iwads[i].name)); + + if (filename != NULL) + { + *mission = g_iwads[i].mission; + + return filename; + } + } + + return NULL; +} + +/* When given an IWAD with the '-iwad' parameter, + * attempt to identify it by its name. + */ + +static gamemission_t identify_iwad_by_name(const char *name, int mask) +{ + size_t i; + gamemission_t mission; + + name = m_base_name(name); + mission = none; + + for (i = 0; i < arrlen(g_iwads); ++i) + { + /* Check if the filename is this IWAD name. + * Only use supported missions: + */ + + if (((1 << g_iwads[i].mission) & mask) == 0) continue; + + /* Check if it ends in this IWAD name. */ + + if (!strcasecmp(name, g_iwads[i].name)) + { + mission = g_iwads[i].mission; + break; + } + } + + return mission; +} + +/* Add IWAD directories parsed from splitting a path string containing + * paths separated by PATH_SEPARATOR. 'suffix' is a string to concatenate + * to the end of the paths before adding them. + */ + +static void add_iwad_path(const char *path, const char *suffix) +{ + char *left; + char *p; + char *dup_path; + + dup_path = m_string_duplicate(path); + + /* Split into individual dirs within the list. */ + + left = dup_path; + + for (; ; ) + { + p = strchr(left, PATH_SEPARATOR); + if (p != NULL) + { + /* Break at the separator and use the left hand side + * as another iwad dir + */ + + *p = '\0'; + + add_iwad_dir(m_string_join(left, suffix, NULL)); + left = p + 1; + } + else + { + break; + } + } + + add_iwad_dir(m_string_join(left, suffix, NULL)); + + free(dup_path); +} + +/* Build a list of IWAD files */ + +static void buld_iwad_dir_list(void) +{ + char *env; + + if (iwad_dirs_built) + { + return; + } + + /* Look in the current directory. Doom always does this. */ + + add_iwad_dir("."); + + /* Next check the directory where the executable is located. This might + * be different from the current directory. + */ + + add_iwad_dir(m_dir_name(myargv[0])); + + /* Add DOOMWADDIR if it is in the environment */ + + env = getenv("DOOMWADDIR"); + if (env != NULL) + { + add_iwad_dir(env); + } + + /* Add dirs from DOOMWADPATH: */ + + env = getenv("DOOMWADPATH"); + if (env != NULL) + { + add_iwad_path(env, ""); + } + + /* Don't run this function again. */ + + iwad_dirs_built = true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Searches WAD search paths for an WAD with a specific filename. */ + +char *d_find_wad_by_name(const char *name) +{ + char *path; + char *probe; + int i; + + /* Absolute path? */ + + probe = m_file_case_exists(name); + if (probe != NULL) + { + return probe; + } + + buld_iwad_dir_list(); + + /* Search through all IWAD paths for a file with the given name. */ + + for (i = 0; i < num_iwad_dirs; ++i) + { + /* As a special case, if this is in DOOMWADDIR or DOOMWADPATH, + * the "directory" may actually refer directly to an IWAD + * file. + */ + + probe = m_file_case_exists(iwad_dirs[i]); + if (dir_is_file(iwad_dirs[i], name) && probe != NULL) + { + return probe; + } + + free(probe); + + /* Construct a string for the full path */ + + path = m_string_join(iwad_dirs[i], DIR_SEPARATOR_S, name, NULL); + + probe = m_file_case_exists(path); + if (probe != NULL) + { + return probe; + } + + free(path); + } + + /* File not found */ + + return NULL; +} + +/* d_try_find_wad_by_name + * + * Searches for a WAD by its filename, or returns a copy of the filename + * if not found. + */ + +char *d_try_find_wad_by_name(const char *filename) +{ + char *result; + + result = d_find_wad_by_name(filename); + + if (result != NULL) + { + return result; + } + else + { + return m_string_duplicate(filename); + } +} + +/* FindIWAD + * Checks availability of IWAD files by name, + * to determine whether registered/commercial features + * should be executed (notably loading PWADs). + */ + +char *d_find_iwad(int mask, gamemission_t *mission) +{ + char *result; + const char *iwadfile; + int iwadparm; + int i; + + /* Check for the -iwad parameter + * + * Specify an IWAD file to use. + * + * @arg + * + */ + + iwadparm = m_check_parm_with_args("-iwad", 1); + + if (iwadparm) + { + /* Search through IWAD dirs for an IWAD with the given name. */ + + iwadfile = myargv[iwadparm + 1]; + + result = d_find_wad_by_name(iwadfile); + + if (result == NULL) + { + i_error("IWAD file '%s' not found!", iwadfile); + } + + *mission = identify_iwad_by_name(result, mask); + } + else + { + /* Search through the list and look for an IWAD */ + + result = NULL; + + buld_iwad_dir_list(); + + for (i = 0; result == NULL && i < num_iwad_dirs; ++i) + { + result = search_dir_for_iwad(iwad_dirs[i], mask, mission); + } + } + + return result; +} + +/* Find all IWADs in the IWAD search path matching the given mask. */ + +const iwad_t **d_find_all_iwads(int mask) +{ + const iwad_t **result; + int result_len; + char *filename; + int i; + + result = malloc(sizeof(iwad_t *) * (arrlen(g_iwads) + 1)); + result_len = 0; + + /* Try to find all IWADs */ + + for (i = 0; i < arrlen(g_iwads); ++i) + { + if (((1 << g_iwads[i].mission) & mask) == 0) + { + continue; + } + + filename = d_find_wad_by_name(g_iwads[i].name); + + if (filename != NULL) + { + result[result_len] = &g_iwads[i]; + ++result_len; + } + } + + /* End of list */ + + result[result_len] = NULL; + + return result; +} + +/* Get the IWAD name used for savegames. */ + +const char *d_save_game_iwad_name(gamemission_t gamemission, + game_variant_t gamevariant) +{ + size_t i; + + /* Determine the IWAD name to use for savegames. + * This determines the directory the savegame files get put into. + * + * Note that we match on gamemission rather than on IWAD name. + * This ensures that doom1.wad and doom.wad saves are stored + * in the same place. + */ + + if (gamevariant == freedoom) + { + if (gamemission == doom) + { + return "freedoom1.wad"; + } + else if (gamemission == doom2) + { + return "freedoom2.wad"; + } + } + else if (gamevariant == freedm && gamemission == doom2) + { + return "freedm.wad"; + } + + for (i = 0; i < arrlen(g_iwads); ++i) + { + if (gamemission == g_iwads[i].mission) + { + return g_iwads[i].name; + } + } + + /* Default fallback: */ + + return "unknown.wad"; +} + +const char *d_suggest_iwad_name(gamemission_t mission, game_mode_t mode) +{ + int i; + + for (i = 0; i < arrlen(g_iwads); ++i) + { + if (g_iwads[i].mission == mission && g_iwads[i].mode == mode) + { + return g_iwads[i].name; + } + } + + return "unknown.wad"; +} + +const char *d_suggest_game_name(gamemission_t mission, game_mode_t mode) +{ + int i; + + for (i = 0; i < arrlen(g_iwads); ++i) + { + if (g_iwads[i].mission == mission && + (mode == indetermined || g_iwads[i].mode == mode)) + { + return g_iwads[i].description; + } + } + + return "Unknown game?"; +} diff --git a/games/NXDoom/src/d_iwad.h b/games/NXDoom/src/d_iwad.h new file mode 100644 index 00000000000..bb2681c8b7b --- /dev/null +++ b/games/NXDoom/src/d_iwad.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_iwad.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Find IWAD and initialize according to IWAD type. + * + ****************************************************************************/ + +#ifndef __D_IWAD__ +#define __D_IWAD__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_mode.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IWAD_MASK_DOOM \ + ((1 << doom) | (1 << doom2) | (1 << pack_tnt) | (1 << pack_plut) | \ + (1 << pack_chex) | (1 << pack_hacx)) +#define IWAD_MASK_HERETIC (1 << heretic) +#define IWAD_MASK_HEXEN (1 << hexen) +#define IWAD_MASK_STRIFE (1 << strife) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct +{ + const char *name; + gamemission_t mission; + game_mode_t mode; + const char *description; +} iwad_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +char *d_find_wad_by_name(const char *filename); +char *d_try_find_wad_by_name(const char *filename); +char *d_find_iwad(int mask, gamemission_t *mission); +const iwad_t **d_find_all_iwads(int mask); +const char *d_save_game_iwad_name(gamemission_t gamemission, + game_variant_t gamevariant); +const char *d_suggest_iwad_name(gamemission_t mission, game_mode_t mode); +const char *d_suggest_game_name(gamemission_t mission, game_mode_t mode); + +#endif /* __D_IWAD__ */ diff --git a/games/NXDoom/src/d_loop.c b/games/NXDoom/src/d_loop.c new file mode 100644 index 00000000000..28b00dfaa3e --- /dev/null +++ b/games/NXDoom/src/d_loop.c @@ -0,0 +1,955 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_loop.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Main loop code. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "d_event.h" +#include "d_loop.h" +#include "d_ticcmd.h" + +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" + +#include "m_argv.h" +#include "m_fixed.h" + +#ifdef CONFIG_GAMES_NXDOOM_NET +#include "net_client.h" +#include "net_gui.h" +#include "net_io.h" +#include "net_loop.h" +#include "net_query.h" +#include "net_sdl.h" +#include "net_server.h" +#endif + +/* TODO: Move nonvanilla demo functions into a dedicated file. */ + +#include "m_misc.h" +#include "w_wad.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Maximum time that we wait in try_run_tics() for netgame data to be + * received before we bail out and render a frame anyway. + * Vanilla Doom used 20 for this value, but we use a smaller value + * instead for better responsiveness of the menu when we're stuck. + */ + +#define MAX_NETGAME_STALL_TICS 5 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The complete set of data for a particular tic. */ + +typedef struct +{ + ticcmd_t cmds[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; + boolean ingame[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +} ticcmd_set_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* gametic is the tic about to (or currently being) run + * maketic is the tic that hasn't had control made for it yet + * recvtic is the latest tic received from the server. + * + * a gametic cannot be run until ticcmds are received for it + * from all players. + */ + +static ticcmd_set_t ticdata[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + +/* The index of the next tic to be made (with a call to build_ticcmd). */ + +static int maketic; + +/* The number of complete tics received from the server so far. */ + +static int recvtic; + +/* Index of the local player. */ + +static int localplayer; + +/* Used for original sync code. */ + +static int skiptics = 0; + +/* Use new client synchronization code */ + +static boolean new_sync = true; + +/* Callback functions for loop code. */ + +static loop_interface_t *loop_interface = NULL; + +/* Current players in the multiplayer game. + * This is distinct from playeringame[] used by the game code, which may + * modify playeringame[] when playing back multiplayer demos. + */ + +static boolean local_playeringame[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; + +/* Requested player class "sent" to the server on connect. + * If we are only doing a single player game then this needs to be remembered + * and saved in the game settings. + */ + +static int player_class; + +#ifndef CONFIG_GAMES_NXDOOM_NET +static boolean net_client_connected = false; +static boolean drone = false; +#endif + +static int frameon; +static int frameskip[4]; +static int oldnettics; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* The number of tics that have been run (using run_tic) so far. */ + +int gametic; + +/* When set to true, a single tic is run each time try_run_tics() is called. + * This is used for -timedemo mode. + */ + +boolean singletics = false; + +/* Reduce the bandwidth needed by sampling game input less and transmitting + * less. If ticdup is 2, sample half normal, 3 = one third normal, etc. + */ + +int ticdup; + +/* Amount to offset the timer for game sync. */ + +fixed_t offsetms; + +int lasttime; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* 35 fps clock adjusted by offsetms milliseconds */ + +static int get_adjusted_time(void) +{ + int time_ms; + + time_ms = i_get_time_ms(); + + if (new_sync) + { + /* Use the adjustments from net_client.c only if we are + * using the new sync mode. + */ + + time_ms += (offsetms / FRACUNIT); + } + + return (time_ms * TICRATE) / 1000; +} + +static boolean build_new_tic(void) +{ + int gameticdiv; + ticcmd_t cmd; + + gameticdiv = gametic / ticdup; + + i_start_tic(); + loop_interface->process_events(); + + /* Always run the menu */ + + loop_interface->run_menu(); + + if (drone) + { + /* In drone mode, do not generate any ticcmds. */ + + return false; + } + + if (new_sync) + { + /* If playing single player, do not allow tics to buffer + * up very far + */ + + if (!net_client_connected && maketic - gameticdiv > 2) return false; + + /* Never go more than ~200ms ahead */ + + if (maketic - gameticdiv > 8) return false; + } + else + { + if (maketic - gameticdiv >= 5) return false; + } + + /* printf ("mk:%i ",maketic); */ + + memset(&cmd, 0, sizeof(ticcmd_t)); + loop_interface->build_ticcmd(&cmd, maketic); + +#ifdef CONFIG_GAMES_NXDOOM_NET + if (net_client_connected) + { + net_cl_send_ticcmd(&cmd, maketic); + } +#endif + + ticdata[maketic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].cmds[localplayer] = + cmd; + ticdata[maketic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].ingame[localplayer] = + true; + + ++maketic; + + return true; +} + +static boolean strict_demos(void) +{ + /* @category demo + * + * When recording or playing back demos, disable any extensions + * of the vanilla demo format - record demos as vanilla would do, + * and play back demos as vanilla would do. + * + */ + + return m_parm_exists("-strictdemos"); +} + +static void d_disconnected(void) +{ + /* In drone mode, the game cannot continue once disconnected. */ + + if (drone) + { + i_error("Disconnected from server in drone mode."); + } + + /* disconnected from server */ + + printf("Disconnected from server.\n"); +} + +static int get_low_tic(void) +{ + int lowtic; + + lowtic = maketic; + + if (net_client_connected) + { + if (drone || recvtic < lowtic) + { + lowtic = recvtic; + } + } + + return lowtic; +} + +static void old_net_sync(void) +{ + unsigned int i; + int keyplayer = -1; + + frameon++; + + /* ideally maketic should be 1 - 3 tics above lowtic + * if we are consistently slower, speed up time + */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; i++) + { + if (local_playeringame[i]) + { + keyplayer = i; + break; + } + } + + if (keyplayer < 0) + { + /* If there are no players, we can never advance anyway */ + + return; + } + + if (localplayer == keyplayer) + { + /* the key player does not adapt */ + } + else + { + if (maketic <= recvtic) + { + lasttime--; + } + + frameskip[frameon & 3] = oldnettics > recvtic; + oldnettics = maketic; + + if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) + { + skiptics = 1; + } + } +} + +/* Returns true if there are players in the game: */ + +static boolean players_in_game(void) +{ + boolean result = false; + unsigned int i; + + /* If we are connected to a server, check if there are any players + * in the game. + */ + + if (net_client_connected) + { + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + result = result || local_playeringame[i]; + } + } + + /* Whether single or multi-player, unless we are running as a drone, + * we are in the game. + */ + + if (!drone) + { + result = true; + } + + return result; +} + +/* When using ticdup, certain values must be cleared out when running + * the duplicate ticcmds. + */ + +static void ticdup_squash(ticcmd_set_t *set) +{ + ticcmd_t *cmd; + unsigned int i; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + cmd = &set->cmds[i]; + cmd->chatchar = 0; + if (cmd->buttons & BT_SPECIAL) cmd->buttons = 0; + } +} + +/* When running in single player mode, clear all the ingame[] array + * except the local player. + */ + +static void single_player_clear(ticcmd_set_t *set) +{ + unsigned int i; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (i != localplayer) + { + set->ingame[i] = false; + } + } +} + +/* Returns true if the given lump number corresponds to data from a .lmp + * file, as opposed to a WAD. + */ + +static boolean is_demo_file(int lumpnum) +{ + char *lower; + boolean result; + + lower = m_string_duplicate(lumpinfo[lumpnum]->wad_file->path); + m_force_lowercase(lower); + result = m_string_ends_with(lower, ".lmp"); + free(lower); + + return result; +} + +/* Block until the game start message is received from the server. */ + +#ifdef CONFIG_GAMES_NXDOOM_NET +static void block_until_start(net_gamesettings_t *settings, + netgame_startup_callback_t callback) +{ + while (!net_cl_get_settings(settings)) + { + net_cl_run(); + net_sv_run(); + + if (!net_client_connected) + { + i_error("Lost connection to server"); + } + + if (callback != NULL && !callback(net_client_wait_data.ready_players, + net_client_wait_data.num_players)) + { + i_error("Netgame startup aborted."); + } + + usleep(100000); + } +} + +/* d_quit_net_game + * + * Called before quitting to leave a net game without hanging the other + * players + * + * Broadcasts special packets to other players to notify of game exit + */ + +static void d_quit_net_game(void) +{ + net_sv_shutdown(); + net_cl_disconnect(); +} +#endif /* CONFIG_GAMES_NXDOOM_NET */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* net_update + * Builds ticcmds for console player, sends out a packet + */ + +void net_update(void) +{ + int nowtime; + int newtics; + int i; + + /* If we are running with singletics (timing a demo), this + * is all done separately. + */ + + if (singletics) return; + + /* Run network subsystems */ + +#ifdef CONFIG_GAMES_NXDOOM_NET + net_cl_run(); + net_sv_run(); +#endif + + /* check time */ + + nowtime = get_adjusted_time() / ticdup; + newtics = nowtime - lasttime; + + lasttime = nowtime; + + if (skiptics <= newtics) + { + newtics -= skiptics; + skiptics = 0; + } + else + { + skiptics -= newtics; + newtics = 0; + } + + /* build new ticcmds for console player */ + + for (i = 0; i < newtics; i++) + { + if (!build_new_tic()) + { + break; + } + } +} + +/* Invoked by the network engine when a complete set of ticcmds is + * available. + */ + +void d_receive_tic(ticcmd_t *ticcmds, boolean *players_mask) +{ + int i; + + /* Disconnected from server? */ + + if (ticcmds == NULL && players_mask == NULL) + { + d_disconnected(); + return; + } + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (!drone && i == localplayer) + { + /* This is us. Don't overwrite it. */ + } + else + { + ticdata[recvtic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].cmds[i] = + ticcmds[i]; + ticdata[recvtic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].ingame[i] = + players_mask[i]; + } + } + + ++recvtic; +} + +/**************************************************************************** + * Name: d_start_game_loop + * + * Description: + * Called after the screen is set but before the game starts running. + * + ****************************************************************************/ + +void d_start_game_loop(void) +{ + lasttime = get_adjusted_time() / ticdup; +} + +void d_start_net_game(net_gamesettings_t *settings, + netgame_startup_callback_t callback) +{ + int i; + + offsetms = 0; + recvtic = 0; + + settings->consoleplayer = 0; + settings->num_players = 1; + settings->player_classes[0] = player_class; + + /* @category net + * + * Use original network client sync code rather than the improved + * sync code. + */ + + settings->new_sync = !m_parm_exists("-oldsync"); + + /* @category net + * @arg + * + * Send n extra tics in every packet as insurance against dropped + * packets. + */ + + i = m_check_parm_with_args("-extratics", 1); + + if (i > 0) + settings->extratics = atoi(myargv[i + 1]); + else + settings->extratics = 1; + + /* @category net + * @arg + * + * Reduce the resolution of the game by a factor of n, reducing + * the amount of network bandwidth needed. + */ + + i = m_check_parm_with_args("-dup", 1); + + if (i > 0) + settings->ticdup = atoi(myargv[i + 1]); + else + settings->ticdup = 1; + +#ifdef CONFIG_GAMES_NXDOOM_NET + if (net_client_connected) + { + /* Send our game settings and block until game start is received + * from the server. + */ + + net_cl_start_game(settings); + block_until_start(settings, callback); + + /* Read the game settings that were received. */ + + net_cl_get_settings(settings); + } + + if (drone) + { + settings->consoleplayer = 0; + } +#endif + + /* Set the local player and playeringame[] values. */ + + localplayer = settings->consoleplayer; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + local_playeringame[i] = i < settings->num_players; + } + + /* Copy settings to global variables. */ + + ticdup = settings->ticdup; + new_sync = settings->new_sync; + + if (ticdup < 1) + { + i_error("d_start_net_game: invalid ticdup value (%d)", ticdup); + } + + /* TODO: Message disabled until we fix new_sync. + * + * if (!new_sync) + * { + * printf("Syncing netgames like Vanilla Doom.\n"); + * } + */ +} + +boolean d_init_net_game(net_connect_data_t *connect_data) +{ +#ifdef CONFIG_GAMES_NXDOOM_NET + boolean result = false; + net_addr_t *addr = NULL; + int i; + + /* Call d_quit_net_game on exit: */ + + i_at_exit(d_quit_net_game, true); +#endif + + player_class = connect_data->player_class; + +#ifdef CONFIG_GAMES_NXDOOM_NET + /* @category net + * + * Start a multiplayer server, listening for connections. + */ + + if (m_check_parm("-server") > 0 || m_check_parm("-privateserver") > 0) + { + net_sv_init(); + net_sv_add_module(&net_loop_server_module); + net_sv_add_module(&net_sdl_module); + net_sv_register_with_master(); + + net_loop_client_module.init_client(); + addr = net_loop_client_module.resolve_address(NULL); + net_reference_address(addr); + } + else + { + /* @category net + * + * Automatically search the local LAN for a multiplayer + * server and join it. + */ + + i = m_check_parm("-autojoin"); + + if (i > 0) + { + addr = net_find_lan_server(); + + if (addr == NULL) + { + i_error("No server found on local LAN"); + } + } + + /* @arg
+ * @category net + * + * Connect to a multiplayer server running on the given + * address. + */ + + i = m_check_parm_with_args("-connect", 1); + + if (i > 0) + { + net_sdl_module.init_client(); + addr = net_sdl_module.resolve_address(myargv[i + 1]); + net_reference_address(addr); + + if (addr == NULL) + { + i_error("Unable to resolve '%s'\n", myargv[i + 1]); + } + } + } + + if (addr != NULL) + { + if (m_check_parm("-drone") > 0) + { + connect_data->drone = true; + } + + if (!net_cl_connect(addr, connect_data)) + { + i_error("d_init_net_game: Failed to connect to %s:\n%s\n", + net_addr_to_string(addr), net_client_reject_reason); + } + + printf("d_init_net_game: Connected to %s\n", net_addr_to_string(addr)); + net_release_address(addr); + + /* Wait for launch message received from server. */ + + net_wait_for_launch(); + + result = true; + } + + return result; +#endif + return false; +} + +/* try_run_tics */ + +void try_run_tics(void) +{ + int i; + int lowtic; + int entertic; + static int oldentertics; + int realtics; + int availabletics; + int counts; + + /* get real tics */ + + entertic = i_get_time() / ticdup; + realtics = entertic - oldentertics; + oldentertics = entertic; + + /* in singletics mode, run a single tic every time this function + * is called. + */ + + if (singletics) + { + build_new_tic(); + } + else + { + net_update(); + } + + lowtic = get_low_tic(); + + availabletics = lowtic - gametic / ticdup; + + /* decide how many tics to run */ + + if (new_sync) + { + counts = availabletics; + } + else + { + /* 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; + + if (net_client_connected) + { + old_net_sync(); + } + } + + if (counts < 1) counts = 1; + + /* wait for new tics if needed */ + + while (!players_in_game() || lowtic < gametic / ticdup + counts) + { + net_update(); + + lowtic = get_low_tic(); + + if (lowtic < gametic / ticdup) + i_error("try_run_tics: lowtic < gametic"); + + /* Still no tics to run? Sleep until some are available. */ + + if (lowtic < gametic / ticdup + counts) + { + /* If we're in a netgame, we might spin forever waiting for + * new network data to be received. So don't stay in here + * forever - give the menu a chance to work. + */ + + if (i_get_time() / ticdup - entertic >= MAX_NETGAME_STALL_TICS) + { + return; + } + + usleep(1000); + } + } + + /* run the count * ticdup dics */ + + while (counts--) + { + ticcmd_set_t *set; + + if (!players_in_game()) + { + return; + } + + set = &ticdata[(gametic / ticdup) % + CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + if (!net_client_connected) + { + single_player_clear(set); + } + + for (i = 0; i < ticdup; i++) + { + if (gametic / ticdup > lowtic) i_error("gametic>lowtic"); + + memcpy(local_playeringame, set->ingame, + sizeof(local_playeringame)); + + loop_interface->run_tic(set->cmds, set->ingame); + gametic++; + + /* modify command for duplicated tics */ + + ticdup_squash(set); + } + + net_update(); /* check for new console commands */ + } +} + +void d_register_loop_callbacks(loop_interface_t *i) +{ + loop_interface = i; +} + +/* If the provided conditional value is true, we're trying to record + * a demo file that will include a non-vanilla extension. The function + * will return true if the conditional is true and it's allowed to use + * this extension (no extensions are allowed if -strictdemos is given + * on the command line). A warning is shown on the console using the + * provided string describing the non-vanilla expansion. + */ + +boolean d_non_vanilla_record(boolean conditional, const char *feature) +{ + if (!conditional || strict_demos()) + { + return false; + } + + printf("Warning: Recording a demo file with a non-vanilla extension " + "(%s). Use -strictdemos to disable this extension.\n", + feature); + + return true; +} + +/* If the provided conditional value is true, we're trying to play back + * a demo that includes a non-vanilla extension. We return true if the + * conditional is true and it's allowed to use this extension, checking + * that: + * - The -strictdemos command line argument is not provided. + * - The given lumpnum identifying the demo to play back identifies a + * demo that comes from a .lmp file, not a .wad file. + * - Before proceeding, a warning is shown to the user on the console. + */ + +boolean d_non_vanilla_playback(boolean conditional, int lumpnum, + const char *feature) +{ + if (!conditional || strict_demos()) + { + return false; + } + + if (!is_demo_file(lumpnum)) + { + printf("Warning: WAD contains demo with a non-vanilla extension " + "(%s)\n", + feature); + return false; + } + + printf("Warning: Playing back a demo file with a non-vanilla extension " + "(%s). Use -strictdemos to disable this extension.\n", + feature); + + return true; +} diff --git a/games/NXDoom/src/d_loop.h b/games/NXDoom/src/d_loop.h new file mode 100644 index 00000000000..95c2f7a3c5a --- /dev/null +++ b/games/NXDoom/src/d_loop.h @@ -0,0 +1,118 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_loop.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Main loop stuff. + * + ****************************************************************************/ + +#ifndef __D_LOOP__ +#define __D_LOOP__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "m_fixed.h" +#include "net_defs.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Callback function invoked while waiting for the netgame to start. + * The callback is invoked when new players are ready. The callback + * should return true, or return false to abort startup. + */ + +typedef boolean (*netgame_startup_callback_t)(int ready_players, + int num_players); + +typedef struct +{ + /* Read events from the event queue, and process them. */ + + void (*process_events)(void); + + /* Given the current input state, fill in the fields of the specified + * ticcmd_t structure with data for a new tic. + */ + + void (*build_ticcmd)(ticcmd_t *cmd, int maketic); + + /* Advance the game forward one tic, using the specified player input. */ + + void (*run_tic)(ticcmd_t *cmds, boolean *ingame); + + /* Run the menu (runs independently of the game). */ + + void (*run_menu)(void); +} loop_interface_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern boolean singletics; +extern int gametic; +extern int ticdup; +extern fixed_t offsetms; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Register callback functions for the main loop code to use. */ + +void d_register_loop_callbacks(loop_interface_t *i); + +/* Create any new ticcmds and broadcast to other players. */ + +void net_update(void); + +/* ? how many ticks to run? */ + +void try_run_tics(void); + +/* Called at start of game loop to initialize timers */ + +void d_start_game_loop(void); + +/* Initialize networking code and connect to server. */ + +boolean d_init_net_game(net_connect_data_t *connect_data); + +/* Start game with specified settings. The structure will be updated + * with the actual settings for the game. + */ + +void d_start_net_game(net_gamesettings_t *settings, + netgame_startup_callback_t callback); + +/* Check if it is permitted to record a demo with a non-vanilla feature. */ + +boolean d_non_vanilla_record(boolean conditional, const char *feature); + +/* Check if it is permitted to play back a demo with a non-vanilla feature. */ + +boolean d_non_vanilla_playback(boolean conditional, int lumpnum, + const char *feature); + +void d_receive_tic(ticcmd_t *ticcmds, boolean *playeringame); + +#endif /* __D_LOOP__ */ diff --git a/games/NXDoom/src/d_mode.c b/games/NXDoom/src/d_mode.c new file mode 100644 index 00000000000..6c4891cc1e0 --- /dev/null +++ b/games/NXDoom/src/d_mode.c @@ -0,0 +1,247 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_mode.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Functions and definitions relating to the game type and operational + * mode. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_mode.h" +#include "doomtype.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Valid game mode/mission combinations, with the number of + * episodes/maps for each. + */ + +struct modemission +{ + gamemission_t mission; + game_mode_t mode; + int episode; + int map; +}; + +struct gameversion +{ + gamemission_t mission; + game_version_t version; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct modemission g_valid_modes[] = +{ + {.mission = doom, .mode = shareware, .episode = 1, .map = 9}, + {.mission = doom, .mode = registered, .episode = 3, .map = 9}, + {.mission = doom, .mode = retail, .episode = 4, .map = 9}, + {.mission = doom2, .mode = commercial, .episode = 1, .map = 32}, +}; + +/* Table of valid versions */ + +static struct gameversion g_valid_versions[] = +{ + {.mission = doom, .version = exe_doom_1_2}, + {.mission = doom, .version = exe_doom_1_5}, + {.mission = doom, .version = exe_doom_1_666}, + {.mission = doom, .version = exe_doom_1_7}, + {.mission = doom, .version = exe_doom_1_8}, + {.mission = doom, .version = exe_doom_1_9}, + {.mission = doom, .version = exe_hacx}, + {.mission = doom, .version = exe_ultimate}, + {.mission = doom, .version = exe_final}, + {.mission = doom, .version = exe_final2}, + {.mission = doom, .version = exe_chex}, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Check that a gamemode + gamemission received over the network is valid. */ + +boolean d_valid_game_mode(gamemission_t mission, game_mode_t mode) +{ + int i; + + for (i = 0; i < arrlen(g_valid_modes); ++i) + { + if (g_valid_modes[i].mode == mode && + g_valid_modes[i].mission == mission) + { + return true; + } + } + + return false; +} + +boolean d_valid_episode_map(gamemission_t mission, game_mode_t mode, + int episode, int map) +{ + int i; + + /* Hacks for Heretic secret episodes */ + + if (mission == heretic) + { + if (mode == retail && episode == 6) + { + return map >= 1 && map <= 3; + } + else if (mode == registered && episode == 4) + { + return map == 1; + } + } + + /* Find the table entry for this mission/mode combination. */ + + for (i = 0; i < arrlen(g_valid_modes); ++i) + { + if (mission == g_valid_modes[i].mission && + mode == g_valid_modes[i].mode) + { + return episode >= 1 && episode <= g_valid_modes[i].episode && + map >= 1 && map <= g_valid_modes[i].map; + } + } + + /* Unknown mode/mission combination */ + + return false; +} + +/* Get the number of valid episodes for the specified mission/mode. */ + +int d_get_num_episodes(gamemission_t mission, game_mode_t mode) +{ + int episode; + + episode = 1; + + while (d_valid_episode_map(mission, mode, episode, 1)) + { + ++episode; + } + + return episode - 1; +} + +boolean d_valid_game_version(gamemission_t mission, game_version_t version) +{ + int i; + + /* All Doom variants can use the Doom versions. */ + + if (mission == doom2 || mission == pack_plut || mission == pack_tnt || + mission == pack_hacx || mission == pack_chex) + { + mission = doom; + } + + for (i = 0; i < arrlen(g_valid_versions); ++i) + { + if (g_valid_versions[i].mission == mission && + g_valid_versions[i].version == version) + { + return true; + } + } + + return false; +} + +/* Does this mission type use ExMy form, rather than MAPxy form? */ + +boolean d_is_episode_map(gamemission_t mission) +{ + switch (mission) + { + case doom: + case heretic: + case pack_chex: + return true; + + case none: + case hexen: + case doom2: + case pack_hacx: + case pack_tnt: + case pack_plut: + case strife: + default: + return false; + } +} + +const char *d_game_mission_string(gamemission_t mission) +{ + switch (mission) + { + case none: + default: + return "none"; + case doom: + return "doom"; + case doom2: + return "doom2"; + case pack_tnt: + return "tnt"; + case pack_plut: + return "plutonia"; + case pack_hacx: + return "hacx"; + case pack_chex: + return "chex"; + case heretic: + return "heretic"; + case hexen: + return "hexen"; + case strife: + return "strife"; + } +} + +const char *d_game_mode_string(game_mode_t mode) +{ + switch (mode) + { + case shareware: + return "shareware"; + case registered: + return "registered"; + case commercial: + return "commercial"; + case retail: + return "retail"; + case indetermined: + default: + return "unknown"; + } +} diff --git a/games/NXDoom/src/d_mode.h b/games/NXDoom/src/d_mode.h new file mode 100644 index 00000000000..26c2827535f --- /dev/null +++ b/games/NXDoom/src/d_mode.h @@ -0,0 +1,126 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_mode.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Functions and definitions relating to the game type and operational + * mode. + * + ****************************************************************************/ + +#ifndef __D_MODE__ +#define __D_MODE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The "mission" controls what game we are playing. */ + +typedef enum +{ + doom, /* Doom 1 */ + doom2, /* Doom 2 */ + pack_tnt, /* Final Doom: TNT: Evilution */ + pack_plut, /* Final Doom: The Plutonia Experiment */ + pack_chex, /* Chex Quest (modded doom) */ + pack_hacx, /* Hacx (modded doom2) */ + heretic, /* Heretic */ + hexen, /* Hexen */ + strife, /* Strife */ + doom2f, /* Doom 2: L'Enfer sur Terre */ + none +} gamemission_t; + +/* The "mode" allows more accurate specification of the game mode we are + * in: eg. shareware vs. registered. So doom1.wad and doom.wad are the + * same mission, but a different mode. + */ + +typedef enum +{ + shareware, /* Doom/Heretic shareware */ + registered, /* Doom/Heretic registered */ + commercial, /* Doom II/Hexen */ + retail, /* Ultimate Doom */ + indetermined /* Unknown. */ +} game_mode_t; + +/* What version are we emulating? */ + +typedef enum +{ + exe_doom_1_2, /* Doom 1.2: shareware and registered */ + exe_doom_1_5, /* Doom 1.5: " */ + exe_doom_1_666, /* Doom 1.666: for shareware, registered and commercial */ + exe_doom_1_7, /* Doom 1.7/1.7a: " */ + exe_doom_1_8, /* Doom 1.8: " */ + exe_doom_1_9, /* Doom 1.9: " */ + exe_hacx, /* Hacx */ + exe_ultimate, /* Ultimate Doom (retail) */ + exe_final, /* Final Doom */ + exe_final2, /* Final Doom (alternate exe) */ + exe_chex, /* Chex Quest executable (based on Final Doom) */ + exe_heretic_1_3, /* Heretic 1.3 */ + exe_hexen_1_1, /* Hexen 1.1 */ + exe_hexen_1_1r2, /* Hexen 1.1 (alternate exe) */ + exe_strife_1_2, /* Strife v1.2 */ + exe_strife_1_31 /* Strife v1.31 */ +} game_version_t; + +/* What IWAD variant are we using? */ + +typedef enum +{ + vanilla, /* Vanilla Doom */ + freedoom, /* FreeDoom: Phase 1 + 2 */ + freedm, /* FreeDM */ + bfgedition, /* Doom Classic (Doom 3: BFG Edition) */ +} game_variant_t; + +/* Skill level. */ + +typedef enum +{ + sk_noitems = -1, /* the "-skill 0" hack */ + sk_baby = 0, + sk_easy, + sk_medium, + sk_hard, + sk_nightmare +} skill_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean d_valid_game_mode(gamemission_t mission, game_mode_t mode); +boolean d_valid_game_version(gamemission_t mission, game_version_t version); +boolean d_valid_episode_map(gamemission_t mission, game_mode_t mode, + int episode, int map); +int d_get_num_episodes(gamemission_t mission, game_mode_t mode); +boolean d_is_episode_map(gamemission_t mission); +const char *d_game_mission_string(gamemission_t mission); +const char *d_game_mode_string(game_mode_t mode); + +#endif /* __D_MODE__ */ diff --git a/games/NXDoom/src/d_ticcmd.h b/games/NXDoom/src/d_ticcmd.h new file mode 100644 index 00000000000..c6734072799 --- /dev/null +++ b/games/NXDoom/src/d_ticcmd.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_ticcmd.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __D_TICCMD__ +#define __D_TICCMD__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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 +{ + signed char forwardmove; /* *2048 for move */ + signed char sidemove; /* *2048 for move */ + short angleturn; /* <<16 for angle delta */ + byte chatchar; + byte buttons; + + /* villsa [STRIFE] according to the asm, + * consistency is a short, not a byte + */ + + byte consistency; /* checks for net game */ + + /* villsa - Strife specific: */ + + byte buttons2; + int inventory; + + /* Heretic/Hexen specific: */ + + byte lookfly; /* look/fly up/down/centering */ + byte arti; /* artitype_t to use */ +} ticcmd_t; + +#endif /* __D_TICCMD__ */ diff --git a/games/NXDoom/src/deh_defs.h b/games/NXDoom/src/deh_defs.h new file mode 100644 index 00000000000..f4f5c1ea4bb --- /dev/null +++ b/games/NXDoom/src/deh_defs.h @@ -0,0 +1,120 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_defs.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Definitions for use in the dehacked code + * + ****************************************************************************/ + +#ifndef DEH_DEFS_H +#define DEH_DEFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "sha1.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct deh_context_s deh_context_t; +typedef struct deh_section_s deh_section_t; +typedef void (*deh_section_init_t)(void); +typedef void *(*deh_section_start_t)(deh_context_t *context, char *line); +typedef void (*deh_section_end_t)(deh_context_t *context, void *tag); +typedef void (*deh_line_parser_t)(deh_context_t *context, char *line, + void *tag); +typedef void (*deh_sha1_hash_t)(SHA1_CTX *context); + +struct deh_section_s +{ + const char *name; + + /* Called on startup to initialize code */ + + deh_section_init_t init; + + /* This is called when a new section is started. The pointer + * returned is used as a tag for the following calls. + */ + + deh_section_start_t start; + + /* This is called for each line in the section */ + + deh_line_parser_t line_parser; + + /* This is called at the end of the section for any cleanup */ + + deh_section_end_t end; + + /* Called when generating an SHA1 sum of the dehacked state */ + + deh_sha1_hash_t sha1_hash; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern deh_section_t *deh_section_types[]; +extern const char *deh_signatures[]; + +/* deh_ammo.c: */ + +extern deh_section_t deh_section_ammo; + +/* deh_cheat.c: */ + +extern deh_section_t deh_section_cheat; + +/* deh_frame.c: */ + +extern deh_section_t deh_section_frame; + +/* deh_misc.c: */ + +extern deh_section_t deh_section_misc; + +/* deh_ptr.c: */ + +extern deh_section_t deh_section_pointer; + +/* deh_sound.c */ + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +extern deh_section_t deh_section_sound; +#endif + +/* deh_text.c: */ + +extern deh_section_t deh_section_text; + +/* deh_thing.c: */ + +extern deh_section_t deh_section_thing; + +/* deh_weapon.c: */ + +extern deh_section_t deh_section_weapon; + +/* deh_bexstr.c: */ + +extern deh_section_t deh_section_bexstr; + +#endif /* DEH_DEFS_H */ diff --git a/games/NXDoom/src/deh_io.c b/games/NXDoom/src/deh_io.c new file mode 100644 index 00000000000..fa1102bb53b --- /dev/null +++ b/games/NXDoom/src/deh_io.c @@ -0,0 +1,392 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_io.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * + * Dehacked I/O code (does all reads from dehacked files) + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "m_misc.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "deh_defs.h" +#include "deh_io.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + DEH_INPUT_FILE, + DEH_INPUT_LUMP +} deh_input_type_t; + +struct deh_context_s +{ + deh_input_type_t type; + char *filename; + + /* If the input comes from a memory buffer, pointer to the memory + * buffer. + */ + + unsigned char *input_buffer; + size_t input_buffer_len; + unsigned int input_buffer_pos; + int lumpnum; + + /* If the input comes from a file, the file stream for reading + * data. + */ + + FILE *stream; + + /* Current line number that we have reached: */ + + int linenum; + + /* Used by deh_read_line: */ + + boolean last_was_newline; + char *readbuffer; + int readbuffer_size; + + /* Error handling. */ + + boolean had_error; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static deh_context_t *deh_new_context(void) +{ + deh_context_t *context; + + context = z_malloc(sizeof(*context), PU_STATIC, NULL); + + /* Initial read buffer size of 128 bytes */ + + context->readbuffer_size = 128; + context->readbuffer = z_malloc(context->readbuffer_size, PU_STATIC, NULL); + context->linenum = 0; + context->last_was_newline = true; + + context->had_error = false; + + return context; +} + +/* Increase the read buffer size */ + +static void increase_read_buffer(deh_context_t *context) +{ + char *newbuffer; + int newbuffer_size; + + newbuffer_size = context->readbuffer_size * 2; + newbuffer = z_malloc(newbuffer_size, PU_STATIC, NULL); + + memcpy(newbuffer, context->readbuffer, context->readbuffer_size); + + z_free(context->readbuffer); + + context->readbuffer = newbuffer; + context->readbuffer_size = newbuffer_size; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Open a dehacked file for reading + * Returns NULL if open failed + */ + +deh_context_t *deh_open_file(const char *filename) +{ + FILE *fstream; + deh_context_t *context; + + fstream = fopen(filename, "r"); + + if (fstream == NULL) return NULL; + + context = deh_new_context(); + + context->type = DEH_INPUT_FILE; + context->stream = fstream; + context->filename = m_string_duplicate(filename); + + return context; +} + +/* Open a WAD lump for reading. */ + +deh_context_t *deh_open_lump(int lumpnum) +{ + deh_context_t *context; + void *lump; + + lump = w_cache_lump_num(lumpnum, PU_STATIC); + + context = deh_new_context(); + + context->type = DEH_INPUT_LUMP; + context->lumpnum = lumpnum; + context->input_buffer = lump; + context->input_buffer_len = w_lump_length(lumpnum); + context->input_buffer_pos = 0; + + context->filename = malloc(9); + m_str_copy(context->filename, lumpinfo[lumpnum]->name, 9); + + return context; +} + +/* Close dehacked file */ + +void deh_close_file(deh_context_t *context) +{ + if (context->type == DEH_INPUT_FILE) + { + fclose(context->stream); + } + else if (context->type == DEH_INPUT_LUMP) + { + w_release_lump_num(context->lumpnum); + } + + free(context->filename); + z_free(context->readbuffer); + z_free(context); +} + +int deh_get_char_file(deh_context_t *context) +{ + if (feof(context->stream)) + { + /* end of file */ + + return -1; + } + + return fgetc(context->stream); +} + +int deh_get_char_lump(deh_context_t *context) +{ + int result; + + if (context->input_buffer_pos >= context->input_buffer_len) + { + return -1; + } + + result = context->input_buffer[context->input_buffer_pos]; + ++context->input_buffer_pos; + + return result; +} + +/* Reads a single character from a dehacked file */ + +int deh_get_char(deh_context_t *context) +{ + int result = 0; + boolean last_was_cr = false; + + /* Track the current line number */ + + if (context->last_was_newline) + { + ++context->linenum; + } + + /* Read characters, converting CRLF to LF */ + + do + { + switch (context->type) + { + case DEH_INPUT_FILE: + result = deh_get_char_file(context); + break; + + case DEH_INPUT_LUMP: + result = deh_get_char_lump(context); + break; + } + + /* Handle \r characters not paired with \n */ + + if (last_was_cr && result != '\n') + { + switch (context->type) + { + case DEH_INPUT_FILE: + ungetc(result, context->stream); + break; + + case DEH_INPUT_LUMP: + --context->input_buffer_pos; + break; + } + + return '\r'; + } + + last_was_cr = result == '\r'; + } + while (last_was_cr); + + context->last_was_newline = result == '\n'; + + return result; +} + +/* Read a whole line */ + +char *deh_read_line(deh_context_t *context, boolean extended) +{ + int c; + int pos; + boolean escaped = false; + + for (pos = 0; ; ) + { + c = deh_get_char(context); + + if (c < 0 && pos == 0) + { + /* end of file */ + + return NULL; + } + + /* cope with lines of any length: increase the buffer size */ + + if (pos >= context->readbuffer_size) + { + increase_read_buffer(context); + } + + /* extended string support */ + + if (extended && c == '\\') + { + c = deh_get_char(context); + + /* "\n" in the middle of a string indicates an internal linefeed */ + + if (c == 'n') + { + context->readbuffer[pos] = '\n'; + ++pos; + continue; + } + + /* values to be assigned may be split onto multiple lines by ending + * each line that is to be continued with a backslash + */ + + if (c == '\n') + { + escaped = true; + continue; + } + } + + /* blanks before the backslash are included in the string + * but indentation after the linefeed is not + */ + + if (escaped && c >= 0 && isspace(c) && c != '\n') + { + continue; + } + else + { + escaped = false; + } + + if (c == '\n' || c < 0) + { + /* end of line: a full line has been read */ + + context->readbuffer[pos] = '\0'; + break; + } + else if (c != '\0') + { + /* normal character; don't allow NUL characters to be + * added. + */ + + context->readbuffer[pos] = (char)c; + ++pos; + } + } + + return context->readbuffer; +} + +void deh_warning(deh_context_t *context, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + + fprintf(stderr, "%s:%i: warning: ", context->filename, context->linenum); + vfprintf(stderr, msg, args); + fprintf(stderr, "\n"); + + va_end(args); +} + +void deh_error(deh_context_t *context, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + + fprintf(stderr, "%s:%i: ", context->filename, context->linenum); + vfprintf(stderr, msg, args); + fprintf(stderr, "\n"); + + va_end(args); + + context->had_error = true; +} + +boolean deh_had_error(deh_context_t *context) +{ + return context->had_error; +} diff --git a/games/NXDoom/src/deh_io.h b/games/NXDoom/src/deh_io.h new file mode 100644 index 00000000000..8ee12b5c0f5 --- /dev/null +++ b/games/NXDoom/src/deh_io.h @@ -0,0 +1,46 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_io.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Dehacked I/O code (does all reads from dehacked files) + * + ****************************************************************************/ + +#ifndef DEH_IO_H +#define DEH_IO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_defs.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +deh_context_t *deh_open_file(const char *filename); +deh_context_t *deh_open_lump(int lumpnum); +void deh_close_file(deh_context_t *context); +int deh_get_char(deh_context_t *context); +char *deh_read_line(deh_context_t *context, boolean extended); +void deh_error(deh_context_t *context, const char *msg, ...) + PRINTF_ATTR(2, 3); +void deh_warning(deh_context_t *context, const char *msg, ...) + PRINTF_ATTR(2, 3); +boolean deh_had_error(deh_context_t *context); + +#endif /* DEH_IO_H */ diff --git a/games/NXDoom/src/deh_main.c b/games/NXDoom/src/deh_main.c new file mode 100644 index 00000000000..f6eb667ac54 --- /dev/null +++ b/games/NXDoom/src/deh_main.c @@ -0,0 +1,554 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_main.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Main dehacked code + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "d_iwad.h" +#include "doomtype.h" +#include "i_glob.h" +#include "i_system.h" +#include "m_argv.h" +#include "w_wad.h" + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static boolean deh_initialized = false; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* If true, we can parse [STRINGS] sections in BEX format. */ + +boolean deh_allow_extended_strings = false; + +/* If true, we can do long string replacements. */ + +boolean deh_allow_long_strings = false; + +/* If true, we can do cheat replacements longer than the originals. */ + +boolean deh_allow_long_cheats = false; + +/* If false, dehacked cheat replacements are ignored. */ + +boolean deh_apply_cheats = true; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Called on startup to call the Init functions */ + +static void initialize_sections(void) +{ + unsigned int i; + + for (i = 0; deh_section_types[i] != NULL; ++i) + { + if (deh_section_types[i]->init != NULL) + { + deh_section_types[i]->init(); + } + } +} + +static void deh_init(void) +{ + /* @category mod + * + * Ignore cheats in dehacked files. + */ + + if (m_check_parm("-nocheats") > 0) + { + deh_apply_cheats = false; + } + + /* Call init functions for all the section definitions. */ + + initialize_sections(); + + deh_initialized = true; +} + +/* Given a section name, get the section structure which corresponds */ + +static deh_section_t *get_section_by_name(char *name) +{ + unsigned int i; + + /* we explicitly do not recognize [STRINGS] sections at all + * if extended strings are not allowed + */ + + if (!deh_allow_extended_strings && !strncasecmp("[STRINGS]", name, 9)) + { + return NULL; + } + + for (i = 0; deh_section_types[i] != NULL; ++i) + { + if (!strcasecmp(deh_section_types[i]->name, name)) + { + return deh_section_types[i]; + } + } + + return NULL; +} + +/* Is the string passed just whitespace? */ + +static boolean is_whitespace(char *s) +{ + for (; *s; ++s) + { + if (!isspace(*s)) return false; + } + + return true; +} + +/* Strip whitespace from the start and end of a string */ + +static char *clean_string(char *s) +{ + char *strending; + + /* Leading whitespace */ + + while (*s && isspace(*s)) + ++s; + + /* Trailing whitespace */ + + strending = s + strlen(s) - 1; + + while (strlen(s) > 0 && isspace(*strending)) + { + *strending = '\0'; + --strending; + } + + return s; +} + +static boolean check_signatures(deh_context_t *context) +{ + size_t i; + char *line; + + /* Read the first line */ + + line = deh_read_line(context, false); + + if (line == NULL) + { + return false; + } + + /* Check all signatures to see if one matches */ + + for (i = 0; deh_signatures[i] != NULL; ++i) + { + if (!strcmp(deh_signatures[i], line)) + { + return true; + } + } + + return false; +} + +/* Parses a comment string in a dehacked file. */ + +static void deh_parse_comment(char *comment) +{ + /* Welcome, to the super-secret Chocolate Doom-specific Dehacked + * overrides function. + * + * Putting these magic comments into your Dehacked lumps will + * allow you to go beyond the normal limits of Vanilla Dehacked. + * Because of this, these comments are deliberately undocumented, + * and if you're using them you should be aware that your mod + * is not compatible with Vanilla Doom and you're probably a + * very naughty person. + * + + * Allow comments containing this special value to allow string + * replacements longer than those permitted by DOS dehacked. + * This allows us to use a dehacked patch for doing string + * replacements for emulating Chex Quest. + * + * If you use this, your dehacked patch may not work in Vanilla + * Doom. + */ + + if (strstr(comment, "*allow-long-strings*") != NULL) + { + deh_allow_long_strings = true; + } + + /* Allow magic comments to allow longer cheat replacements than + * those permitted by DOS dehacked. This is also for Chex + * Quest. + */ + + if (strstr(comment, "*allow-long-cheats*") != NULL) + { + deh_allow_long_cheats = true; + } + + /* Allow magic comments to allow parsing [STRINGS] section + * that are usually only found in BEX format files. This allows + * for substitution of map and episode names when loading + * Freedoom/FreeDM IWADs. + */ + + if (strstr(comment, "*allow-extended-strings*") != NULL) + { + deh_allow_extended_strings = true; + } +} + +/* Parses a dehacked file by reading from the context */ + +static void deh_parse_context(deh_context_t *context) +{ + deh_section_t *current_section = NULL; + char section_name[20]; + void *tag = NULL; + boolean extended; + char *line; + + /* Read the header and check it matches the signature */ + + if (!check_signatures(context)) + { + deh_error(context, "This is not a valid dehacked patch file!"); + } + + /* Read the file */ + + while (!deh_had_error(context)) + { + /* Read the next line. We only allow the special extended parsing + * for the BEX [STRINGS] section. + */ + + extended = current_section != NULL && + !strcasecmp(current_section->name, "[STRINGS]"); + line = deh_read_line(context, extended); + + /* end of file? */ + + if (line == NULL) + { + return; + } + + while (line[0] != '\0' && isspace(line[0])) + ++line; + + if (line[0] == '#') + { + /* comment */ + + deh_parse_comment(line); + continue; + } + + if (is_whitespace(line)) + { + if (current_section != NULL) + { + /* end of section */ + + if (current_section->end != NULL) + { + current_section->end(context, tag); + } + + current_section = NULL; + } + } + else + { + if (current_section != NULL) + { + /* parse this line */ + + current_section->line_parser(context, line, tag); + } + else + { + /* possibly the start of a new section */ + + sscanf(line, "%19s", section_name); + + current_section = get_section_by_name(section_name); + + if (current_section != NULL) + { + tag = current_section->start(context, line); + } + else + { + /* printf("unknown section name %s\n", section_name); */ + } + } + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void deh_checksum(sha1_digest_t digest) +{ + SHA1_CTX sha1_context; + unsigned int i; + + sha1init(&sha1_context); + + for (i = 0; deh_section_types[i] != NULL; ++i) + { + if (deh_section_types[i]->sha1_hash != NULL) + { + deh_section_types[i]->sha1_hash(&sha1_context); + } + } + + sha1final(digest, &sha1_context); +} + +/* This pattern is used a lot of times in different sections, + * an assignment is essentially just a statement of the form: + * + * Variable Name = Value + * + * The variable name can include spaces or any other characters. + * The string is split on the '=', essentially. + * + * Returns true if read correctly + */ + +boolean deh_parse_assignment(char *line, char **variable_name, char **value) +{ + char *p; + + /* find the equals */ + + p = strchr(line, '='); + + if (p == NULL) + { + return false; + } + + /* variable name at the start + * turn the '=' into a \0 to terminate the string here + */ + + *p = '\0'; + *variable_name = clean_string(line); + + /* value immediately follows the '=' */ + + *value = clean_string(p + 1); + + return true; +} + +/* Parses a dehacked file */ + +int deh_loadfile(const char *filename) +{ + deh_context_t *context; + boolean had_error; + + if (!deh_initialized) + { + deh_init(); + } + + /* Before parsing a new file, reset special override flags to false. + * Magic comments should only apply to the file in which they were + * defined, and shouldn't carry over to subsequent files as well. + */ + + deh_allow_long_strings = false; + deh_allow_long_cheats = false; + deh_allow_extended_strings = false; + + printf(" loading %s\n", filename); + + context = deh_open_file(filename); + + if (context == NULL) + { + fprintf(stderr, "deh_loadfile: Unable to open %s\n", filename); + return 0; + } + + deh_parse_context(context); + + had_error = deh_had_error(context); + + deh_close_file(context); + + if (had_error) + { + i_error("Error parsing dehacked file"); + } + + return 1; +} + +/* Load all dehacked patches from the given directory. */ + +void deh_auto_load_patches(const char *path) +{ + const char *filename; + glob_t *glob; + + glob = i_start_multi_glob(path, GLOB_FLAG_NOCASE | GLOB_FLAG_SORTED, + "*.deh", "*.hhe", "*.seh", NULL); + for (; ; ) + { + filename = i_next_glob(glob); + if (filename == NULL) + { + break; + } + + printf(" [autoload]"); + deh_loadfile(filename); + } + + i_end_glob(glob); +} + +/* Load dehacked file from WAD lump. + * If allow_long is set, allow long strings and cheats just for this lump. + */ + +int deh_load_lump(int lumpnum, boolean allow_long, boolean allow_error) +{ + deh_context_t *context; + boolean had_error; + + if (!deh_initialized) + { + deh_init(); + } + + /* Reset all special flags to defaults. */ + + deh_allow_long_strings = allow_long; + deh_allow_long_cheats = allow_long; + deh_allow_extended_strings = false; + + context = deh_open_lump(lumpnum); + + if (context == NULL) + { + fprintf(stderr, "deh_loadfile: Unable to open lump %i\n", lumpnum); + return 0; + } + + deh_parse_context(context); + + had_error = deh_had_error(context); + + deh_close_file(context); + + /* If there was an error while parsing, abort with an error, but allow + * errors to just be ignored if allow_error=true. + */ + + if (!allow_error && had_error) + { + i_error("Error parsing dehacked lump"); + } + + return 1; +} + +int deh_load_lump_by_name(const char *name, boolean allow_long, + boolean allow_error) +{ + int lumpnum; + + lumpnum = w_check_num_for_name(name); + + if (lumpnum == -1) + { + fprintf(stderr, "deh_load_lump_by_name: '%s' lump not found\n", name); + return 0; + } + + return deh_load_lump(lumpnum, allow_long, allow_error); +} + +/* Check the command line for -deh argument, and others. */ + +void deh_parse_command_line(void) +{ + char *filename; + int p; + + /* @arg + * @category mod + * + * Load the given dehacked patch(es) + */ + + p = m_check_parm("-deh"); + + if (p > 0) + { + ++p; + + while (p < myargc && myargv[p][0] != '-') + { + filename = d_try_find_wad_by_name(myargv[p]); + deh_loadfile(filename); + free(filename); + ++p; + } + } +} diff --git a/games/NXDoom/src/deh_main.h b/games/NXDoom/src/deh_main.h new file mode 100644 index 00000000000..8ae728243ca --- /dev/null +++ b/games/NXDoom/src/deh_main.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_main.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Dehacked entrypoint and common code + * + ****************************************************************************/ + +#ifndef DEH_MAIN_H +#define DEH_MAIN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_str.h" +#include "doomtype.h" +#include "sha1.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* These are the limits that dehacked uses (from dheinit.h in the dehacked + * source). If these limits are exceeded, it does not generate an error, but + * a warning is displayed. + */ + +#define DEH_VANILLA_NUMSTATES 966 +#define DEH_VANILLA_NUMSFX 107 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern boolean deh_allow_extended_strings; +extern boolean deh_allow_long_strings; +extern boolean deh_allow_long_cheats; +extern boolean deh_apply_cheats; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void deh_parse_command_line(void); +int deh_loadfile(const char *filename); +void deh_auto_load_patches(const char *path); +int deh_load_lump(int lumpnum, boolean allow_long, boolean allow_error); +int deh_load_lump_by_name(const char *name, boolean allow_long, + boolean allow_error); + +boolean deh_parse_assignment(char *line, char **variable_name, char **value); + +void deh_checksum(sha1_digest_t digest); + +#endif /* DEH_MAIN_H */ diff --git a/games/NXDoom/src/deh_mapping.c b/games/NXDoom/src/deh_mapping.c new file mode 100644 index 00000000000..17bed207421 --- /dev/null +++ b/games/NXDoom/src/deh_mapping.c @@ -0,0 +1,206 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_mapping.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Dehacked "mapping" code + * Allows the fields in structures to be mapped out and accessed by + * name + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_misc.h" + +#include "deh_mapping.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static deh_mapping_entry_t *get_mapping_entry_by_name(deh_context_t *context, + deh_mapping_t *mapping, + char *name) +{ + int i; + + for (i = 0; mapping->entries[i].name != NULL; ++i) + { + deh_mapping_entry_t *entry = &mapping->entries[i]; + + if (!strcasecmp(entry->name, name)) + { + if (entry->location == NULL) + { + deh_warning(context, "Field '%s' is unsupported", name); + return NULL; + } + + return entry; + } + } + + /* Not found. */ + + deh_warning(context, "Field named '%s' not found", name); + + return NULL; +} + +/* Get the location of the specified field in the specified structure. */ + +static void *get_struct_field(void *structptr, deh_mapping_t *mapping, + deh_mapping_entry_t *entry) +{ + unsigned int offset; + + offset = (uint8_t *)entry->location - (uint8_t *)mapping->base; + + return (uint8_t *)structptr + offset; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Set the value of a particular field in a structure by name */ + +boolean deh_set_mapping(deh_context_t *context, deh_mapping_t *mapping, + void *structptr, char *name, int value) +{ + deh_mapping_entry_t *entry; + void *location; + + entry = get_mapping_entry_by_name(context, mapping, name); + + if (entry == NULL) + { + return false; + } + + /* Sanity check: */ + + if (entry->is_string) + { + deh_error(context, "Tried to set '%s' as integer (BUG)", name); + return false; + } + + location = get_struct_field(structptr, mapping, entry); + + /* Set field content based on its type: */ + + switch (entry->size) + { + case 1: + *((uint8_t *)location) = value; + break; + case 2: + *((uint16_t *)location) = value; + break; + case 4: + *((uint32_t *)location) = value; + break; + default: + deh_error(context, "Unknown field type for '%s' (BUG)", name); + return false; + } + + return true; +} + +/* Set the value of a string field in a structure by name */ + +boolean deh_set_string_mapping(deh_context_t *context, + deh_mapping_t *mapping, void *structptr, char *name, char *value) +{ + deh_mapping_entry_t *entry; + void *location; + + entry = get_mapping_entry_by_name(context, mapping, name); + + if (entry == NULL) + { + return false; + } + + /* Sanity check: */ + + if (!entry->is_string) + { + deh_error(context, "Tried to set '%s' as string (BUG)", name); + return false; + } + + location = get_struct_field(structptr, mapping, entry); + + /* Copy value into field: */ + + m_str_copy(location, value, entry->size); + + return true; +} + +void deh_struct_sha1_sum(SHA1_CTX *context, deh_mapping_t *mapping, + void *structptr) +{ + int i; + + /* Go through each mapping */ + + for (i = 0; mapping->entries[i].name != NULL; ++i) + { + deh_mapping_entry_t *entry = &mapping->entries[i]; + void *location; + + if (entry->location == NULL) + { + /* Unsupported field */ + + continue; + } + + /* Add in data for this field */ + + location = (uint8_t *)structptr + + ((uint8_t *)entry->location - (uint8_t *)mapping->base); + + switch (entry->size) + { + case 1: + sha1_updateint32(context, *((uint8_t *)location)); + break; + case 2: + sha1_updateint32(context, *((uint16_t *)location)); + break; + case 4: + sha1_updateint32(context, *((uint32_t *)location)); + break; + default: + i_error("Unknown dehacked mapping field type for '%s' (BUG)", + entry->name); + break; + } + } +} diff --git a/games/NXDoom/src/deh_mapping.h b/games/NXDoom/src/deh_mapping.h new file mode 100644 index 00000000000..cd5cf5bf663 --- /dev/null +++ b/games/NXDoom/src/deh_mapping.h @@ -0,0 +1,106 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_mapping.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Dehacked "mapping" code + * Allows the fields in structures to be mapped out and accessed by + * name + * + ****************************************************************************/ + +#ifndef DEH_MAPPING_H +#define DEH_MAPPING_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_io.h" +#include "doomtype.h" +#include "sha1.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DEH_BEGIN_MAPPING(mapping_name, structname) \ + static structname deh_mapping_base; \ + static deh_mapping_t mapping_name = {&deh_mapping_base, { + +#define DEH_MAPPING(deh_name, fieldname) \ + {deh_name, &deh_mapping_base.fieldname, \ + sizeof(deh_mapping_base.fieldname), false}, + +#define DEH_MAPPING_STRING(deh_name, fieldname) \ + {deh_name, &deh_mapping_base.fieldname, \ + sizeof(deh_mapping_base.fieldname), true}, + +#define DEH_UNSUPPORTED_MAPPING(deh_name) {deh_name, NULL, -1, false}, + +#define DEH_END_MAPPING \ + {NULL, NULL, -1} \ + } \ + } \ + ; + +#define MAX_MAPPING_ENTRIES 32 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct deh_mapping_s deh_mapping_t; +typedef struct deh_mapping_entry_s deh_mapping_entry_t; + +struct deh_mapping_entry_s +{ + /* field name */ + + const char *name; + + /* location relative to the base in the deh_mapping_t struct + * If this is NULL, it is an unsupported mapping + */ + + void *location; + + /* field size */ + + int size; + + /* if true, this is a string value. */ + + boolean is_string; +}; + +struct deh_mapping_s +{ + void *base; + deh_mapping_entry_t entries[MAX_MAPPING_ENTRIES]; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean deh_set_mapping(deh_context_t *context, deh_mapping_t *mapping, + void *structptr, char *name, int value); +boolean deh_set_string_mapping(deh_context_t *context, + deh_mapping_t *mapping, void *structptr, char *name, char *value); +void deh_struct_sha1_sum(SHA1_CTX *context, deh_mapping_t *mapping, + void *structptr); + +#endif /* DEH_MAPPING_H */ diff --git a/games/NXDoom/src/deh_str.c b/games/NXDoom/src/deh_str.c new file mode 100644 index 00000000000..9c3f8b8b41a --- /dev/null +++ b/games/NXDoom/src/deh_str.c @@ -0,0 +1,228 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_str.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * + * Parses Text substitution sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "deh_str.h" +#include "doomtype.h" +#include "m_misc.h" + +#include "z_zone.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + char *from_text; + char *to_text; +} deh_substitution_t; + +typedef enum +{ + FORMAT_ARG_INVALID, + FORMAT_ARG_INT, + FORMAT_ARG_FLOAT, + FORMAT_ARG_CHAR, + FORMAT_ARG_STRING, + FORMAT_ARG_PTR, + FORMAT_ARG_SAVE_POS +} format_arg_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static deh_substitution_t **hash_table = NULL; +static int hash_table_entries; +static int hash_table_length = -1; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* This is the algorithm used by glib */ + +static unsigned int strhash(const char *s) +{ + const char *p = s; + unsigned int h = *p; + + if (h) + { + for (p += 1; *p; p++) + h = (h << 5) - h + *p; + } + + return h; +} + +static deh_substitution_t *substitution_for_string(const char *s) +{ + int entry; + + /* Fallback if we have not initialized the hash table yet */ + + if (hash_table_length < 0) return NULL; + + entry = strhash(s) % hash_table_length; + + while (hash_table[entry] != NULL) + { + if (!strcmp(hash_table[entry]->from_text, s)) + { + return hash_table[entry]; /* substitution found! */ + } + + entry = (entry + 1) % hash_table_length; + } + + /* no substitution found */ + + return NULL; +} + +static void init_hash_table(void) +{ + /* init hash table */ + + hash_table_entries = 0; + hash_table_length = 16; + hash_table = z_malloc(sizeof(deh_substitution_t *) * hash_table_length, + PU_STATIC, NULL); + memset(hash_table, 0, sizeof(deh_substitution_t *) * hash_table_length); +} + +static void deh_add_to_hashtable(deh_substitution_t *sub); + +static void increase_hashtable(void) +{ + deh_substitution_t **old_table; + int old_table_length; + int i; + + /* save the old table */ + + old_table = hash_table; + old_table_length = hash_table_length; + + /* double the size */ + + hash_table_length *= 2; + hash_table = z_malloc(sizeof(deh_substitution_t *) * hash_table_length, + PU_STATIC, NULL); + memset(hash_table, 0, sizeof(deh_substitution_t *) * hash_table_length); + + /* go through the old table and insert all the old entries */ + + for (i = 0; i < old_table_length; ++i) + { + if (old_table[i] != NULL) + { + deh_add_to_hashtable(old_table[i]); + } + } + + /* free the old table */ + + z_free(old_table); +} + +static void deh_add_to_hashtable(deh_substitution_t *sub) +{ + int entry; + + /* if the hash table is more than 60% full, increase its size */ + + if ((hash_table_entries * 10) / hash_table_length > 6) + { + increase_hashtable(); + } + + /* find where to insert it */ + + entry = strhash(sub->from_text) % hash_table_length; + + while (hash_table[entry] != NULL) + { + entry = (entry + 1) % hash_table_length; + } + + hash_table[entry] = sub; + ++hash_table_entries; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void deh_add_string_replacement(const char *from_text, const char *to_text) +{ + deh_substitution_t *sub; + size_t len; + + /* Initialize the hash table if this is the first time */ + + if (hash_table_length < 0) + { + init_hash_table(); + } + + /* Check to see if there is an existing substitution already in place. */ + + sub = substitution_for_string(from_text); + + if (sub != NULL) + { + z_free(sub->to_text); + + len = strlen(to_text) + 1; + sub->to_text = z_malloc(len, PU_STATIC, NULL); + memcpy(sub->to_text, to_text, len); + } + else + { + /* We need to allocate a new substitution. */ + + sub = z_malloc(sizeof(*sub), PU_STATIC, 0); + + /* We need to create our own duplicates of the provided strings. */ + + len = strlen(from_text) + 1; + sub->from_text = z_malloc(len, PU_STATIC, NULL); + memcpy(sub->from_text, from_text, len); + + len = strlen(to_text) + 1; + sub->to_text = z_malloc(len, PU_STATIC, NULL); + memcpy(sub->to_text, to_text, len); + + deh_add_to_hashtable(sub); + } +} diff --git a/games/NXDoom/src/deh_str.h b/games/NXDoom/src/deh_str.h new file mode 100644 index 00000000000..3b2e6fefedb --- /dev/null +++ b/games/NXDoom/src/deh_str.h @@ -0,0 +1,33 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_str.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Dehacked string replacements + * + ****************************************************************************/ + +#ifndef DEH_STR_H +#define DEH_STR_H + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Used to do dehacked text substitutions throughout the program */ + +void deh_add_string_replacement(const char *from_text, const char *to_text); + +#endif /* DEH_STR_H */ diff --git a/games/NXDoom/src/deh_text.c b/games/NXDoom/src/deh_text.c new file mode 100644 index 00000000000..927a3e7c6ba --- /dev/null +++ b/games/NXDoom/src/deh_text.c @@ -0,0 +1,140 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_text.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Parses Text substitution sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" + +#include "z_zone.h" + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void deh_text_parse_line(deh_context_t *context, char *line, + void *tag); +static void *deh_text_start(deh_context_t *context, char *line); +static void *deh_text_start(deh_context_t *context, char *line); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_text = +{ + "Text", NULL, deh_text_start, deh_text_parse_line, NULL, NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Given a string length, find the maximum length of a string that can + * replace it. + */ + +static int txt_max_string_length(int len) +{ + /* Enough bytes for the string and the NUL terminator */ + + len += 1; + + /* All strings in doom.exe are on 4-byte boundaries, so we may be able + * to support a slightly longer string. + * Extend up to the next 4-byte boundary + */ + + len += (4 - (len % 4)) % 4; + + /* Less one for the NUL terminator. */ + + return len - 1; +} + +static void *deh_text_start(deh_context_t *context, char *line) +{ + char *from_text; + char *to_text; + int fromlen; + int to_len; + int i; + + if (sscanf(line, "Text %i %i", &fromlen, &to_len) != 2) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + /* Only allow string replacements that are possible in Vanilla Doom. + * Chocolate Doom is unforgiving! + */ + + if (!deh_allow_long_strings && to_len > txt_max_string_length(fromlen)) + { + deh_error(context, "Replacement string is longer than the maximum " + "possible in doom.exe"); + return NULL; + } + + from_text = malloc(fromlen + 1); + to_text = malloc(to_len + 1); + + /* read in the "from" text */ + + for (i = 0; i < fromlen; ++i) + { + from_text[i] = deh_get_char(context); + } + + from_text[fromlen] = '\0'; + + /* read in the "to" text */ + + for (i = 0; i < to_len; ++i) + { + to_text[i] = deh_get_char(context); + } + + to_text[to_len] = '\0'; + + deh_add_string_replacement(from_text, to_text); + + free(from_text); + free(to_text); + + return NULL; +} + +static void deh_text_parse_line(deh_context_t *context, char *line, + void *tag) +{ + /* not used */ +} diff --git a/games/NXDoom/src/doom/.gitignore b/games/NXDoom/src/doom/.gitignore new file mode 100644 index 00000000000..d4e88e5a219 --- /dev/null +++ b/games/NXDoom/src/doom/.gitignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +.deps +tags +TAGS diff --git a/games/NXDoom/src/doom/am_map.c b/games/NXDoom/src/doom/am_map.c new file mode 100644 index 00000000000..02d233df2e6 --- /dev/null +++ b/games/NXDoom/src/doom/am_map.c @@ -0,0 +1,1813 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/am_map.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: the automap code + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "deh_main.h" + +#include "doomdef.h" +#include "doomkeys.h" +#include "p_local.h" +#include "st_stuff.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_cheat.h" +#include "m_controls.h" +#include "m_misc.h" + +/* Needs access to LFB. */ + +#include "v_video.h" + +#include "doomstat.h" +#include "r_state.h" + +#include "dstrings.h" + +#include "am_map.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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 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) fixed_mul(((x) << FRACBITS), scale_ftom) +#define MTOF(x) (fixed_mul((x), scale_mtof) >> FRACBITS) + +/* 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 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + int x; + int y; +} fpoint_t; + +typedef struct +{ + fpoint_t a; + fpoint_t b; +} fline_t; + +typedef struct +{ + fixed_t x; + fixed_t y; +} mpoint_t; + +typedef struct +{ + mpoint_t a; + mpoint_t b; +} mline_t; + +typedef struct +{ + fixed_t slp; + fixed_t islp; +} islope_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* 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 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 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), + }, + { + (fixed_t)(0), + (fixed_t)(R), + }, + }, + { + { + (fixed_t)(0), + (fixed_t)(R), + }, + { + (fixed_t)(-.867 * R), + (fixed_t)(-.5 * R), + }, + }, +}; +#undef R + +#define R (FRACUNIT) +mline_t thintriangle_guy[] = +{ + { + { + (fixed_t)(-.5 * R), + (fixed_t)(-.7 * R), + }, + { + (fixed_t)(R), + (fixed_t)(0), + }, + }, + { + { + (fixed_t)(R), + (fixed_t)(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 + +boolean automapactive = false; + +cheatseq_t cheat_amap = CHEAT("iddt", 0); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int cheating = 0; +static int grid = 0; + +static int finit_width = SCREENWIDTH; +static int finit_height = SCREENHEIGHT - ST_HEIGHT; + +/* 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 pixel_t *fb; /* pseudo-frame buffer */ +static int amclock; + +/* how far the window pans each tic (map coords) */ + +static mpoint_t m_paninc; + +/* how far the window zooms in each tic (map coords) */ + +static fixed_t mtof_zoommul; + +/* how far the window zooms in each tic (fb coords) */ + +static fixed_t ftom_zoommul; + +/* LL x,y where the window is on the map (map coords) */ + +static fixed_t m_x; +static fixed_t m_y; + +/* UR x,y where the window is on the map (map coords) */ + +static fixed_t m_x2; +static fixed_t m_y2; + +/* 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; +static fixed_t old_m_h; +static fixed_t old_m_x; +static fixed_t 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 */ + +/* specifies whether to follow the player around */ + +static int followplayer = 1; + +static boolean stopped = true; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: am_get_i_slope + * + * Description: + * 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. + * + ****************************************************************************/ + +static void am_get_i_slope(mline_t *ml, islope_t *is) +{ + int dx; + int dy; + + dy = ml->a.y - ml->b.y; + dx = ml->b.x - ml->a.x; + if (!dy) + is->islp = (dx < 0 ? -INT_MAX : INT_MAX); + else + is->islp = fixed_div(dx, dy); + if (!dx) + is->slp = (dy < 0 ? -INT_MAX : INT_MAX); + else + is->slp = fixed_div(dy, dx); +} + +/**************************************************************************** + * Name: am_activate_new_scale + ****************************************************************************/ + +static void am_activate_new_scale(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; +} + +/**************************************************************************** + * Name: am_save_scale_and_loc + ****************************************************************************/ + +static void am_save_scale_and_loc(void) +{ + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; +} + +/**************************************************************************** + * Name: am_restore_scale_and_loc + ****************************************************************************/ + +static void am_restore_scale_and_loc(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 = fixed_div(f_w << FRACBITS, m_w); + scale_ftom = fixed_div(FRACUNIT, scale_mtof); +} + +/**************************************************************************** + * Name: am_add_mark + * + * Description: + * adds a marker at the current location + * + ****************************************************************************/ + +static void am_add_mark(void) +{ + markpoints[markpointnum].x = m_x + m_w / 2; + markpoints[markpointnum].y = m_y + m_h / 2; + markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS; +} + +/**************************************************************************** + * Name: am_find_minmax_boundaries + * + * Description: + * Determines bounding box of all vertices, sets global variables + * controlling zoom range. + * + ****************************************************************************/ + +static void am_find_minmax_boundaries(void) +{ + int i; + fixed_t a; + fixed_t b; + + min_x = min_y = INT_MAX; + max_x = max_y = -INT_MAX; + + for (i = 0; i < numvertices; i++) + { + if (vertices[i].x < min_x) + min_x = vertices[i].x; + else if (vertices[i].x > max_x) + max_x = vertices[i].x; + + if (vertices[i].y < min_y) + min_y = vertices[i].y; + else if (vertices[i].y > max_y) + max_y = vertices[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 = fixed_div(f_w << FRACBITS, max_w); + b = fixed_div(f_h << FRACBITS, max_h); + + min_scale_mtof = a < b ? a : b; + max_scale_mtof = fixed_div(f_h << FRACBITS, 2 * PLAYERRADIUS); +} + +/**************************************************************************** + * Name: am_change_window_loc + ****************************************************************************/ + +static void am_change_window_loc(void) +{ + if (m_paninc.x || m_paninc.y) + { + followplayer = 0; + f_oldloc.x = INT_MAX; + } + + 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; +} + +/**************************************************************************** + * Name: am_init_variables + ****************************************************************************/ + +static void am_init_variables(void) +{ + int pnum; + static event_t st_notify = + { + ev_keyup, AM_MSGENTERED, 0, 0 + }; + + automapactive = true; + fb = i_video_buffer; + + f_oldloc.x = INT_MAX; + 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[consoleplayer]) + { + plr = &players[consoleplayer]; + } + else + { + plr = &players[0]; + + for (pnum = 0; pnum < MAXPLAYERS; pnum++) + { + if (playeringame[pnum]) + { + plr = &players[pnum]; + break; + } + } + } + + m_x = plr->mo->x - m_w / 2; + m_y = plr->mo->y - m_h / 2; + am_change_window_loc(); + + /* 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); +} + +/**************************************************************************** + * Name: am_load_pics + ****************************************************************************/ + +static void am_load_pics(void) +{ + int i; + char namebuf[9]; + + for (i = 0; i < 10; i++) + { + snprintf(namebuf, 9, "AMMNUM%d", i); + marknums[i] = w_cache_lump_name(namebuf, PU_STATIC); + } +} + +/**************************************************************************** + * Name: am_unload_pics + ****************************************************************************/ + +static void am_unload_pics(void) +{ + int i; + char namebuf[9]; + + for (i = 0; i < 10; i++) + { + snprintf(namebuf, 9, "AMMNUM%d", i); + w_release_lump_name(namebuf); + } +} + +/**************************************************************************** + * Name: am_clear_marks + ****************************************************************************/ + +static void am_clear_marks(void) +{ + int i; + + for (i = 0; i < AM_NUMMARKPOINTS; i++) + { + markpoints[i].x = -1; /* means empty */ + } + + markpointnum = 0; +} + +/**************************************************************************** + * Name: am_level_init + * + * Description: + * should be called at the start of every level right now, i figure it out + * myself + * + ****************************************************************************/ + +static void am_level_init(void) +{ + f_x = f_y = 0; + f_w = finit_width; + f_h = finit_height; + + am_clear_marks(); + + am_find_minmax_boundaries(); + scale_mtof = fixed_div(min_scale_mtof, (int)(0.7 * FRACUNIT)); + if (scale_mtof > max_scale_mtof) scale_mtof = min_scale_mtof; + scale_ftom = fixed_div(FRACUNIT, scale_mtof); +} + +/**************************************************************************** + * Name: am_start + ****************************************************************************/ + +static void am_start(void) +{ + static int lastlevel = -1; + static int lastepisode = -1; + + if (!stopped) am_stop(); + stopped = false; + if (lastlevel != gamemap || lastepisode != gameepisode) + { + am_level_init(); + lastlevel = gamemap; + lastepisode = gameepisode; + } + + am_init_variables(); + am_load_pics(); +} + +/**************************************************************************** + * Name: am_min_out_window_scale + * + * Description: + * set the window scale to the maximum size + * + ****************************************************************************/ + +static void am_min_out_window_scale(void) +{ + scale_mtof = min_scale_mtof; + scale_ftom = fixed_div(FRACUNIT, scale_mtof); + am_activate_new_scale(); +} + +/**************************************************************************** + * Name: am_max_out_window_scale + * + * Description: + * set the window scale to the minimum size + * + ****************************************************************************/ + +static void am_max_out_window_scale(void) +{ + scale_mtof = max_scale_mtof; + scale_ftom = fixed_div(FRACUNIT, scale_mtof); + am_activate_new_scale(); +} + +/**************************************************************************** + * Name: am_change_window_scale + * + * Description: + * Zooming + * + ****************************************************************************/ + +static void am_change_window_scale(void) +{ + /* Change the scaling multipliers */ + + scale_mtof = fixed_mul(scale_mtof, mtof_zoommul); + scale_ftom = fixed_div(FRACUNIT, scale_mtof); + + if (scale_mtof < min_scale_mtof) + am_min_out_window_scale(); + else if (scale_mtof > max_scale_mtof) + am_max_out_window_scale(); + else + am_activate_new_scale(); +} + +/**************************************************************************** + * Name: am_do_follow_player + ****************************************************************************/ + +static void am_do_follow_player(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; + + /* m_x = FTOM(MTOF(plr->mo->x - m_w/2)); + * m_y = FTOM(MTOF(plr->mo->y - m_h/2)); + * m_x = plr->mo->x - m_w/2; + * m_y = plr->mo->y - m_h/2; + */ + } +} + +/**************************************************************************** + * Name: am_update_light_lev + ****************************************************************************/ + +static void am_update_light_lev(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 == arrlen(litelevels)) litelevelscnt = 0; + nexttic = amclock + 6 - (amclock % 6); + } +} + +/**************************************************************************** + * Name: am_clear_fb + * + * Description: + * Clear automap frame buffer. + * + ****************************************************************************/ + +static void am_clear_fb(int color) +{ + memset(fb, color, f_w * f_h * sizeof(*fb)); +} + +/**************************************************************************** + * Name: am_clip_m_line + * + * Description: + * 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. + * + ****************************************************************************/ + +static boolean am_clip_m_line(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; + } + else + { + tmp.x = 0; + tmp.y = 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 + +/**************************************************************************** + * Name: am_draw_f_line + * + * Description: + * Classic Bresenham w/ whatever optimizations needed for speed + * + ****************************************************************************/ + +static void am_draw_f_line(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 (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) + { + fprintf(stderr, "fuck %d \r", fuck++); + return; + } + +#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; + } + } +} + +/**************************************************************************** + * Name: am_draw_m_line + * + * Description: + * Clip lines, draw visible part sof lines. + * + ****************************************************************************/ + +void am_draw_m_line(mline_t *ml, int color) +{ + static fline_t fl; + + if (am_clip_m_line(ml, &fl)) + { + /* draws it on frame buffer using fb coords */ + + am_draw_f_line(&fl, color); + } +} + +/**************************************************************************** + * Name: am_draw_grid + * + * Description: + * Draws flat (floor/ceiling tile) aligned grid lines. + * + ****************************************************************************/ + +static void am_draw_grid(int color) +{ + fixed_t x, y; + fixed_t start; + fixed_t 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_draw_m_line(&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_draw_m_line(&ml, color); + } +} + +/**************************************************************************** + * Name: am_draw_walls + * + * Description: + * Determines visible lines, draws them. This is LineDef based, not LineSeg + * based. + * + ****************************************************************************/ + +static void am_draw_walls(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_draw_m_line(&l, WALLCOLORS + lightlev); + } + else + { + if (lines[i].special == 39) + { + /* teleporters */ + + am_draw_m_line(&l, WALLCOLORS + WALLRANGE / 2); + } + else if (lines[i].flags & ML_SECRET) /* secret door */ + { + if (cheating) + am_draw_m_line(&l, SECRETWALLCOLORS + lightlev); + else + am_draw_m_line(&l, WALLCOLORS + lightlev); + } + else if (lines[i].backsector->floorheight != + lines[i].frontsector->floorheight) + { + /* floor level change */ + + am_draw_m_line(&l, FDWALLCOLORS + lightlev); + } + else if (lines[i].backsector->ceilingheight != + lines[i].frontsector->ceilingheight) + { + /* ceiling level change */ + + am_draw_m_line(&l, CDWALLCOLORS + lightlev); + } + else if (cheating) + { + am_draw_m_line(&l, TSWALLCOLORS + lightlev); + } + } + } + else if (plr->powers[pw_allmap]) + { + if (!(lines[i].flags & LINE_NEVERSEE)) + { + am_draw_m_line(&l, GRAYS + 3); + } + } + } +} + +/**************************************************************************** + * Name: am_rotate + * + * Description: + * Rotation in 2D. + * Used to rotate player arrow line character. + * + ****************************************************************************/ + +static void am_rotate(fixed_t *x, fixed_t *y, angle_t a) +{ + fixed_t tmpx; + + tmpx = fixed_mul(*x, finecosine[a >> ANGLETOFINESHIFT]) - + fixed_mul(*y, finesine[a >> ANGLETOFINESHIFT]); + + *y = fixed_mul(*x, finesine[a >> ANGLETOFINESHIFT]) + + fixed_mul(*y, finecosine[a >> ANGLETOFINESHIFT]); + + *x = tmpx; +} + +static void am_draw_line_character(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 = fixed_mul(scale, l.a.x); + l.a.y = fixed_mul(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 = fixed_mul(scale, l.b.x); + l.b.y = fixed_mul(scale, l.b.y); + } + + if (angle) am_rotate(&l.b.x, &l.b.y, angle); + + l.b.x += x; + l.b.y += y; + + am_draw_m_line(&l, color); + } +} + +static void am_draw_players(void) +{ + int i; + player_t *p; + int their_color = -1; + int color; + + static int their_colors[] = + { + GREENS, GRAYS, BROWNS, REDS + }; + + if (!netgame) + { + if (cheating) + { + am_draw_line_character( + cheat_player_arrow, arrlen(cheat_player_arrow), 0, + plr->mo->angle, WHITE, plr->mo->x, plr->mo->y); + } + else + { + am_draw_line_character(player_arrow, arrlen(player_arrow), 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_draw_line_character(player_arrow, arrlen(player_arrow), 0, + p->mo->angle, color, p->mo->x, p->mo->y); + } +} + +static void am_draw_things(int colors, int colorrange) +{ + int i; + mobj_t *t; + + for (i = 0; i < numsectors; i++) + { + t = sectors[i].thinglist; + while (t) + { + am_draw_line_character(thintriangle_guy, arrlen(thintriangle_guy), + 16 << FRACBITS, t->angle, colors + lightlev, + t->x, t->y); + t = t->snext; + } + } +} + +static void am_draw_marks(void) +{ + int i; + int fx; + int fy; + int w; + int 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_draw_patch(fx, fy, marknums[i]); + } + } +} + +static void am_draw_crosshair(int color) +{ + fb[(f_w * (f_h + 1)) / 2] = color; /* single point for now */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: am_stop + ****************************************************************************/ + +void am_stop(void) +{ + static event_t st_notify = + { + 0, ev_keyup, AM_MSGEXITED, 0 + }; + + am_unload_pics(); + automapactive = false; + st_responder(&st_notify); + stopped = true; +} + +/**************************************************************************** + * Name: am_ticker + * + * Description: + * Updates on Game Tick + * + ****************************************************************************/ + +void am_ticker(void) +{ + if (!automapactive) return; + + amclock++; + + if (followplayer) am_do_follow_player(); + + /* Change the zoom if necessary */ + + if (ftom_zoommul != FRACUNIT) am_change_window_scale(); + + /* Change x,y location */ + + if (m_paninc.x || m_paninc.y) am_change_window_loc(); + + /* Update light level */ + + /* am_update_light_lev(); */ +} + +/**************************************************************************** + * Name: am_responder + * + * Description: + * Handle events (user inputs) in automap mode + * + ****************************************************************************/ + +boolean am_responder(event_t *ev) +{ + int rc; + static int bigstate = 0; + static char buffer[20]; + int key; + + rc = false; + + if (ev->type == ev_joystick && joybautomap >= 0 && + (ev->data1 & (1 << joybautomap)) != 0) + { + joywait = i_get_time() + 5; + + if (!automapactive) + { + am_start(); + viewactive = false; + } + else + { + bigstate = 0; + viewactive = true; + am_stop(); + } + + return true; + } + + if (!automapactive) + { + if (ev->type == ev_keydown && ev->data1 == key_map_toggle) + { + am_start(); + viewactive = false; + rc = true; + } + } + else if (ev->type == ev_keydown) + { + rc = true; + key = ev->data1; + + if (key == key_map_east) /* pan right */ + { + if (!followplayer) + m_paninc.x = FTOM(F_PANINC); + else + rc = false; + } + else if (key == key_map_west) /* pan left */ + { + if (!followplayer) + m_paninc.x = -FTOM(F_PANINC); + else + rc = false; + } + else if (key == key_map_north) /* pan up */ + { + if (!followplayer) + m_paninc.y = FTOM(F_PANINC); + else + rc = false; + } + else if (key == key_map_south) /* pan down */ + { + if (!followplayer) + m_paninc.y = -FTOM(F_PANINC); + else + rc = false; + } + else if (key == key_map_zoomout) /* zoom out */ + { + mtof_zoommul = M_ZOOMOUT; + ftom_zoommul = M_ZOOMIN; + } + else if (key == key_map_zoomin) /* zoom in */ + { + mtof_zoommul = M_ZOOMIN; + ftom_zoommul = M_ZOOMOUT; + } + else if (key == key_map_toggle) + { + bigstate = 0; + viewactive = true; + am_stop(); + } + else if (key == key_map_maxzoom) + { + bigstate = !bigstate; + if (bigstate) + { + am_save_scale_and_loc(); + am_min_out_window_scale(); + } + else + am_restore_scale_and_loc(); + } + else if (key == key_map_follow) + { + followplayer = !followplayer; + f_oldloc.x = INT_MAX; + if (followplayer) + plr->message = (AMSTR_FOLLOWON); + else + plr->message = (AMSTR_FOLLOWOFF); + } + else if (key == key_map_grid) + { + grid = !grid; + if (grid) + plr->message = (AMSTR_GRIDON); + else + plr->message = (AMSTR_GRIDOFF); + } + else if (key == key_map_mark) + { + snprintf(buffer, sizeof(buffer), "%s %d", (AMSTR_MARKEDSPOT), + markpointnum); + plr->message = buffer; + am_add_mark(); + } + else if (key == key_map_clearmark) + { + am_clear_marks(); + plr->message = (AMSTR_MARKSCLEARED); + } + else + { + rc = false; + } + + if ((!deathmatch || gameversion <= exe_doom_1_8) && + cht_check_cheat(&cheat_amap, ev->data2)) + { + rc = false; + cheating = (cheating + 1) % 3; + } + } + else if (ev->type == ev_keyup) + { + rc = false; + key = ev->data1; + + if (key == key_map_east) + { + if (!followplayer) m_paninc.x = 0; + } + else if (key == key_map_west) + { + if (!followplayer) m_paninc.x = 0; + } + else if (key == key_map_north) + { + if (!followplayer) m_paninc.y = 0; + } + else if (key == key_map_south) + { + if (!followplayer) m_paninc.y = 0; + } + else if (key == key_map_zoomout || key == key_map_zoomin) + { + mtof_zoommul = FRACUNIT; + ftom_zoommul = FRACUNIT; + } + } + + return rc; +} + +void am_drawer(void) +{ + if (!automapactive) return; + + am_clear_fb(BACKGROUND); + if (grid) am_draw_grid(GRIDCOLORS); + am_draw_walls(); + am_draw_players(); + if (cheating == 2) am_draw_things(THINGCOLORS, THINGRANGE); + am_draw_crosshair(XHAIRCOLORS); + + am_draw_marks(); + + v_mark_rect(f_x, f_y, f_w, f_h); +} diff --git a/games/NXDoom/src/doom/am_map.h b/games/NXDoom/src/doom/am_map.h new file mode 100644 index 00000000000..38b8e4ab179 --- /dev/null +++ b/games/NXDoom/src/doom/am_map.h @@ -0,0 +1,95 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/am_map.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * AutoMap module. + * + ****************************************************************************/ + +#ifndef __AMMAP_H__ +#define __AMMAP_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "m_cheat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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)) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern cheatseq_t cheat_amap; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: am_responder + * + * Description: + * Called by main loop. + * + ****************************************************************************/ + +boolean am_responder(event_t *ev); + +/**************************************************************************** + * Name: am_ticker + * + * Description: + * Called by main loop. + * + ****************************************************************************/ + +void am_ticker(void); + +/**************************************************************************** + * Name: am_ticker + * + * Description: + * Called by main loop. Called instead of view drawer if automap active. + * + ****************************************************************************/ + +void am_drawer(void); + +/**************************************************************************** + * Name: am_stop + * + * Description: + * Called to force the automap to quit if the level is completed while it is + * up. + * + ****************************************************************************/ + +void am_stop(void); + +#endif /* __AMMAP_H__ */ diff --git a/games/NXDoom/src/doom/d_englsh.h b/games/NXDoom/src/doom/d_englsh.h new file mode 100644 index 00000000000..3f0945fc9ac --- /dev/null +++ b/games/NXDoom/src/doom/d_englsh.h @@ -0,0 +1,695 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_englsh.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Printed strings for translation. + * English language support (default). + * + ****************************************************************************/ + +#ifndef __D_ENGLSH__ +#define __D_ENGLSH__ + +/* Printed strings for translation */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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 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 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 to dos.)" + +#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" + +/* DOOM end messages */ + +#define DOOM1_ENDMSG \ + "are you sure you want to\nquit this great game?", \ + "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 */ + +#define DOOM2_ENDMSG \ + "are you sure you want to\nquit this great game?", \ + "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.", + +#endif /* __D_ENGLSH__ */ diff --git a/games/NXDoom/src/doom/d_french.h b/games/NXDoom/src/doom/d_french.h new file mode 100644 index 00000000000..0252aa7471d --- /dev/null +++ b/games/NXDoom/src/doom/d_french.h @@ -0,0 +1,698 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_french.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Printed strings for translation. + * French language support. + * TODO: finish implementation + * WARN: Non-ascii text does not render properly. Find a solution or cope. + * + ****************************************************************************/ + +#ifndef __D_FRENCH__ +#define __D_FRENCH__ + +/* Printed strings for translation */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* D_Main.C */ + +#define D_DEVSTR "Mode développement activé.\n" +#define D_CDROM "Version CD-ROM: default.cfg de c:\\doomdata\n" + +/* M_Menu.C */ + +#define PRESSKEY "appuie une touche." +#define PRESSYN "appuie y ou n." +#define QUITMSG "es-tu certain que tu veux quitter ce super jeu ?" +#define LOADNET "tu ne peux pas charger pendant un jeu en ligne!\n\n" PRESSKEY +#define QLOADNET \ + "tu ne peux pas charger rapidement pendant un jeu en ligne!\n\n" PRESSKEY +#define QSAVESPOT "tu n'as pas choisi un slot sauvegarde rapide\n\n" PRESSKEY +#define SAVEDEAD "tu ne peux pas sauvegarder si tu ne joue pas\n\n" PRESSKEY +#define QSPROMPT "sauvegarde ton jeu nommé\n\n'%s'?\n\n" PRESSYN +#define QLPROMPT \ + "tu veux charger rapidement le jeu nommé\n\n'%s'?\n\n" PRESSYN + +#define NEWGAME \ + "tu ne peux pas commencer un nouveau jeu\n" \ + "durant un jeu en ligne.\n\n" PRESSKEY + +#define NIGHTMARE \ + "es-tu sure? ce niveau\n" \ + "n'est absolument pas juste.\n\n" PRESSYN + +#define SWSTRING \ + "ceci est le version shareware de doom.\n\n" \ + "tu besoin de commander la trilogie entiere.\n\n" PRESSKEY + +#define MSGOFF "Messages DÉSACTIVÉS" +#define MSGON "Messages ACTIVÉS" +#define NETEND "tu ne peux pas quitter un jeu en ligne!\n\n" PRESSKEY +#define ENDGAME "es-tu sure que tu veux quitter le jeu?\n\n" PRESSYN + +#define DOSY "(appuie y pour quitter vers dos.)" + +#define DETAILHI "Haute définition" +#define DETAILLO "Faible définition" +#define GAMMALVL0 "Correction gamma DÉSACTIVÉ" +#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 "slot vide" + +/* P_inter.C */ + +#define GOTARMOR "Ramassé l'armure." +#define GOTMEGA "Ramassé le Mega Armure!" +#define GOTHTHBONUS "Ramassé un bonus de santé." +#define GOTARMBONUS "Ramassé un bonus d'armure." +#define GOTSTIM "Ramassé un stimpack." +#define GOTMEDINEED "Ramassé un medikit que tu as VRAIMENT besoin!" +#define GOTMEDIKIT "Ramassé un medikit." +#define GOTSUPER "Superchargé!" + +#define GOTBLUECARD "Ramassé une carte d'accès bleue." +#define GOTYELWCARD "Ramassé une carte d'accès jaune." +#define GOTREDCARD "Ramassé une carte d'accès rouge." +#define GOTBLUESKUL "Ramassé une clé crâne bleue." +#define GOTYELWSKUL "Ramassé une clé crâne jaune." +#define GOTREDSKULL "Ramassé une clé crâne rouge." + +#define GOTINVUL "Invulnérabilité!" +#define GOTBERSERK "Berserk!" +#define GOTINVIS "Invisibilité Partielle" +#define GOTSUIT "Radiation Shielding Suit" // TODO? +#define GOTMAP "Carte de la Zone Informatique" +#define GOTVISOR "Light Amplification Visor" // TODO? +#define GOTMSPHERE "MegaSphere!" + +#define GOTCLIP "Ramassé un clip." +#define GOTCLIPBOX "Ramassé une boîte de balles." +#define GOTROCKET "Ramassé une fusée." +#define GOTROCKBOX "Ramassé une boîte de fusées." +#define GOTCELL "Ramassé une cellule énergétique." +#define GOTCELLBOX "Ramassé un paquet de cellules énergétiques." +#define GOTSHELLS "Ramassé 4 cartouches de fusil de chasse." +#define GOTSHELLBOX "Ramassé une boîte de cartouches de fusil de chasse." +#define GOTBACKPACK "Ramassé une sac à dos plein d'ammo!" + +#define GOTBFG9000 "Tu as eu le BFG9000! Hohoho, oui!" +#define GOTCHAINGUN "Tu as eu le chaingun!" +#define GOTCHAINSAW "Un scie à chaîne! Trouve de la viande!" +#define GOTLAUNCHER "Tu as eu la lance-roquettes!" +#define GOTPLASMA "Tu as eu le pistolet à plasma!" +#define GOTSHOTGUN "Tu as eu le fusil de chasse!" +#define GOTSHOTGUN2 "Tu as eu le super fusil de chasse!" + +/* 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 "niveau 1: entryway" +#define HUSTR_2 "niveau 2: underhalls" +#define HUSTR_3 "niveau 3: the gantlet" +#define HUSTR_4 "niveau 4: the focus" +#define HUSTR_5 "niveau 5: the waste tunnels" +#define HUSTR_6 "niveau 6: the crusher" +#define HUSTR_7 "niveau 7: dead simple" +#define HUSTR_8 "niveau 8: tricks and traps" +#define HUSTR_9 "niveau 9: the pit" +#define HUSTR_10 "niveau 10: refueling base" +#define HUSTR_11 "niveau 11: 'o' of destruction!" + +#define HUSTR_12 "niveau 12: the factory" +#define HUSTR_13 "niveau 13: downtown" +#define HUSTR_14 "niveau 14: the inmost dens" +#define HUSTR_15 "niveau 15: industrial zone" +#define HUSTR_16 "niveau 16: suburbs" +#define HUSTR_17 "niveau 17: tenements" +#define HUSTR_18 "niveau 18: the courtyard" +#define HUSTR_19 "niveau 19: the citadel" +#define HUSTR_20 "niveau 20: gotcha!" + +#define HUSTR_21 "niveau 21: nirvana" +#define HUSTR_22 "niveau 22: the catacombs" +#define HUSTR_23 "niveau 23: barrels o' fun" +#define HUSTR_24 "niveau 24: the chasm" +#define HUSTR_25 "niveau 25: bloodfalls" +#define HUSTR_26 "niveau 26: the abandoned mines" +#define HUSTR_27 "niveau 27: monster condo" +#define HUSTR_28 "niveau 28: the spirit world" +#define HUSTR_29 "niveau 29: the living end" +#define HUSTR_30 "niveau 30: icon of sin" + +#define HUSTR_31 "niveau 31: wolfenstein" +#define HUSTR_32 "niveau 32: grosse" + +#define PHUSTR_1 "niveau 1: congo" +#define PHUSTR_2 "niveau 2: well of souls" +#define PHUSTR_3 "niveau 3: aztec" +#define PHUSTR_4 "niveau 4: caged" +#define PHUSTR_5 "niveau 5: ghost town" +#define PHUSTR_6 "niveau 6: baron's lair" +#define PHUSTR_7 "niveau 7: caughtyard" +#define PHUSTR_8 "niveau 8: realm" +#define PHUSTR_9 "niveau 9: abattoire" +#define PHUSTR_10 "niveau 10: onslaught" +#define PHUSTR_11 "niveau 11: hunted" + +#define PHUSTR_12 "niveau 12: speed" +#define PHUSTR_13 "niveau 13: the crypt" +#define PHUSTR_14 "niveau 14: genesis" +#define PHUSTR_15 "niveau 15: the twilight" +#define PHUSTR_16 "niveau 16: the omen" +#define PHUSTR_17 "niveau 17: compound" +#define PHUSTR_18 "niveau 18: neurosphere" +#define PHUSTR_19 "niveau 19: nme" +#define PHUSTR_20 "niveau 20: the death domain" + +#define PHUSTR_21 "niveau 21: slayer" +#define PHUSTR_22 "niveau 22: impossible mission" +#define PHUSTR_23 "niveau 23: tombstone" +#define PHUSTR_24 "niveau 24: the final frontier" +#define PHUSTR_25 "niveau 25: the temple of darkness" +#define PHUSTR_26 "niveau 26: bunker" +#define PHUSTR_27 "niveau 27: anti-christ" +#define PHUSTR_28 "niveau 28: the sewers" +#define PHUSTR_29 "niveau 29: odyssey of noises" +#define PHUSTR_30 "niveau 30: the gateway of hell" + +#define PHUSTR_31 "niveau 31: cyberden" +#define PHUSTR_32 "niveau 32: go 2 it" + +#define THUSTR_1 "niveau 1: system control" +#define THUSTR_2 "niveau 2: human bbq" +#define THUSTR_3 "niveau 3: power control" +#define THUSTR_4 "niveau 4: wormhole" +#define THUSTR_5 "niveau 5: hanger" +#define THUSTR_6 "niveau 6: open season" +#define THUSTR_7 "niveau 7: prison" +#define THUSTR_8 "niveau 8: metal" +#define THUSTR_9 "niveau 9: stronghold" +#define THUSTR_10 "niveau 10: redemption" +#define THUSTR_11 "niveau 11: storage facility" + +#define THUSTR_12 "niveau 12: crater" +#define THUSTR_13 "niveau 13: nukage processing" +#define THUSTR_14 "niveau 14: steel works" +#define THUSTR_15 "niveau 15: dead zone" +#define THUSTR_16 "niveau 16: deepest reaches" +#define THUSTR_17 "niveau 17: processing area" +#define THUSTR_18 "niveau 18: mill" +#define THUSTR_19 "niveau 19: shipping/respawning" +#define THUSTR_20 "niveau 20: central processing" + +#define THUSTR_21 "niveau 21: administration center" +#define THUSTR_22 "niveau 22: habitat" +#define THUSTR_23 "niveau 23: lunar mining project" +#define THUSTR_24 "niveau 24: quarry" +#define THUSTR_25 "niveau 25: baron's den" +#define THUSTR_26 "niveau 26: ballistyx" +#define THUSTR_27 "niveau 27: mount pain" +#define THUSTR_28 "niveau 28: heck" +#define THUSTR_29 "niveau 29: river styx" +#define THUSTR_30 "niveau 30: last call" + +#define THUSTR_31 "niveau 31: pharaoh" +#define THUSTR_32 "niveau 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 "Oui" +#define HUSTR_CHATMACRO0 "Non" + +#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 "Vert: " +#define HUSTR_PLRINDIGO "Indigo: " +#define HUSTR_PLRBROWN "Brun: " +#define HUSTR_PLRRED "Rouge: " + +#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 niveau 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 niveau 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 niveau 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 niveau 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 niveau 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 niveau 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 niveau we\n" \ + "got. Hope you have a saved game a niveau 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" \ + "niveau 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" + +/* DOOM end messages */ + +#define DOOM1_ENDMSG \ + "are you sure you want to\nquit this great game?", \ + "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 */ + +#define DOOM2_ENDMSG \ + "are you sure you want to\nquit this great game?", \ + "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.", + +#endif /* __D_FRENCH__ */ diff --git a/games/NXDoom/src/doom/d_items.c b/games/NXDoom/src/doom/d_items.c new file mode 100644 index 00000000000..8fc606b6c47 --- /dev/null +++ b/games/NXDoom/src/doom/d_items.c @@ -0,0 +1,143 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_items.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* We are referring to sprite numbers. */ + +#include "info.h" + +#include "d_items.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* PSPRITE ACTIONS for weapons. + * This struct controls the weapon animations. + * + * Each entry is: + * ammo/ammunition 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, + }, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/d_items.h b/games/NXDoom/src/doom/d_items.h new file mode 100644 index 00000000000..bcc50b9af68 --- /dev/null +++ b/games/NXDoom/src/doom/d_items.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_items.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Items: key cards, artifacts, weapon, ammunition. + * + ****************************************************************************/ + +#ifndef __D_ITEMS__ +#define __D_ITEMS__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Weapon info: sprite frames, ammunition use. */ + +typedef struct +{ + ammotype_t ammo; + int upstate; + int downstate; + int readystate; + int atkstate; + int flashstate; +} weaponinfo_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern weaponinfo_t weaponinfo[NUMWEAPONS]; + +#endif /* __D_ITEMS__ */ diff --git a/games/NXDoom/src/doom/d_main.c b/games/NXDoom/src/doom/d_main.c new file mode 100644 index 00000000000..52df6eda110 --- /dev/null +++ b/games/NXDoom/src/doom/d_main.c @@ -0,0 +1,2184 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_main.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * DOOM main program (d_doom_main) 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "config.h" +#include "deh_main.h" +#include "doomdef.h" +#include "doomstat.h" + +#include "dstrings.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "d_iwad.h" + +#include "v_diskicon.h" +#include "v_video.h" +#include "w_main.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "f_finale.h" +#include "f_wipe.h" + +#include "m_argv.h" +#include "m_config.h" +#include "m_controls.h" +#include "m_menu.h" +#include "m_misc.h" +#include "p_saveg.h" + +#include "i_endoom.h" +#include "i_input.h" +#include "i_joystick.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" + +#include "g_game.h" + +#include "am_map.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "wi_stuff.h" + +#ifdef CONFIG_GAMES_NXDOOM_NET +#include "net_client.h" +#include "net_dedicated.h" +#include "net_query.h" +#endif + +#include "p_setup.h" +#include "r_local.h" +#include "statdump.h" + +#include "d_main.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct pack +{ + const char *name; + int mission; +}; + +struct gameversion +{ + const char *description; + const char *cmdline; + game_version_t version; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* D-DoomLoop() + * + * Not a globally visible function, just included for source reference, + * called by d_doom_main, never exits. + * + * Manages timing and IO, calls all ?_Responder, ?_Ticker, and ?_Drawer, + * calls i_get_time, i_start_frame, and i_start_tic + */ + +void d_doomloop(void); + +void d_connect_net_game(void); +void d_check_net_game(void); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Location where savegames are stored */ + +char *savegamedir; + +/* location of IWAD and WAD files */ + +char *iwadfile; + +boolean devparm; /* started game with -devparm */ +boolean nomonsters; /* checkparm of -nomonsters */ +boolean respawnparm; /* checkparm of -respawn */ +boolean fastparm; /* checkparm of -fast */ + +skill_t startskill; +int startepisode; +int startmap; +boolean autostart; +int startloadgame; + +boolean advancedemo; + +/* Store demo, do not accept any inputs */ + +boolean storedemo; + +/* If true, the main game loop has started. */ + +boolean main_loop_started = false; + +int show_endoom = 1; +int show_diskicon = 1; + +/* wipegamestate can be set to -1 to force a wipe on the next draw */ + +gamestate_t wipegamestate = GS_DEMOSCREEN; + +/* DEMO LOOP */ + +int demosequence; +int pagetic; +const char *pagename; + +/* print title for every printed line */ + +char title[128]; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Copyright message banners + * Some dehacked mods replace these. These are only displayed if they are + * replaced by dehacked. + */ + +static const char *g_copyright_banners[] = +{ + "========================================================================" + "===\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", + + "========================================================================" + "===\n" + " Commercial product - do not distribute!\n" + " Please report software piracy to the SPA: 1-800-388-PIR8\n" + "========================================================================" + "===\n", + + "========================================================================" + "===\n" + " Shareware!\n" + "========================================================================" + "===\n", +}; + +/* These are the lumps that will be checked in IWAD, + * if any one is not present, execution will be aborted. + */ + +static char g_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", +}; + +static char *g_gamedescription; + +/* Add configuration file variable bindings. */ + +static const char *const g_chat_macro_defaults[10] = +{ + HUSTR_CHATMACRO0, HUSTR_CHATMACRO1, HUSTR_CHATMACRO2, HUSTR_CHATMACRO3, + HUSTR_CHATMACRO4, HUSTR_CHATMACRO5, HUSTR_CHATMACRO6, HUSTR_CHATMACRO7, + HUSTR_CHATMACRO8, HUSTR_CHATMACRO9, +}; + +/* Strings for dehacked replacements of the startup banner + * + * These are from the original source: some of them are perhaps + * not used in any dehacked patches + */ + +static const char *g_banners[] = +{ + /* doom2.wad */ + + " " + "DOOM 2: Hell on Earth v%i.%i" + " ", + + /* doom2.wad v1.666 */ + + " " + "DOOM 2: Hell on Earth v%i.%i66" + " ", + + /* doom1.wad */ + + " " + "DOOM Shareware Startup v%i.%i" + " ", + + /* doom.wad */ + + " " + "DOOM Registered Startup v%i.%i" + " ", + + /* Registered DOOM uses this */ + + " " + "DOOM System Startup v%i.%i" + " ", + + /* Doom v1.666 */ + + " " + "DOOM System Startup v%i.%i66" + " " + + /* doom.wad (Ultimate DOOM) */ + + " " + "The Ultimate DOOM Startup v%i.%i" + " ", + + /* tnt.wad */ + + " " + "DOOM 2: TNT - Evilution v%i.%i" + " ", + + /* plutonia.wad */ + + " " + "DOOM 2: Plutonia Experiment v%i.%i" + " ", +}; + +static const struct gameversion g_gameversions[] = +{ + {"Doom 1.2", "1.2", exe_doom_1_2}, + {"Doom 1.5", "1.5", exe_doom_1_5}, + {"Doom 1.666", "1.666", exe_doom_1_666}, + {"Doom 1.7/1.7a", "1.7", exe_doom_1_7}, + {"Doom 1.8", "1.8", exe_doom_1_8}, + {"Doom 1.9", "1.9", exe_doom_1_9}, + {"Hacx", "hacx", exe_hacx}, + {"Ultimate Doom", "ultimate", exe_ultimate}, + {"Final Doom", "final", exe_final}, + {"Final Doom (alt)", "final2", exe_final2}, + {"Chex Quest", "chex", exe_chex}, + {NULL, NULL, 0}, +}; + +static const struct pack g_packs[] = +{ + {"doom2", doom2}, + {"tnt", pack_tnt}, + {"plutonia", pack_plut}, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void enable_loading_disk(void) +{ + const char *disk_lump_name; + + if (show_diskicon) + { + if (m_check_parm("-cdrom") > 0) + { + disk_lump_name = ("STCDROM"); + } + else + { + disk_lump_name = ("STDISK"); + } + + v_enable_loading_disk(disk_lump_name, SCREENWIDTH - LOADING_DISK_W, + SCREENHEIGHT - LOADING_DISK_H); + } +} + +static void d_bind_variables(void) +{ + int i; + + m_apply_platform_defaults(); + + i_bind_input_variables(); + i_bind_video_variables(); + i_bind_joystick_variables(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + i_bind_sound_variables(); +#endif + + m_bind_base_controls(); + m_bind_weapon_controls(); + m_bind_map_controls(); + m_bind_menu_controls(); + m_bind_chat_controls(MAXPLAYERS); + + key_multi_msgplayer[0] = HUSTR_KEYGREEN; + key_multi_msgplayer[1] = HUSTR_KEYINDIGO; + key_multi_msgplayer[2] = HUSTR_KEYBROWN; + key_multi_msgplayer[3] = HUSTR_KEYRED; + +#ifdef CONFIG_GAMES_NXDOOM_NET + net_bind_variables(); +#endif + + m_bind_int_variable("mouse_sensitivity", &g_mouse_sensitivity); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + m_bind_int_variable("sfx_volume", &g_sfx_volume); + m_bind_int_variable("music_volume", &g_music_volume); + m_bind_int_variable("snd_channels", &snd_channels); +#endif + m_bind_int_variable("show_messages", &g_show_messages); + m_bind_int_variable("screenblocks", &screenblocks); + m_bind_int_variable("detaillevel", &g_detail_level); + m_bind_int_variable("vanilla_savegame_limit", &vanilla_savegame_limit); + m_bind_int_variable("vanilla_demo_limit", &vanilla_demo_limit); + m_bind_int_variable("show_endoom", &show_endoom); + m_bind_int_variable("show_diskicon", &show_diskicon); + + /* Multiplayer chat macros */ + + for (i = 0; i < 10; ++i) + { + char buf[12]; + + chat_macros[i] = m_string_duplicate(g_chat_macro_defaults[i]); + snprintf(buf, sizeof(buf), "chatmacro%i", i); + m_bind_string_variable(buf, &chat_macros[i]); + } +} + +/* Function called at exit to display the ENDOOM screen */ + +static void d_endoom(void) +{ +#ifdef CONFIG_GAMES_NXDOOM_ENDOOM + byte *endoom; + + /* Don't show ENDOOM if we have it disabled, or we're running + * in screensaver or control test mode. Only show it once the + * game has actually started. + */ + + if (!show_endoom || !main_loop_started || screensaver_mode || + m_check_parm("-testcontrols") > 0) + { + return; + } + + endoom = w_cache_lump_name(("ENDOOM"), PU_STATIC); + + i_endoom(endoom); +#else + return; +#endif +} + +static boolean is_french_iwad(void) +{ + return (gamemission == doom2 && w_check_num_for_name("M_RDTHIS") < 0 && + w_check_num_for_name("M_EPISOD") < 0 && + w_check_num_for_name("M_EPI1") < 0 && + w_check_num_for_name("M_EPI2") < 0 && + w_check_num_for_name("M_EPI3") < 0 && + w_check_num_for_name("WIOSTF") < 0 && + w_check_num_for_name("WIOBJ") >= 0); +} + +/* Load dehacked patches needed for certain IWADs. */ + +static void load_iwad_deh(void) +{ + /* The Freedoom IWADs have DEHACKED lumps that must be loaded. */ + + if (gamevariant == freedoom || gamevariant == freedm) + { + /* Old versions of Freedoom (before 2014-09) did not have technically + * valid DEHACKED lumps, so ignore errors and just continue if this + * is an old IWAD. + */ + + deh_load_lump_by_name("DEHACKED", false, true); + } + + /* If this is the HACX IWAD, we need to load the DEHACKED lump. */ + + if (gameversion == exe_hacx) + { + if (!deh_load_lump_by_name("DEHACKED", true, false)) + { + i_error("DEHACKED lump not found. Please check that this is the " + "Hacx v1.2 IWAD."); + } + } + + /* Chex Quest needs a separate Dehacked patch which must be downloaded + * and installed next to the IWAD. + */ + + if (gameversion == exe_chex) + { + char *chex_deh = NULL; + char *dirname; + + /* Look for chex.deh in the same directory as the IWAD file. */ + + dirname = m_dir_name(iwadfile); + chex_deh = m_string_join(dirname, DIR_SEPARATOR_S, "chex.deh", NULL); + free(dirname); + + /* If the dehacked patch isn't found, try searching the WAD + * search path instead. We might find it... + */ + + if (!m_file_exists(chex_deh)) + { + free(chex_deh); + chex_deh = d_find_wad_by_name("chex.deh"); + } + + /* Still not found? */ + + if (chex_deh == NULL) + { + i_error("Unable to find Chex Quest dehacked file (chex.deh).\n" + "The dehacked file is required in order to emulate\n" + "chex.exe correctly. It can be found in your nearest\n" + "/idgames repository mirror at:\n\n" + " themes/chex/chexdeh.zip"); + } + + if (!deh_loadfile(chex_deh)) + { + i_error("Failed to load chex.deh needed for emulating chex.exe."); + } + } + + if (is_french_iwad()) + { + char *french_deh = NULL; + char *dirname; + + /* Look for french.deh in the same directory as the IWAD file. */ + + dirname = m_dir_name(iwadfile); + french_deh = + m_string_join(dirname, DIR_SEPARATOR_S, "french.deh", NULL); + printf("French version\n"); + free(dirname); + + /* If the dehacked patch isn't found, try searching the WAD + * search path instead. We might find it... + */ + + if (!m_file_exists(french_deh)) + { + free(french_deh); + french_deh = d_find_wad_by_name("french.deh"); + } + + /* Still not found? */ + + if (french_deh == NULL) + { + i_error( + "Unable to find French Doom II dehacked file\n" + "(french.deh). The dehacked file is required in order to\n" + "emulate French doom2.exe correctly. It can be found in\n" + "your nearest /idgames repository mirror at:\n\n" + " utils/exe_edit/patches/french.zip" + ); + } + + if (!deh_loadfile(french_deh)) + { + i_error("Failed to load french.deh needed for emulating French\n" + "doom2.exe."); + } + } +} + +static void g_check_demo_status_at_exit(void) +{ + g_check_demo_status(); +} + +/* draw current display, possibly wiping it from the previous */ + +static boolean d_display(void) +{ + static boolean viewactivestate = false; + static boolean g_menuactivestate = false; + static boolean inhelpscreensstate = false; + static boolean d_fullscreen = false; + static gamestate_t oldgamestate = -1; + static int borderdrawcount; + int y; + boolean wipe; + boolean redrawsbar; + + redrawsbar = false; + + /* change the view size if needed */ + + if (setsizeneeded) + { + r_execute_set_view_size(); + oldgamestate = -1; /* force background redraw */ + borderdrawcount = 3; + } + + /* save the current screen if about to wipe */ + + if (gamestate != wipegamestate) + { + wipe = true; + wipe_start_screen(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 != SCREENHEIGHT && d_fullscreen)) + { + redrawsbar = true; + } + + if (inhelpscreensstate && !inhelpscreens) + { + redrawsbar = true; /* just put away the help screen */ + } + + st_drawer(viewheight == SCREENHEIGHT, redrawsbar); + d_fullscreen = viewheight == SCREENHEIGHT; + break; + + case GS_INTERMISSION: + wi_drawer(); + break; + + case GS_FINALE: + f_drawer(); + break; + + case GS_DEMOSCREEN: + d_page_drawer(); + break; + } + + /* draw buffered stuff to screen */ + + i_update_no_blit(); + + /* draw the view directly */ + + if (gamestate == GS_LEVEL && !automapactive && gametic) + { + r_render_player_view(&players[displayplayer]); + } + + if (gamestate == GS_LEVEL && gametic) + { + hu_drawer(); + } + + /* clean up border stuff */ + + if (gamestate != oldgamestate && gamestate != GS_LEVEL) + i_set_palette(w_cache_lump_name(("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_fill_back_screen(); /* draw the pattern into the back screen */ + } + + /* see if the border needs to be updated to the screen */ + + if (gamestate == GS_LEVEL && !automapactive && + scaledviewwidth != SCREENWIDTH) + { + if (g_menuactive || g_menuactivestate || !viewactivestate) + borderdrawcount = 3; + if (borderdrawcount) + { + r_draw_view_border(); /* erase old menu stuff */ + borderdrawcount--; + } + } + + if (testcontrols) + { + /* Box showing current mouse speed */ + + v_draw_mouse_speed_box(testcontrols_mousespeed); + } + + g_menuactivestate = g_menuactive; + viewactivestate = viewactive; + inhelpscreensstate = inhelpscreens; + oldgamestate = wipegamestate = gamestate; + + /* draw pause pic */ + + if (paused) + { + if (automapactive) + y = 4; + else + y = viewwindowy + 4; + v_draw_patch_direct(viewwindowx + (scaledviewwidth - 68) / 2, y, + w_cache_lump_name(("M_PAUSE"), PU_CACHE)); + } + + /* menus go directly to the screen */ + + m_drawer(); /* menu is drawn even on top of everything */ + net_update(); /* send out any new accumulation */ + + return wipe; +} + +/**************************************************************************** + * Name: d_grab_mouse_callback + * + * Description: + * Called to determine whether to grab the mouse pointer + * + ****************************************************************************/ + +static boolean d_grab_mouse_callback(void) +{ + /* Drone players don't need mouse focus */ + +#ifdef CONFIG_GAMES_NXDOOM_NET + if (drone) return false; +#endif + + /* when menu is active or game is paused, release the mouse */ + + if (g_menuactive || paused) return false; + + /* only grab mouse when playing levels (but not demos) */ + + return (gamestate == GS_LEVEL) && !demoplayback && !advancedemo; +} + +/**************************************************************************** + * Name: d_run_frame + ****************************************************************************/ + +static void d_run_frame(void) +{ + int nowtime; + int tics; + static int wipestart; + static boolean wipe; + + if (wipe) + { + do + { + nowtime = i_get_time(); + tics = nowtime - wipestart; + usleep(1000); + } + while (tics <= 0); + + wipestart = nowtime; + wipe = !wipe_screen_wipe(WIPE_MELT, 0, 0, + SCREENWIDTH, SCREENHEIGHT, tics); + i_update_no_blit(); + m_drawer(); /* menu is drawn even on top of wipes */ + i_finish_update(); /* page flip or blit buffer */ + return; + } + + /* frame synchronous IO operations */ + + i_start_frame(); + + try_run_tics(); /* will run at least one tic */ + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_update_sounds(players[consoleplayer].mo); /* move positional sounds */ +#endif + + /* Update display, next frame, with current state if no profiling is on */ + + if (screenvisible && !nodrawers) + { + if ((wipe = d_display())) + { + /* start wipe on this frame */ + + wipe_endscreen(0, 0, SCREENWIDTH, SCREENHEIGHT); + + wipestart = i_get_time() - 1; + } + else + { + /* normal update */ + + i_finish_update(); /* page flip or blit buffer */ + } + } +} + +/* Get game name: if the startup banner has been replaced, use that. + * Otherwise, use the name given + */ + +static char *get_game_name(const char *gamename) +{ + size_t i; + + for (i = 0; i < arrlen(g_banners); ++i) + { + const char *deh_sub; + + /* Has the banner been replaced? */ + + deh_sub = (g_banners[i]); + + if (deh_sub != g_banners[i]) + { + size_t gamename_size; + int version; + char *deh_gamename; + + /* Has been replaced. + * We need to expand via printf to include the Doom version number + * We also need to cut off spaces to get the basic name + */ + + gamename_size = strlen(deh_sub) + 10; + deh_gamename = malloc(gamename_size); + if (deh_gamename == NULL) + { + i_error("GetGameName: Failed to allocate new string"); + } + + version = g_vanilla_version_code(); + snprintf(deh_gamename, gamename_size, g_banners[i], version / 100, + version % 100); + + while (deh_gamename[0] != '\0' && isspace(deh_gamename[0])) + { + memmove(deh_gamename, deh_gamename + 1, gamename_size - 1); + } + + while (deh_gamename[0] != '\0' && + isspace(deh_gamename[strlen(deh_gamename) - 1])) + { + deh_gamename[strlen(deh_gamename) - 1] = '\0'; + } + + return deh_gamename; + } + } + + return m_string_duplicate(gamename); +} + +static void set_mission_for_pack_name(const char *pack_name) +{ + int i; + + for (i = 0; i < arrlen(g_packs); ++i) + { + if (!strcasecmp(pack_name, g_packs[i].name)) + { + gamemission = g_packs[i].mission; + return; + } + } + + printf("Valid mission packs are:\n"); + + for (i = 0; i < arrlen(g_packs); ++i) + { + printf("\t%s\n", g_packs[i].name); + } + + i_error("Unknown mission pack name: %s", pack_name); +} + +/* Find out what version of Doom is playing. */ + +static void d_identify_version(void) +{ + /* gamemission is set up by the d_find_iwad function. But if + * we specify '-iwad', we have to identify using + * IdentifyIWADByName. However, if the iwad does not match + * any known IWAD name, we may have a dilemma. Try to + * identify by its contents. + */ + + if (gamemission == none) + { + unsigned int i; + + for (i = 0; i < numlumps; ++i) + { + if (!strncasecmp(lumpinfo[i]->name, "MAP01", 8)) + { + gamemission = doom2; + break; + } + else if (!strncasecmp(lumpinfo[i]->name, "E1M1", 8)) + { + gamemission = doom; + break; + } + } + + if (gamemission == none) + { + /* Still no idea. I don't think this is going to work. */ + + i_error("Unknown or invalid IWAD file."); + } + } + + /* Make sure gamemode is set up correctly */ + + if (logical_gamemission == doom) + { + /* Doom 1. But which version? */ + + if (w_check_num_for_name("E4M1") > 0) + { + /* Ultimate Doom */ + + gamemode = retail; + } + else if (w_check_num_for_name("E3M1") > 0) + { + gamemode = registered; + } + else + { + gamemode = shareware; + } + } + else + { + int p; + + /* Doom 2 of some kind. */ + + gamemode = commercial; + + /* We can manually override the gamemission that we got from the + * IWAD detection code. This allows us to eg. play Plutonia 2 + * with Freedoom and get the right level names. + */ + + /* @category compat + * @arg + * + * Explicitly specify a Doom II "mission pack" to run as, instead of + * detecting it based on the filename. Valid values are: "doom2", + * "tnt" and "plutonia". + * + */ + + p = m_check_parm_with_args("-pack", 1); + if (p > 0) + { + set_mission_for_pack_name(myargv[p + 1]); + } + } +} + +/* Set the gamedescription string */ + +static void d_set_game_description(void) +{ + if (logical_gamemission == doom) + { + /* Doom 1. But which version? */ + + if (gamevariant == freedoom) + { + g_gamedescription = get_game_name("Freedoom: Phase 1"); + } + else if (gamemode == retail) + { + /* Ultimate Doom */ + + g_gamedescription = get_game_name("The Ultimate DOOM"); + } + else if (gamemode == registered) + { + g_gamedescription = get_game_name("DOOM Registered"); + } + else if (gamemode == shareware) + { + g_gamedescription = get_game_name("DOOM Shareware"); + } + } + else + { + /* Doom 2 of some kind. But which mission? */ + + if (gamevariant == freedm) + { + g_gamedescription = get_game_name("FreeDM"); + } + else if (gamevariant == freedoom) + { + g_gamedescription = get_game_name("Freedoom: Phase 2"); + } + else if (logical_gamemission == doom2) + { + g_gamedescription = get_game_name("DOOM 2: Hell on Earth"); + } + else if (logical_gamemission == pack_plut) + { + g_gamedescription = get_game_name("DOOM 2: Plutonia Experiment"); + } + else if (logical_gamemission == pack_tnt) + { + g_gamedescription = get_game_name("DOOM 2: TNT - Evilution"); + } + } + + if (g_gamedescription == NULL) + { + g_gamedescription = m_string_duplicate("Unknown"); + } +} + +static boolean d_addfile(char *filename) +{ + wad_file_t *handle; + + printf(" adding %s\n", filename); + handle = w_add_file(filename); + + return handle != NULL; +} + +/* Prints a message only if it has been modified by dehacked. */ + +static void print_dehacked_banners(void) +{ + size_t i; + + for (i = 0; i < arrlen(g_copyright_banners); ++i) + { + const char *deh_s; + + deh_s = (g_copyright_banners[i]); + + if (deh_s != g_copyright_banners[i]) + { + printf("%s", deh_s); + + /* Make sure the modified banner always ends in a newline + * character. If it doesn't, add a newline. This fixes av.wad. + */ + + if (deh_s[strlen(deh_s) - 1] != '\n') + { + printf("\n"); + } + } + } +} + +/* Initialize the game version */ + +static void init_game_version(void) +{ + byte *demolump; + char demolumpname[6]; + int demoversion; + int p; + int i; + boolean status; + + /* @arg + * @category compat + * + * Emulate a specific version of Doom. Valid values are "1.2", + * "1.5", "1.666", "1.7", "1.8", "1.9", "ultimate", "final", + * "final2", "hacx" and "chex". + * + */ + + p = m_check_parm_with_args("-gameversion", 1); + + if (p) + { + for (i = 0; g_gameversions[i].description != NULL; ++i) + { + if (!strcmp(myargv[p + 1], g_gameversions[i].cmdline)) + { + gameversion = g_gameversions[i].version; + break; + } + } + + if (g_gameversions[i].description == NULL) + { + printf("Supported game versions:\n"); + + for (i = 0; g_gameversions[i].description != NULL; ++i) + { + printf("\t%s (%s)\n", g_gameversions[i].cmdline, + g_gameversions[i].description); + } + + i_error("Unknown game version '%s'", myargv[p + 1]); + } + } + else + { + /* Determine automatically */ + + if (gamemission == pack_chex) + { + /* chex.exe - identified by iwad filename */ + + gameversion = exe_chex; + } + else if (gamemission == pack_hacx) + { + /* hacx.exe: identified by iwad filename */ + + gameversion = exe_hacx; + } + else if (gamemode == shareware || gamemode == registered || + (gamemode == commercial && gamemission == doom2)) + { + /* original */ + + gameversion = exe_doom_1_9; + + /* Detect version from demo lump */ + + for (i = 1; i <= 3; ++i) + { + snprintf(demolumpname, 6, "demo%i", i); + if (w_check_num_for_name(demolumpname) > 0) + { + demolump = w_cache_lump_name(demolumpname, PU_STATIC); + demoversion = demolump[0]; + w_release_lump_name(demolumpname); + status = true; + switch (demoversion) + { + case 0: + case 1: + case 2: + case 3: + case 4: + gameversion = exe_doom_1_2; + break; + case 106: + gameversion = exe_doom_1_666; + break; + case 107: + gameversion = exe_doom_1_7; + break; + case 108: + gameversion = exe_doom_1_8; + break; + case 109: + gameversion = exe_doom_1_9; + break; + default: + status = false; + break; + } + + if (status) + { + break; + } + } + } + } + else if (gamemode == retail) + { + gameversion = exe_ultimate; + } + else if (gamemode == commercial) + { + /* Final Doom: tnt or plutonia + * Defaults to emulating the first Final Doom executable, + * which has the crash in the demo loop; however, having + * this as the default should mean that it plays back + * most demos correctly. + */ + + gameversion = exe_final; + } + } + + /* Deathmatch 2.0 did not exist until Doom v1.4 */ + + if (gameversion <= exe_doom_1_2 && deathmatch == 2) + { + deathmatch = 1; + } + + /* The original exe does not support retail - 4th episode not supported */ + + if (gameversion < exe_ultimate && gamemode == retail) + { + gamemode = registered; + } + + /* EXEs prior to the Final Doom exes do not support Final Doom. */ + + if (gameversion < exe_final && gamemode == commercial && + (gamemission == pack_tnt || gamemission == pack_plut)) + { + gamemission = doom2; + } +} + +static void print_game_version(void) +{ + int i; + + for (i = 0; g_gameversions[i].description != NULL; ++i) + { + if (g_gameversions[i].version == gameversion) + { + printf("Emulating the behavior of the " + "'%s' executable.\n", + g_gameversions[i].description); + break; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* d_process_events + * Send all the events of the given timestamp down the responder chain + */ + +void d_process_events(void) +{ + event_t *ev; + + /* IF STORE DEMO, DO NOT ACCEPT INPUT */ + + if (storedemo) return; + + while ((ev = d_pop_event()) != NULL) + { + if (m_responder(ev)) continue; /* menu ate the event */ + g_responder(ev); + } +} + +/* d_doomloop */ + +void d_doomloop(void) +{ + if (gamevariant == bfgedition && + (demorecording || (gameaction == ga_playdemo) || netgame)) + { + printf(" WARNING: You are playing using one of the Doom Classic\n" + " IWAD files shipped with the Doom 3: BFG Edition. These are\n" + " known to be incompatible with the regular IWAD files and\n" + " may cause demos and network games to get out of sync.\n"); + } + + if (demorecording) + { + g_begin_recording(); + } + + main_loop_started = true; + + i_set_window_title(g_gamedescription); + i_graphics_check_commandline(); + i_set_grab_mouse_callback(d_grab_mouse_callback); + i_init_graphics(); + enable_loading_disk(); + + try_run_tics(); + + v_restore_buffer(); + r_execute_set_view_size(); + + d_start_game_loop(); + + if (testcontrols) + { + wipegamestate = gamestate; + } + + while (1) + { + d_run_frame(); + } +} + +/* d_page_ticker + * Handles timing for warped projection + */ + +void d_page_ticker(void) +{ + if (--pagetic < 0) d_advance_demo(); +} + +/**************************************************************************** + * Name: d_page_drawer + ****************************************************************************/ + +void d_page_drawer(void) +{ + v_draw_patch(0, 0, w_cache_lump_name(pagename, PU_CACHE)); +} + +/**************************************************************************** + * Name: d_advance_demo + * + * Description: + * Called after each demo or intro demosequence finishes + * + ****************************************************************************/ + +void d_advance_demo(void) +{ + advancedemo = true; +} + +/**************************************************************************** + * Name: d_do_advance_demo + * + * Description: + * This cycles through the demo sequences. + * FIXME - version dependent demo numbers? + * + ****************************************************************************/ + +void d_do_advance_demo(void) +{ + players[consoleplayer].playerstate = PST_LIVE; /* not reborn */ + advancedemo = false; + usergame = false; /* no save / end game here */ + paused = false; + gameaction = ga_nothing; + + /* The Ultimate Doom executable changed the demo sequence to add + * a DEMO4 demo. Final Doom was based on Ultimate, so also + * includes this change; however, the Final Doom IWADs do not + * include a DEMO4 lump, so the game bombs out with an error + * when it reaches this point in the demo sequence. + * + * However! There is an alternate version of Final Doom that + * includes a fixed executable. + */ + + if (gameversion == exe_ultimate || gameversion == exe_final) + demosequence = (demosequence + 1) % 7; + else + demosequence = (demosequence + 1) % 6; + + switch (demosequence) + { + case 0: + if (gamemode == commercial) + pagetic = TICRATE * 11; + else + pagetic = 170; + gamestate = GS_DEMOSCREEN; + pagename = ("TITLEPIC"); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gamemode == commercial) + { + s_start_music(MUS_DM2TTL); + } + else + { + s_start_music(MUS_INTRO); + } +#endif + + break; + case 1: + g_defered_play_demo(("demo1")); + break; + case 2: + pagetic = 200; + gamestate = GS_DEMOSCREEN; + pagename = ("CREDIT"); + break; + case 3: + g_defered_play_demo(("demo2")); + break; + case 4: + gamestate = GS_DEMOSCREEN; + if (gamemode == commercial) + { + pagetic = TICRATE * 11; + pagename = ("TITLEPIC"); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_music(MUS_DM2TTL); +#endif + } + else + { + pagetic = 200; + + if (gameversion >= exe_ultimate) + pagename = ("CREDIT"); + else + pagename = ("HELP2"); + } + break; + case 5: + g_defered_play_demo(("demo3")); + break; + + /* THE DEFINITIVE DOOM Special Edition demo */ + + case 6: + g_defered_play_demo(("demo4")); + break; + } + + /* The Doom 3: BFG Edition version of doom2.wad does not have a + * TITLETPIC lump. Use INTERPIC instead as a workaround. + */ + + if (gamevariant == bfgedition && !strcasecmp(pagename, "TITLEPIC") && + w_check_num_for_name("titlepic") < 0) + { + pagename = ("INTERPIC"); + } +} + +/* d_start_title */ + +void d_start_title(void) +{ + gameaction = ga_nothing; + demosequence = -1; + d_advance_demo(); +} + +/* d_doom_main */ + +void d_doom_main(void) +{ + int p; + char file[256]; + char demolumpname[9]; + + i_at_exit(d_endoom, false); + + /* print banner */ + + i_print_banner(PACKAGE_STRING); + + printf("z_init: Init zone memory allocation daemon. \n"); + z_init(); + +#ifdef CONFIG_GAMES_NXDOOM_NET + /* @category net + * + * Start a dedicated server, routing packets but not participating + * in the game itself. + * + */ + + if (m_check_parm("-dedicated") > 0) + { + printf("Dedicated server mode.\n"); + net_dedicated_server(); + + /* Never returns */ + } + + /* @category net + * + * Query the Internet master server for a global list of active + * servers. + * + */ + + if (m_check_parm("-search")) + { + net_master_query(); + exit(0); + } + + /* @arg
+ * @category net + * + * Query the status of the server running on the given IP + * address. + * + */ + + p = m_check_parm_with_args("-query", 1); + + if (p) + { + net_query_address(myargv[p + 1]); + exit(0); + } + + /* @category net + * + * Search the local LAN for running servers. + * + */ + + if (m_check_parm("-localsearch")) + { + net_lan_query(); + exit(0); + } +#endif + + /* @category game + * @vanilla + * + * Disable monsters. + * + */ + + nomonsters = m_check_parm("-nomonsters"); + + /* @category game + * @vanilla + * + * Monsters respawn after being killed. + * + */ + + respawnparm = m_check_parm("-respawn"); + + /* @category game + * @vanilla + * + * Monsters move faster. + * + */ + + fastparm = m_check_parm("-fast"); + + /* @vanilla + * + * Developer mode. F1 saves a screenshot in the current working + * directory. + * + */ + + devparm = m_check_parm("-devparm"); + + i_display_fps_dots(devparm); + + /* @category net + * @vanilla + * + * Start a deathmatch game. + * + */ + + if (m_check_parm("-deathmatch")) deathmatch = 1; + + /* @category net + * @vanilla + * + * Start a deathmatch 2.0 game. Weapons do not stay in place and + * all items respawn after 30 seconds. + * + */ + + if (m_check_parm("-altdeath")) deathmatch = 2; + + if (devparm) + { + printf(D_DEVSTR); + } + + /* find which dir to use for config files */ + + /* Auto-detect the configuration dir. */ + + m_set_config_dir(NULL); + + /* @category game + * @arg + * @vanilla + * + * Turbo mode. The player's speed is multiplied by x%. If unspecified, + * x defaults to 200. Values are rounded up to 10 and down to 400. + * + */ + + if ((p = m_check_parm("-turbo"))) + { + int scale = 200; + + if (p < myargc - 1) scale = atoi(myargv[p + 1]); + if (scale < 10) scale = 10; + if (scale > 400) scale = 400; + printf("turbo scale: %i%%\n", scale); + forwardmove[0] = forwardmove[0] * scale / 100; + forwardmove[1] = forwardmove[1] * scale / 100; + sidemove[0] = sidemove[0] * scale / 100; + sidemove[1] = sidemove[1] * scale / 100; + } + + /* init subsystems */ + + printf("v_init: allocate screens.\n"); + v_init(); + + /* Load configuration files before initialising other subsystems. */ + + printf("m_load_defaults: Load system defaults.\n"); + m_set_config_filenames("default.cfg", PROGRAM_PREFIX "doom.cfg"); + d_bind_variables(); + m_load_defaults(); + + /* Save configuration at exit. */ + + i_at_exit(m_save_defaults, false); + + /* Find main IWAD file and load it. */ + + iwadfile = d_find_iwad(IWAD_MASK_DOOM, &gamemission); + + /* None found? */ + + if (iwadfile == NULL) + { + i_error("Game mode indeterminate. No IWAD file was found. Try\n" + "specifying one with the '-iwad' command line parameter.\n"); + } + + modifiedgame = false; + + printf("W_Init: Init WADfiles.\n"); + d_addfile(iwadfile); + + w_check_correct_iwad(doom); + + /* Now that we've loaded the IWAD, we can figure out what gamemission + * we're playing and which version of Vanilla Doom we need to emulate. + */ + + d_identify_version(); + init_game_version(); + + /* Check which IWAD variant we are using. */ + + if (w_check_num_for_name("FREEDOOM") >= 0) + { + if (w_check_num_for_name("FREEDM") >= 0) + { + gamevariant = freedm; + } + else + { + gamevariant = freedoom; + } + } + else if (w_check_num_for_name("DMENUPIC") >= 0) + { + gamevariant = bfgedition; + } + + /* @category mod + * + * Disable automatic loading of Dehacked patches for certain + * IWAD files. + * + */ + + if (!m_parm_exists("-nodeh")) + { + /* Some IWADs have dehacked patches that need to be loaded for + * them to be played properly. + */ + + load_iwad_deh(); + } + + /* Doom 3: BFG Edition includes modified versions of the classic + * IWADs which can be identified by an additional DMENUPIC lump. + * Furthermore, the M_GDHIGH lumps have been modified in a way that + * makes them incompatible to Vanilla Doom and the modified version + * of doom2.wad is missing the TITLEPIC lump. + * We specifically check for DMENUPIC here, before PWADs have been + * loaded which could probably include a lump of that name. + */ + + if (gamevariant == bfgedition) + { + printf("BFG Edition: Using workarounds as needed.\n"); + + /* BFG Edition changes the names of the secret levels to + * censor the Wolfenstein references. It also has an extra + * secret level (MAP33). In Vanilla Doom (meaning the DOS + * version), MAP33 overflows into the Plutonia level names + * array, so HUSTR_33 is actually PHUSTR_1. + */ + + deh_add_string_replacement(HUSTR_31, "level 31: idkfa"); + deh_add_string_replacement(HUSTR_32, "level 32: keen"); + deh_add_string_replacement(PHUSTR_1, "level 33: betray"); + + /* The BFG edition doesn't have the "low detail" menu option (fair + * enough). But bizarrely, it reuses the M_GDHIGH patch as a label + * for the options menu (says "Fullscreen:"). Why the perpetrators + * couldn't just add a new graphic lump and had to reuse this one, + * I don't know. + * + * The end result is that M_GDHIGH is too wide and causes the game + * to crash. As a workaround to get a minimum level of support for + * the BFG edition IWADs, use the "ON"/"OFF" graphics instead. + */ + + deh_add_string_replacement("M_GDHIGH", "M_MSGON"); + deh_add_string_replacement("M_GDLOW", "M_MSGOFF"); + + /* The BFG edition's "Screen Size:" graphic has also been changed + * to say "Gamepad:". Fortunately, it (along with the original + * Doom IWADs) has an unused graphic that says "Display". So we + * can swap this in instead, and it kind of makes sense. + */ + + deh_add_string_replacement("M_SCRNSZ", "M_DISP"); + } + + /* @category mod + * + * Disable auto-loading of .wad and .deh files. + * + */ + + if (!m_parm_exists("-noautoload") && gamemode != shareware) + { + char *autoload_dir; + + /* common auto-loaded files for all Doom flavors */ + + if (gamemission < pack_chex) + { + autoload_dir = m_get_autoload_dir("doom-all"); + if (autoload_dir != NULL) + { + deh_auto_load_patches(autoload_dir); + w_auto_load_wads(autoload_dir); + free(autoload_dir); + } + } + + /* auto-loaded files per IWAD */ + + autoload_dir = m_get_autoload_dir( + d_save_game_iwad_name(gamemission, gamevariant)); + + if (autoload_dir != NULL) + { + deh_auto_load_patches(autoload_dir); + w_auto_load_wads(autoload_dir); + free(autoload_dir); + } + } + + /* Load Dehacked patches specified on the command line with -deh. + * Note that there's a very careful and deliberate ordering to how + * Dehacked patches are loaded. The order we use is: + * 1. IWAD dehacked patches. + * 2. Command line dehacked patches specified with -deh. + * 3. PWAD dehacked patches in DEHACKED lumps. + */ + + deh_parse_command_line(); + + /* Load PWAD files. */ + + modifiedgame = w_parse_command_line(); + + /* Debug: + * w_print_directory(); + */ + + /* @arg + * @category demo + * @vanilla + * + * Play back the demo named demo.lmp. + * + */ + + p = m_check_parm_with_args("-playdemo", 1); + + if (!p) + { + /* @arg + * @category demo + * @vanilla + * + * Play back the demo named demo.lmp, determining the framerate + * of the screen. + * + */ + + p = m_check_parm_with_args("-timedemo", 1); + } + + if (p) + { + char *uc_filename = strdup(myargv[p + 1]); + m_force_uppercase(uc_filename); + + /* With Vanilla you have to specify the file without extension, + * but make that optional. + */ + + if (m_string_ends_with(uc_filename, ".LMP")) + { + m_str_copy(file, myargv[p + 1], sizeof(file)); + } + else + { + snprintf(file, sizeof(file), "%s.lmp", myargv[p + 1]); + } + + free(uc_filename); + + if (d_addfile(file)) + { + m_str_copy(demolumpname, lumpinfo[numlumps - 1]->name, + sizeof(demolumpname)); + } + else + { + /* If file failed to load, still continue trying to play + * the demo in the same way as Vanilla Doom. This makes + * tricks like "-playdemo demo1" possible. + */ + + m_str_copy(demolumpname, myargv[p + 1], sizeof(demolumpname)); + } + + printf("Playing demo %s.\n", file); + } + + i_at_exit(g_check_demo_status_at_exit, true); + + /* Generate the WAD hash table. Speed things up a bit. */ + + w_generate_hash_table(); + + /* Load DEHACKED lumps from WAD files - but only if we give the right + * command line parameter. + */ + + /* @category mod + * + * Load Dehacked patches from DEHACKED lumps contained in one of the + * loaded PWAD files. + * + */ + + if (m_parm_exists("-dehlump")) + { + int i; + int loaded = 0; + int numiwadlumps = numlumps; + + while (!w_is_iwad_lump(lumpinfo[numiwadlumps - 1])) + { + numiwadlumps--; + } + + for (i = numiwadlumps; i < numlumps; ++i) + { + if (!strncmp(lumpinfo[i]->name, "DEHACKED", 8)) + { + deh_load_lump(i, false, false); + loaded++; + } + } + + printf(" loaded %i DEHACKED lumps from PWAD files.\n", loaded); + } + + /* Set the gamedescription string. This is only possible now that + * we've finished loading Dehacked patches. + */ + + d_set_game_description(); + + savegamedir = + m_get_save_game_dir(d_save_game_iwad_name(gamemission, gamevariant)); + + /* Check for -file in shareware */ + + if (modifiedgame && (gamevariant != freedoom)) + { + int i; + + if (gamemode == shareware) + i_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_check_num_for_name(g_name[i]) < 0) + { + i_error(("\nThis is not the registered version.")); + } + } + } + } + + if (w_check_num_for_name("SS_START") >= 0 || + w_check_num_for_name("FF_END") >= 0) + { + i_print_divider(); + printf(" WARNING: The loaded WAD file contains modified sprites or\n" + " floor textures. You may want to use the '-merge' command\n" + " line option instead of '-file'.\n"); + } + + i_print_startup_banner(g_gamedescription); + print_dehacked_banners(); + + printf("i_init: Setting up machine state.\n"); + i_check_is_screensaver(); + i_init_timer(); + i_init_joystick(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + i_init_sound(doom); + i_init_music(); +#endif + +#ifdef CONFIG_GAMES_NXDOOM_NET + printf("net_init: Init network subsystem.\n"); + net_init(); +#endif + + /* Initial netgame startup. Connect to server etc. */ + + d_connect_net_game(); + + /* get skill / episode / map from params */ + + startskill = sk_medium; + startepisode = 1; + startmap = 1; + autostart = false; + + /* @category game + * @arg + * @vanilla + * + * Set the game skill, 1-5 (1: easiest, 5: hardest). A skill of + * 0 disables all monsters. + * + */ + + p = m_check_parm_with_args("-skill", 1); + + if (p) + { + startskill = myargv[p + 1][0] - '1'; + autostart = true; + } + + /* @category game + * @arg + * @vanilla + * + * Start playing episode n (1-4). + * + */ + + p = m_check_parm_with_args("-episode", 1); + + if (p) + { + startepisode = myargv[p + 1][0] - '0'; + startmap = 1; + autostart = true; + } + + timelimit = 0; + + /* @arg + * @category net + * @vanilla + * + * For multiplayer games: exit each level after n minutes. + * + */ + + p = m_check_parm_with_args("-timer", 1); + + if (p) + { + timelimit = atoi(myargv[p + 1]); + } + + /* @category net + * @vanilla + * + * Austin Virtual Gaming: end levels after 20 minutes. + * + */ + + p = m_check_parm("-avg"); + + if (p) + { + timelimit = 20; + } + + /* @category game + * @arg [ | ] + * @vanilla + * + * Start a game immediately, warping to ExMy (Doom 1) or MAPxy + * (Doom 2). + * + */ + + p = m_check_parm_with_args("-warp", 1); + + if (p) + { + if (gamemode == commercial) + startmap = atoi(myargv[p + 1]); + else + { + startepisode = myargv[p + 1][0] - '0'; + + if (p + 2 < myargc) + { + startmap = myargv[p + 2][0] - '0'; + } + else + { + startmap = 1; + } + } + + autostart = true; + } + + /* Undocumented: + * Invoked by setup to test the controls. + */ + + p = m_check_parm("-testcontrols"); + + if (p > 0) + { + startepisode = 1; + startmap = 1; + autostart = true; + testcontrols = true; + } + + /* Check for load game parameter + * We do this here and save the slot number, so that the network code + * can override it or send the load slot to other players. + */ + + /* @category game + * @arg + * @vanilla + * + * Load the game in slot s. + * + */ + + p = m_check_parm_with_args("-loadgame", 1); + + if (p) + { + startloadgame = atoi(myargv[p + 1]); + } + else + { + startloadgame = -1; /* Not loading a game */ + } + + printf("m_init: Init miscellaneous info.\n"); + m_init(); + + printf("r_init: Init DOOM refresh daemon - "); + r_init(); + + printf("\np_init: Init Playloop state.\n"); + p_init(); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + printf("s_init: Setting up sound.\n"); + s_init(g_sfx_volume * 8, g_music_volume * 8); +#endif + + printf("d_check_net_game: Checking network game status.\n"); + d_check_net_game(); + + print_game_version(); + + printf("hu_init: Setting up heads up display.\n"); + hu_init(); + + printf("st_init: Init status bar.\n"); + st_init(); + + /* If Doom II without a MAP01 lump, this is a store demo. + * Moved this here so that MAP01 isn't constantly looked up + * in the main loop. + */ + + if (gamemode == commercial && w_check_num_for_name("map01") < 0) + storedemo = true; + + if (m_check_parm_with_args("-statdump", 1)) + { + i_at_exit(stat_dump, true); + printf("External statistics registered.\n"); + } + + /* @arg + * @category demo + * @vanilla + * + * Record a demo named x.lmp. + */ + + p = m_check_parm_with_args("-record", 1); + + if (p) + { + g_record_demo(myargv[p + 1]); + autostart = true; + } + + p = m_check_parm_with_args("-playdemo", 1); + if (p) + { + singledemo = true; /* quit after one demo */ + g_defered_play_demo(demolumpname); + d_doomloop(); /* never returns */ + } + + p = m_check_parm_with_args("-timedemo", 1); + if (p) + { + g_time_demo(demolumpname); + d_doomloop(); /* never returns */ + } + + if (startloadgame >= 0) + { + m_str_copy(file, p_save_game_file(startloadgame), sizeof(file)); + g_load_game(file); + } + + if (gameaction != ga_loadgame) + { + if (autostart || netgame) + g_init_new(startskill, startepisode, startmap); + else + d_start_title(); /* start up intro loop */ + } + + d_doomloop(); /* never returns */ +} diff --git a/games/NXDoom/src/doom/d_main.h b/games/NXDoom/src/doom/d_main.h new file mode 100644 index 00000000000..f9742259711 --- /dev/null +++ b/games/NXDoom/src/doom/d_main.h @@ -0,0 +1,58 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_main.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __D_MAIN__ +#define __D_MAIN__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern gameaction_t gameaction; +extern boolean advancedemo; + +extern const char *pagename; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Read events from all input devices */ + +void d_process_events(void); + +/* BASE LEVEL */ + +void d_page_ticker(void); +void d_page_drawer(void); +void d_advance_demo(void); +void d_do_advance_demo(void); +void d_start_title(void); + +#endif /* __D_MAIN__ */ diff --git a/games/NXDoom/src/doom/d_net.c b/games/NXDoom/src/doom/d_net.c new file mode 100644 index 00000000000..3be74240cc3 --- /dev/null +++ b/games/NXDoom/src/doom/d_net.c @@ -0,0 +1,333 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_net.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * DOOM Network game communication and protocol, all OS independent parts. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "d_main.h" +#include "doomdef.h" +#include "doomstat.h" +#include "g_game.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_menu.h" +#include "m_misc.h" +#include "w_checksum.h" +#include "w_wad.h" + +#include "deh_main.h" + +#include "d_loop.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void run_tic(ticcmd_t *cmds, boolean *ingame); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static loop_interface_t g_doom_loop_interface = +{ + d_process_events, + g_build_ticcmd, + run_tic, + m_ticker, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +ticcmd_t *netcmds; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: player_quit_game + * + * Description: + * Called when a player leaves the game + * + ****************************************************************************/ + +static void player_quit_game(player_t *player) +{ + static char exitmsg[80]; + unsigned int player_num; + + player_num = player - players; + + /* Do this the same way as Vanilla Doom does, to allow dehacked + * replacements of this message + */ + + m_str_copy(exitmsg, ("Player 1 left the game"), sizeof(exitmsg)); + + exitmsg[7] += player_num; + + playeringame[player_num] = false; + players[consoleplayer].message = exitmsg; + + /* TODO: check if it is sensible to do this: */ + + if (demorecording) + { + g_check_demo_status(); + } +} + +static void run_tic(ticcmd_t *cmds, boolean *ingame) +{ + unsigned int i; + + /* Check for player quits. */ + + for (i = 0; i < MAXPLAYERS; ++i) + { + if (!demoplayback && playeringame[i] && !ingame[i]) + { + player_quit_game(&players[i]); + } + } + + netcmds = cmds; + + /* check that there are players in the game. if not, we cannot + * run a tic. + */ + + if (advancedemo) d_do_advance_demo(); + + g_ticker(); +} + +/**************************************************************************** + * Name: Load_Game_Settings + * + * Description: + * Load game settings from the specified structure and + * set global variables. + * + ****************************************************************************/ + +static void load_game_settings(net_gamesettings_t *settings) +{ + unsigned int i; + + deathmatch = settings->deathmatch; + startepisode = settings->episode; + startmap = settings->map; + startskill = settings->skill; + startloadgame = settings->loadgame; + lowres_turn = settings->lowres_turn; + nomonsters = settings->nomonsters; + fastparm = settings->fast_monsters; + respawnparm = settings->respawn_monsters; + timelimit = settings->timelimit; + consoleplayer = settings->consoleplayer; + + if (lowres_turn) + { + printf("NOTE: Turning resolution is reduced; this is probably " + "because there is a client recording a Vanilla demo.\n"); + } + + for (i = 0; i < MAXPLAYERS; ++i) + { + playeringame[i] = i < settings->num_players; + } +} + +/**************************************************************************** + * Name: save_game_settings + * + * Description: + * Save the game settings from global variables to the specified + * game settings structure. + * + ****************************************************************************/ + +static void save_game_settings(net_gamesettings_t *settings) +{ + /* Fill in game settings structure with appropriate parameters + * for the new game + */ + + settings->deathmatch = deathmatch; + settings->episode = startepisode; + settings->map = startmap; + settings->skill = startskill; + settings->loadgame = startloadgame; + settings->gameversion = gameversion; + settings->nomonsters = nomonsters; + settings->fast_monsters = fastparm; + settings->respawn_monsters = respawnparm; + settings->timelimit = timelimit; + + settings->lowres_turn = + (m_parm_exists("-record") && !m_parm_exists("-longtics")) || + m_parm_exists("-shorttics"); +} + +static void init_connect_data(net_connect_data_t *connect_data) +{ + boolean shorttics; + + connect_data->max_players = MAXPLAYERS; + connect_data->drone = false; + + /* @category net + * + * Run as the left screen in three screen mode. + */ + + if (m_check_parm("-left") > 0) + { + viewangleoffset = ANG90; + connect_data->drone = true; + } + + /* @category net + * + * Run as the right screen in three screen mode. + */ + + if (m_check_parm("-right") > 0) + { + viewangleoffset = ANG270; + connect_data->drone = true; + } + + /* Connect data */ + + /* Game type fields: */ + + connect_data->gamemode = gamemode; + connect_data->gamemission = gamemission; + + /* @category demo + * + * Play with low turning resolution to emulate demo recording. + */ + + shorttics = m_parm_exists("-shorttics"); + + /* Are we recording a demo? Possibly set lowres turn mode */ + + connect_data->lowres_turn = + (m_parm_exists("-record") && !m_parm_exists("-longtics")) || shorttics; + + /* Read checksums of our WAD directory and dehacked information */ + + w_checksum(connect_data->wad_sha1sum); + deh_checksum(connect_data->deh_sha1sum); + + /* Are we playing with the Freedoom IWAD? */ + + connect_data->is_freedoom = w_check_num_for_name("FREEDOOM") >= 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void d_connect_net_game(void) +{ + net_connect_data_t connect_data; + + init_connect_data(&connect_data); + netgame = d_init_net_game(&connect_data); + +#ifdef CONFIG_GAMES_NXDOOM_NET + /* @category net + * + * Start the game playing as though in a netgame with a single + * player. This can also be used to play back single player netgame + * demos. + */ + + if (m_check_parm("-solo-net") > 0) + { + netgame = true; + } +#endif +} + +/**************************************************************************** + * Name: d_check_net_game + * + * Description: + * Works out player numbers among the net participants + * + ****************************************************************************/ + +void d_check_net_game(void) +{ + net_gamesettings_t settings; + + if (netgame) + { + autostart = true; + } + + d_register_loop_callbacks(&g_doom_loop_interface); + + save_game_settings(&settings); + d_start_net_game(&settings, NULL); + load_game_settings(&settings); + + printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", + startskill, deathmatch, startmap, startepisode); + + printf("player %i of %i (%i nodes)\n", consoleplayer + 1, + settings.num_players, settings.num_players); + + /* Show players here; the server might have specified a time limit */ + + if (timelimit > 0 && deathmatch) + { + /* Gross hack to work like Vanilla: */ + + if (timelimit == 20 && m_check_parm("-avg")) + { + printf("Austin Virtual Gaming: Levels will end " + "after 20 minutes\n"); + } + else + { + printf("Levels will end after %d minute", timelimit); + if (timelimit > 1) printf("s"); + printf(".\n"); + } + } +} diff --git a/games/NXDoom/src/doom/d_player.h b/games/NXDoom/src/doom/d_player.h new file mode 100644 index 00000000000..54defc586e6 --- /dev/null +++ b/games/NXDoom/src/doom/d_player.h @@ -0,0 +1,236 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_player.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * + ****************************************************************************/ + +#ifndef __D_PLAYER__ +#define __D_PLAYER__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* 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" + +#include "net_defs.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Player states. */ + +typedef enum +{ + PST_LIVE, /* Playing or camping. */ + PST_DEAD, /* Dead on the ground, view follows killer. */ + PST_REBORN /* Ready to restart/respawn??? */ +} playerstate_t; + +/* Player internal flags, for cheats and debug. */ + +typedef enum +{ + CF_NOCLIP = 1, /* No clipping, walk through barriers. */ + CF_GODMODE = 2, /* No damage, no health loss. */ + CF_NOMOMENTUM = 4 /* Not really a cheat, just a debug aid. */ +} 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]; + boolean cards[NUMCARDS]; + boolean backpack; + + /* Frags, kills of other players. */ + + int frags[MAXPLAYERS]; + weapontype_t readyweapon; + + /* Is wp_nochange if not changing. */ + + weapontype_t pendingweapon; + + int 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. */ + + const char *message; + + /* For screen flashing (red or bright). */ + + int damagecount; + int bonuscount; + + /* Who did damage (NULL 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. */ + + boolean didsecret; +} player_t; + +/* INTERMISSION + * Structure passed e.g. to wi_start(wb) + */ + +typedef struct +{ + 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 */ + + 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 /* __D_PLAYER__ */ diff --git a/games/NXDoom/src/doom/d_textur.h b/games/NXDoom/src/doom/d_textur.h new file mode 100644 index 00000000000..cf77f38b4ce --- /dev/null +++ b/games/NXDoom/src/doom/d_textur.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_textur.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Typedefs related to to textures etc., + * isolated here to make it easier separating modules. + * + ****************************************************************************/ + +#ifndef __D_TEXTUR__ +#define __D_TEXTUR__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Flats? + * + * a pic is an unmasked block of pixels + */ + +typedef struct +{ + byte width; + byte height; + byte data; +} pic_t; + +#endif /* __D_TEXTUR__ */ diff --git a/games/NXDoom/src/doom/d_think.h b/games/NXDoom/src/doom/d_think.h new file mode 100644 index 00000000000..19742248140 --- /dev/null +++ b/games/NXDoom/src/doom/d_think.h @@ -0,0 +1,64 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_think.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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__ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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_v acv; + actionf_p1 acp1; + 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 /* __D_THINK__ */ diff --git a/games/NXDoom/src/doom/deh_ammo.c b/games/NXDoom/src/doom/deh_ammo.c new file mode 100644 index 00000000000..83aabf00e1b --- /dev/null +++ b/games/NXDoom/src/doom/deh_ammo.c @@ -0,0 +1,128 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_ammo.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Parses "Ammo" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" +#include "doomdef.h" +#include "doomtype.h" +#include "p_local.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_ammo_start(deh_context_t *context, char *line); +static void deh_ammo_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_ammo_sha1_hash(SHA1_CTX *context); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_ammo = +{ + "Ammo", + NULL, + deh_ammo_start, + deh_ammo_parse_line, + NULL, + deh_ammo_sha1_hash, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_ammo_start(deh_context_t *context, char *line) +{ + int ammo_number = 0; + + if (sscanf(line, "Ammo %i", &ammo_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + if (ammo_number < 0 || ammo_number >= NUMAMMO) + { + deh_warning(context, "Invalid ammo number: %i", ammo_number); + return NULL; + } + + return &maxammo[ammo_number]; +} + +static void deh_ammo_parse_line(deh_context_t *context, char *line, + void *tag) +{ + char *variable_name; + char *value; + int ivalue; + int ammo_number; + + if (tag == NULL) return; + + ammo_number = ((int *)tag) - maxammo; + + /* Parse the assignment */ + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + ivalue = atoi(value); + + /* maxammo */ + + if (!strcasecmp(variable_name, "Per ammo")) + clipammo[ammo_number] = ivalue; + else if (!strcasecmp(variable_name, "Max ammo")) + maxammo[ammo_number] = ivalue; + else + { + deh_warning(context, "Field named '%s' not found", variable_name); + } +} + +static void deh_ammo_sha1_hash(SHA1_CTX *context) +{ + int i; + + for (i = 0; i < NUMAMMO; ++i) + { + sha1_updateint32(context, clipammo[i]); + sha1_updateint32(context, maxammo[i]); + } +} diff --git a/games/NXDoom/src/doom/deh_bexstr.c b/games/NXDoom/src/doom/deh_bexstr.c new file mode 100644 index 00000000000..c1ff9c83df1 --- /dev/null +++ b/games/NXDoom/src/doom/deh_bexstr.c @@ -0,0 +1,406 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_bexstr.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2014 Fabian Greffrath + * + * 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. + * + * Parses [STRINGS] sections in BEX files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" + +#include "dstrings.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + const char *macro; + const char *string; +} bex_string_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_bex_str_start(deh_context_t *context, char *line); +static void deh_bex_str_parse_line(deh_context_t *context, char *line, + void *tag); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* mnemonic keys table */ + +static const bex_string_t g_bex_stringtable[] = +{ + {"D_DEVSTR", D_DEVSTR}, /* part 1 - general initialization and prompts */ + {"D_CDROM", D_CDROM}, + {"QUITMSG", QUITMSG}, + {"LOADNET", LOADNET}, + {"QLOADNET", QLOADNET}, + {"QSAVESPOT", QSAVESPOT}, + {"SAVEDEAD", SAVEDEAD}, + {"QSPROMPT", QSPROMPT}, + {"QLPROMPT", QLPROMPT}, + {"NEWGAME", NEWGAME}, + {"NIGHTMARE", NIGHTMARE}, + {"SWSTRING", SWSTRING}, + {"MSGOFF", MSGOFF}, + {"MSGON", MSGON}, + {"NETEND", NETEND}, + {"ENDGAME", ENDGAME}, + {"DETAILHI", DETAILHI}, + {"DETAILLO", DETAILLO}, + {"GAMMALVL0", GAMMALVL0}, + {"GAMMALVL1", GAMMALVL1}, + {"GAMMALVL2", GAMMALVL2}, + {"GAMMALVL3", GAMMALVL3}, + {"GAMMALVL4", GAMMALVL4}, + {"EMPTYSTRING", EMPTYSTRING}, + {"GGSAVED", GGSAVED}, + {"SAVEGAMENAME", SAVEGAMENAME}, + {"GOTARMOR", + GOTARMOR}, /* part 2 - messages when the player gets things */ + {"GOTMEGA", GOTMEGA}, + {"GOTHTHBONUS", GOTHTHBONUS}, + {"GOTARMBONUS", GOTARMBONUS}, + {"GOTSTIM", GOTSTIM}, + {"GOTMEDINEED", GOTMEDINEED}, + {"GOTMEDIKIT", GOTMEDIKIT}, + {"GOTSUPER", GOTSUPER}, + {"GOTBLUECARD", GOTBLUECARD}, + {"GOTYELWCARD", GOTYELWCARD}, + {"GOTREDCARD", GOTREDCARD}, + {"GOTBLUESKUL", GOTBLUESKUL}, + {"GOTYELWSKUL", GOTYELWSKUL}, + {"GOTREDSKULL", GOTREDSKULL}, + {"GOTINVUL", GOTINVUL}, + {"GOTBERSERK", GOTBERSERK}, + {"GOTINVIS", GOTINVIS}, + {"GOTSUIT", GOTSUIT}, + {"GOTMAP", GOTMAP}, + {"GOTVISOR", GOTVISOR}, + {"GOTMSPHERE", GOTMSPHERE}, + {"GOTCLIP", GOTCLIP}, + {"GOTCLIPBOX", GOTCLIPBOX}, + {"GOTROCKET", GOTROCKET}, + {"GOTROCKBOX", GOTROCKBOX}, + {"GOTCELL", GOTCELL}, + {"GOTCELLBOX", GOTCELLBOX}, + {"GOTSHELLS", GOTSHELLS}, + {"GOTSHELLBOX", GOTSHELLBOX}, + {"GOTBACKPACK", GOTBACKPACK}, + {"GOTBFG9000", GOTBFG9000}, + {"GOTCHAINGUN", GOTCHAINGUN}, + {"GOTCHAINSAW", GOTCHAINSAW}, + {"GOTLAUNCHER", GOTLAUNCHER}, + {"GOTPLASMA", GOTPLASMA}, + {"GOTSHOTGUN", GOTSHOTGUN}, + {"GOTSHOTGUN2", GOTSHOTGUN2}, + {"PD_BLUEO", PD_BLUEO}, /* part 3 - messages when keys are needed */ + {"PD_REDO", PD_REDO}, + {"PD_YELLOWO", PD_YELLOWO}, + {"PD_BLUEK", PD_BLUEK}, + {"PD_REDK", PD_REDK}, + {"PD_YELLOWK", PD_YELLOWK}, + {"HUSTR_MSGU", HUSTR_MSGU}, /* part 4 - multiplayer messaging */ + {"HUSTR_MESSAGESENT", HUSTR_MESSAGESENT}, + {"HUSTR_CHATMACRO0", HUSTR_CHATMACRO0}, + {"HUSTR_CHATMACRO1", HUSTR_CHATMACRO1}, + {"HUSTR_CHATMACRO2", HUSTR_CHATMACRO2}, + {"HUSTR_CHATMACRO3", HUSTR_CHATMACRO3}, + {"HUSTR_CHATMACRO4", HUSTR_CHATMACRO4}, + {"HUSTR_CHATMACRO5", HUSTR_CHATMACRO5}, + {"HUSTR_CHATMACRO6", HUSTR_CHATMACRO6}, + {"HUSTR_CHATMACRO7", HUSTR_CHATMACRO7}, + {"HUSTR_CHATMACRO8", HUSTR_CHATMACRO8}, + {"HUSTR_CHATMACRO9", HUSTR_CHATMACRO9}, + {"HUSTR_TALKTOSELF1", HUSTR_TALKTOSELF1}, + {"HUSTR_TALKTOSELF2", HUSTR_TALKTOSELF2}, + {"HUSTR_TALKTOSELF3", HUSTR_TALKTOSELF3}, + {"HUSTR_TALKTOSELF4", HUSTR_TALKTOSELF4}, + {"HUSTR_TALKTOSELF5", HUSTR_TALKTOSELF5}, + {"HUSTR_PLRGREEN", HUSTR_PLRGREEN}, + {"HUSTR_PLRINDIGO", HUSTR_PLRINDIGO}, + {"HUSTR_PLRBROWN", HUSTR_PLRBROWN}, + {"HUSTR_PLRRED", HUSTR_PLRRED}, + {"HUSTR_E1M1", HUSTR_E1M1}, /* part 5 - level names in the automap */ + {"HUSTR_E1M2", HUSTR_E1M2}, + {"HUSTR_E1M3", HUSTR_E1M3}, + {"HUSTR_E1M4", HUSTR_E1M4}, + {"HUSTR_E1M5", HUSTR_E1M5}, + {"HUSTR_E1M6", HUSTR_E1M6}, + {"HUSTR_E1M7", HUSTR_E1M7}, + {"HUSTR_E1M8", HUSTR_E1M8}, + {"HUSTR_E1M9", HUSTR_E1M9}, + {"HUSTR_E2M1", HUSTR_E2M1}, + {"HUSTR_E2M2", HUSTR_E2M2}, + {"HUSTR_E2M3", HUSTR_E2M3}, + {"HUSTR_E2M4", HUSTR_E2M4}, + {"HUSTR_E2M5", HUSTR_E2M5}, + {"HUSTR_E2M6", HUSTR_E2M6}, + {"HUSTR_E2M7", HUSTR_E2M7}, + {"HUSTR_E2M8", HUSTR_E2M8}, + {"HUSTR_E2M9", HUSTR_E2M9}, + {"HUSTR_E3M1", HUSTR_E3M1}, + {"HUSTR_E3M2", HUSTR_E3M2}, + {"HUSTR_E3M3", HUSTR_E3M3}, + {"HUSTR_E3M4", HUSTR_E3M4}, + {"HUSTR_E3M5", HUSTR_E3M5}, + {"HUSTR_E3M6", HUSTR_E3M6}, + {"HUSTR_E3M7", HUSTR_E3M7}, + {"HUSTR_E3M8", HUSTR_E3M8}, + {"HUSTR_E3M9", HUSTR_E3M9}, + {"HUSTR_E4M1", HUSTR_E4M1}, + {"HUSTR_E4M2", HUSTR_E4M2}, + {"HUSTR_E4M3", HUSTR_E4M3}, + {"HUSTR_E4M4", HUSTR_E4M4}, + {"HUSTR_E4M5", HUSTR_E4M5}, + {"HUSTR_E4M6", HUSTR_E4M6}, + {"HUSTR_E4M7", HUSTR_E4M7}, + {"HUSTR_E4M8", HUSTR_E4M8}, + {"HUSTR_E4M9", HUSTR_E4M9}, + {"HUSTR_1", HUSTR_1}, + {"HUSTR_2", HUSTR_2}, + {"HUSTR_3", HUSTR_3}, + {"HUSTR_4", HUSTR_4}, + {"HUSTR_5", HUSTR_5}, + {"HUSTR_6", HUSTR_6}, + {"HUSTR_7", HUSTR_7}, + {"HUSTR_8", HUSTR_8}, + {"HUSTR_9", HUSTR_9}, + {"HUSTR_10", HUSTR_10}, + {"HUSTR_11", HUSTR_11}, + {"HUSTR_12", HUSTR_12}, + {"HUSTR_13", HUSTR_13}, + {"HUSTR_14", HUSTR_14}, + {"HUSTR_15", HUSTR_15}, + {"HUSTR_16", HUSTR_16}, + {"HUSTR_17", HUSTR_17}, + {"HUSTR_18", HUSTR_18}, + {"HUSTR_19", HUSTR_19}, + {"HUSTR_20", HUSTR_20}, + {"HUSTR_21", HUSTR_21}, + {"HUSTR_22", HUSTR_22}, + {"HUSTR_23", HUSTR_23}, + {"HUSTR_24", HUSTR_24}, + {"HUSTR_25", HUSTR_25}, + {"HUSTR_26", HUSTR_26}, + {"HUSTR_27", HUSTR_27}, + {"HUSTR_28", HUSTR_28}, + {"HUSTR_29", HUSTR_29}, + {"HUSTR_30", HUSTR_30}, + {"HUSTR_31", HUSTR_31}, + {"HUSTR_32", HUSTR_32}, + {"PHUSTR_1", PHUSTR_1}, + {"PHUSTR_2", PHUSTR_2}, + {"PHUSTR_3", PHUSTR_3}, + {"PHUSTR_4", PHUSTR_4}, + {"PHUSTR_5", PHUSTR_5}, + {"PHUSTR_6", PHUSTR_6}, + {"PHUSTR_7", PHUSTR_7}, + {"PHUSTR_8", PHUSTR_8}, + {"PHUSTR_9", PHUSTR_9}, + {"PHUSTR_10", PHUSTR_10}, + {"PHUSTR_11", PHUSTR_11}, + {"PHUSTR_12", PHUSTR_12}, + {"PHUSTR_13", PHUSTR_13}, + {"PHUSTR_14", PHUSTR_14}, + {"PHUSTR_15", PHUSTR_15}, + {"PHUSTR_16", PHUSTR_16}, + {"PHUSTR_17", PHUSTR_17}, + {"PHUSTR_18", PHUSTR_18}, + {"PHUSTR_19", PHUSTR_19}, + {"PHUSTR_20", PHUSTR_20}, + {"PHUSTR_21", PHUSTR_21}, + {"PHUSTR_22", PHUSTR_22}, + {"PHUSTR_23", PHUSTR_23}, + {"PHUSTR_24", PHUSTR_24}, + {"PHUSTR_25", PHUSTR_25}, + {"PHUSTR_26", PHUSTR_26}, + {"PHUSTR_27", PHUSTR_27}, + {"PHUSTR_28", PHUSTR_28}, + {"PHUSTR_29", PHUSTR_29}, + {"PHUSTR_30", PHUSTR_30}, + {"PHUSTR_31", PHUSTR_31}, + {"PHUSTR_32", PHUSTR_32}, + {"THUSTR_1", THUSTR_1}, + {"THUSTR_2", THUSTR_2}, + {"THUSTR_3", THUSTR_3}, + {"THUSTR_4", THUSTR_4}, + {"THUSTR_5", THUSTR_5}, + {"THUSTR_6", THUSTR_6}, + {"THUSTR_7", THUSTR_7}, + {"THUSTR_8", THUSTR_8}, + {"THUSTR_9", THUSTR_9}, + {"THUSTR_10", THUSTR_10}, + {"THUSTR_11", THUSTR_11}, + {"THUSTR_12", THUSTR_12}, + {"THUSTR_13", THUSTR_13}, + {"THUSTR_14", THUSTR_14}, + {"THUSTR_15", THUSTR_15}, + {"THUSTR_16", THUSTR_16}, + {"THUSTR_17", THUSTR_17}, + {"THUSTR_18", THUSTR_18}, + {"THUSTR_19", THUSTR_19}, + {"THUSTR_20", THUSTR_20}, + {"THUSTR_21", THUSTR_21}, + {"THUSTR_22", THUSTR_22}, + {"THUSTR_23", THUSTR_23}, + {"THUSTR_24", THUSTR_24}, + {"THUSTR_25", THUSTR_25}, + {"THUSTR_26", THUSTR_26}, + {"THUSTR_27", THUSTR_27}, + {"THUSTR_28", THUSTR_28}, + {"THUSTR_29", THUSTR_29}, + {"THUSTR_30", THUSTR_30}, + {"THUSTR_31", THUSTR_31}, + {"THUSTR_32", THUSTR_32}, + {"AMSTR_FOLLOWON", + AMSTR_FOLLOWON}, /* part 6 - messages as a result of toggling states */ + {"AMSTR_FOLLOWOFF", AMSTR_FOLLOWOFF}, + {"AMSTR_GRIDON", AMSTR_GRIDON}, + {"AMSTR_GRIDOFF", AMSTR_GRIDOFF}, + {"AMSTR_MARKEDSPOT", AMSTR_MARKEDSPOT}, + {"AMSTR_MARKSCLEARED", AMSTR_MARKSCLEARED}, + {"STSTR_MUS", STSTR_MUS}, + {"STSTR_NOMUS", STSTR_NOMUS}, + {"STSTR_DQDON", STSTR_DQDON}, + {"STSTR_DQDOFF", STSTR_DQDOFF}, + {"STSTR_KFAADDED", STSTR_KFAADDED}, + {"STSTR_FAADDED", STSTR_FAADDED}, + {"STSTR_NCON", STSTR_NCON}, + {"STSTR_NCOFF", STSTR_NCOFF}, + {"STSTR_BEHOLD", STSTR_BEHOLD}, + {"STSTR_BEHOLDX", STSTR_BEHOLDX}, + {"STSTR_CHOPPERS", STSTR_CHOPPERS}, + {"STSTR_CLEV", STSTR_CLEV}, + {"E1TEXT", E1TEXT}, /* part 7 - episode intermission texts */ + {"E2TEXT", E2TEXT}, + {"E3TEXT", E3TEXT}, + {"E4TEXT", E4TEXT}, + {"C1TEXT", C1TEXT}, + {"C2TEXT", C2TEXT}, + {"C3TEXT", C3TEXT}, + {"C4TEXT", C4TEXT}, + {"C5TEXT", C5TEXT}, + {"C6TEXT", C6TEXT}, + {"P1TEXT", P1TEXT}, + {"P2TEXT", P2TEXT}, + {"P3TEXT", P3TEXT}, + {"P4TEXT", P4TEXT}, + {"P5TEXT", P5TEXT}, + {"P6TEXT", P6TEXT}, + {"T1TEXT", T1TEXT}, + {"T2TEXT", T2TEXT}, + {"T3TEXT", T3TEXT}, + {"T4TEXT", T4TEXT}, + {"T5TEXT", T5TEXT}, + {"T6TEXT", T6TEXT}, + {"CC_ZOMBIE", CC_ZOMBIE}, /* part 8 - creature names for the finale */ + {"CC_SHOTGUN", CC_SHOTGUN}, + {"CC_HEAVY", CC_HEAVY}, + {"CC_IMP", CC_IMP}, + {"CC_DEMON", CC_DEMON}, + {"CC_LOST", CC_LOST}, + {"CC_CACO", CC_CACO}, + {"CC_HELL", CC_HELL}, + {"CC_BARON", CC_BARON}, + {"CC_ARACH", CC_ARACH}, + {"CC_PAIN", CC_PAIN}, + {"CC_REVEN", CC_REVEN}, + {"CC_MANCU", CC_MANCU}, + {"CC_ARCH", CC_ARCH}, + {"CC_SPIDER", CC_SPIDER}, + {"CC_CYBER", CC_CYBER}, + {"CC_HERO", CC_HERO}, + {"BGFLATE1", "FLOOR4_8"}, /* part 9 - intermission tiled backgrounds */ + {"BGFLATE2", "SFLR6_1"}, + {"BGFLATE3", "MFLR8_4"}, + {"BGFLATE4", "MFLR8_3"}, + {"BGFLAT06", "SLIME16"}, + {"BGFLAT11", "RROCK14"}, + {"BGFLAT20", "RROCK07"}, + {"BGFLAT30", "RROCK17"}, + {"BGFLAT15", "RROCK13"}, + {"BGFLAT31", "RROCK19"}, + {"BGCASTCALL", "BOSSBACK"}, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_bexstr = +{ + "[STRINGS]", + NULL, + deh_bex_str_start, + deh_bex_str_parse_line, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_bex_str_start(deh_context_t *context, char *line) +{ + char s[10]; + + if (sscanf(line, "%9s", s) == 0 || strncmp("[STRINGS]", s, sizeof(s))) + { + deh_warning(context, "Parse error on section start"); + } + + return NULL; +} + +static void deh_bex_str_parse_line(deh_context_t *context, char *line, + void *tag) +{ + char *variable_name; + char *value; + int i; + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + deh_warning(context, "Failed to parse assignment"); + return; + } + + for (i = 0; i < arrlen(g_bex_stringtable); i++) + { + if (!strcmp(g_bex_stringtable[i].macro, variable_name)) + { + deh_add_string_replacement(g_bex_stringtable[i].string, value); + } + } +} diff --git a/games/NXDoom/src/doom/deh_cheat.c b/games/NXDoom/src/doom/deh_cheat.c new file mode 100644 index 00000000000..f01c08976d6 --- /dev/null +++ b/games/NXDoom/src/doom/deh_cheat.c @@ -0,0 +1,179 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_cheat.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Parses "Cheat" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" + +#include "am_map.h" +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" +#include "st_stuff.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + const char *name; + cheatseq_t *seq; +} deh_cheat_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_cheat_start(deh_context_t *context, char *line); +static void deh_cheat_parse_line(deh_context_t *context, char *line, + void *tag); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static deh_cheat_t g_allcheats[] = +{ + {"Change music", &cheat_mus}, + {"Chainsaw", &cheat_choppers}, + {"God mode", &cheat_god}, + {"Ammo & Keys", &cheat_ammo}, + {"Ammo", &cheat_ammonokey}, + {"No Clipping 1", &cheat_noclip}, + {"No Clipping 2", &cheat_commercial_noclip}, + {"Invincibility", &cheat_powerup[0]}, + {"Berserk", &cheat_powerup[1]}, + {"Invisibility", &cheat_powerup[2]}, + {"Radiation Suit", &cheat_powerup[3]}, + {"Auto-map", &cheat_powerup[4]}, + {"Lite-Amp Goggles", &cheat_powerup[5]}, + {"BEHOLD menu", &cheat_powerup[6]}, + {"Level Warp", &cheat_clev}, + {"Player Position", &cheat_mypos}, + {"Map cheat", &cheat_amap}, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_cheat = +{ + "Cheat", + NULL, + deh_cheat_start, + deh_cheat_parse_line, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static deh_cheat_t *find_cheat_by_name(char *name) +{ + size_t i; + + for (i = 0; i < arrlen(g_allcheats); ++i) + { + if (!strcasecmp(g_allcheats[i].name, name)) return &g_allcheats[i]; + } + + return NULL; +} + +static void *deh_cheat_start(deh_context_t *context, char *line) +{ + return NULL; +} + +static void deh_cheat_parse_line(deh_context_t *context, char *line, + void *tag) +{ + deh_cheat_t *cheat; + char *variable_name; + char *value; + unsigned char *unsvalue; + unsigned int i; + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + unsvalue = (unsigned char *)value; + + cheat = find_cheat_by_name(variable_name); + + if (cheat == NULL) + { + deh_warning(context, "Unknown cheat '%s'", variable_name); + return; + } + + /* write the value into the cheat sequence */ + + i = 0; + + while (unsvalue[i] != 0 && unsvalue[i] != 0xff) + { + /* If the cheat length exceeds the Vanilla limit, stop. This + * does not apply if we have the limit turned off. + */ + + if (!deh_allow_long_cheats && i >= cheat->seq->sequence_len) + { + deh_warning(context, "Cheat sequence longer than supported by " + "Vanilla dehacked"); + break; + } + + if (deh_apply_cheats) + { + cheat->seq->sequence[i] = unsvalue[i]; + } + + ++i; + + /* Absolute limit - don't exceed */ + + if (i >= MAX_CHEAT_LEN - cheat->seq->parameter_chars) + { + deh_error(context, "Cheat sequence too long!"); + return; + } + } + + if (deh_apply_cheats) + { + cheat->seq->sequence[i] = '\0'; + } +} diff --git a/games/NXDoom/src/doom/deh_doom.c b/games/NXDoom/src/doom/deh_doom.c new file mode 100644 index 00000000000..419caffa8b8 --- /dev/null +++ b/games/NXDoom/src/doom/deh_doom.c @@ -0,0 +1,65 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_doom.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * + * Top-level dehacked definitions for Doom dehacked. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_defs.h" +#include "deh_main.h" +#include + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const char *deh_signatures[] = +{ + "Patch File for DeHackEd v2.3", + "Patch File for DeHackEd v3.0", + NULL, +}; + +/* List of section types: */ + +deh_section_t *deh_section_types[] = +{ + &deh_section_ammo, + &deh_section_cheat, + &deh_section_frame, + &deh_section_misc, + &deh_section_pointer, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + &deh_section_sound, +#else + NULL, +#endif + &deh_section_text, + &deh_section_thing, + &deh_section_weapon, + &deh_section_bexstr, + NULL, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/deh_frame.c b/games/NXDoom/src/doom/deh_frame.c new file mode 100644 index 00000000000..d5ec5521688 --- /dev/null +++ b/games/NXDoom/src/doom/deh_frame.c @@ -0,0 +1,198 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_frame.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Parses "Frame" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "d_items.h" +#include "doomtype.h" +#include "info.h" + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" +#include "deh_mapping.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +DEH_BEGIN_MAPPING(state_mapping, state_t) +DEH_MAPPING("Sprite number", sprite) +DEH_MAPPING("Sprite subnumber", frame) +DEH_MAPPING("Duration", tics) +DEH_MAPPING("Next frame", nextstate) +#if 0 +DEH_MAPPING("Unknown 1", misc1) +DEH_MAPPING("Unknown 2", misc2) +#endif +DEH_UNSUPPORTED_MAPPING("Codep frame") +DEH_END_MAPPING + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_frame_start(deh_context_t *context, char *line); +static void deh_frame_overflow(deh_context_t *context, char *varname, + int value); +static void deh_frame_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_frame_sha1_sum(SHA1_CTX *context); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_frame = +{ + "Frame", + NULL, + deh_frame_start, + deh_frame_parse_line, + NULL, + deh_frame_sha1_sum, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_frame_start(deh_context_t *context, char *line) +{ + int frame_number = 0; + state_t *state; + + if (sscanf(line, "Frame %i", &frame_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + if (frame_number < 0 || frame_number >= NUMSTATES) + { + deh_warning(context, "Invalid frame number: %i", frame_number); + return NULL; + } + + if (frame_number >= DEH_VANILLA_NUMSTATES) + { + deh_warning(context, + "Attempt to modify frame %i: this will cause " + "problems in Vanilla dehacked.", + frame_number); + } + + state = &states[frame_number]; + + return state; +} + +/* Simulate a frame overflow: Doom has 967 frames in the states[] array, but + * DOS dehacked internally only allocates memory for 966. As a result, + * attempts to set frame 966 (the last frame) will overflow the dehacked + * array and overwrite the weaponinfo[] array instead. + * + * This is noticeable in Batman Doom where it is impossible to switch weapons + * away from the fist once selected. + */ + +static void deh_frame_overflow(deh_context_t *context, char *varname, + int value) +{ + if (!strcasecmp(varname, "Duration")) + { + weaponinfo[0].ammo = value; + } + else if (!strcasecmp(varname, "Codep frame")) + { + weaponinfo[0].upstate = value; + } + else if (!strcasecmp(varname, "Next frame")) + { + weaponinfo[0].downstate = value; + } + else if (!strcasecmp(varname, "Unknown 1")) + { + weaponinfo[0].readystate = value; + } + else if (!strcasecmp(varname, "Unknown 2")) + { + weaponinfo[0].atkstate = value; + } + else + { + deh_error(context, "Unable to simulate frame overflow: field '%s'", + varname); + } +} + +static void deh_frame_parse_line(deh_context_t *context, char *line, + void *tag) +{ + state_t *state; + char *variable_name; + char *value; + int ivalue; + + if (tag == NULL) return; + + state = (state_t *)tag; + + /* Parse the assignment */ + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + /* all values are integers */ + + ivalue = atoi(value); + + if (state == &states[NUMSTATES - 1]) + { + deh_frame_overflow(context, variable_name, ivalue); + } + else + { + /* set the appropriate field */ + + deh_set_mapping(context, &state_mapping, state, variable_name, ivalue); + } +} + +static void deh_frame_sha1_sum(SHA1_CTX *context) +{ + int i; + + for (i = 0; i < NUMSTATES; ++i) + { + deh_struct_sha1_sum(context, &state_mapping, &states[i]); + } +} diff --git a/games/NXDoom/src/doom/deh_misc.c b/games/NXDoom/src/doom/deh_misc.c new file mode 100644 index 00000000000..e932ce3dfeb --- /dev/null +++ b/games/NXDoom/src/doom/deh_misc.c @@ -0,0 +1,281 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_misc.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Parses "Misc" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" +#include "deh_misc.h" +#include "doomtype.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct misc_settings +{ + const char *deh_name; + int *value; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_misc_start(deh_context_t *context, char *line); +static void deh_misc_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_misc_sha1_sum(SHA1_CTX *context); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Dehacked: "Initial Health" + * This is the initial health a player has when starting anew. + * See g_player_reborn in g_game.c + */ + +int deh_initial_health = DEH_DEFAULT_INITIAL_HEALTH; + +/* Dehacked: "Initial bullets" + * This is the number of bullets the player has when starting anew. + * See g_player_reborn in g_game.c + */ + +int deh_initial_bullets = DEH_DEFAULT_INITIAL_BULLETS; + +/* Dehacked: "Max Health" + * This is the maximum health that can be reached using health + * potions. See p_touch_special_thing in p_inter.c + */ + +int deh_max_health = DEH_DEFAULT_MAX_HEALTH; + +/* Dehacked: "Max Armor" + * This is the maximum armor which can be reached by picking up + * armor helmets. See p_touch_special_thing in p_inter.c + */ + +int deh_max_armor = DEH_DEFAULT_MAX_ARMOR; + +/* Dehacked: "Green Armor Class" + * This is the armor class that is given when picking up the green + * armor or an armor helmet. See p_touch_special_thing in p_inter.c + * + * DOS dehacked only modifies the behavior of the green armor shirt, + * the armor class set by armor helmets is not affected. + */ + +int deh_green_armor_class = DEH_DEFAULT_GREEN_ARMOR_CLASS; + +/* Dehacked: "Blue Armor Class" + * This is the armor class that is given when picking up the blue + * armor or a megasphere. See p_touch_special_thing in p_inter.c + * + * DOS dehacked only modifies the MegaArmor behavior and not + * the MegaSphere, which always gives armor type 2. + */ + +int deh_blue_armor_class = DEH_DEFAULT_BLUE_ARMOR_CLASS; + +/* Dehacked: "Max soulsphere" + * The maximum health which can be reached by picking up the + * soulsphere. See p_touch_special_thing in p_inter.c + */ + +int deh_max_soulsphere = DEH_DEFAULT_MAX_SOULSPHERE; + +/* Dehacked: "Soulsphere health" + * The amount of health bonus that picking up a soulsphere + * gives. See p_touch_special_thing in p_inter.c + */ + +int deh_soulsphere_health = DEH_DEFAULT_SOULSPHERE_HEALTH; + +/* Dehacked: "Megasphere health" + * This is what the health is set to after picking up a + * megasphere. See p_touch_special_thing in p_inter.c + */ + +int deh_megasphere_health = DEH_DEFAULT_MEGASPHERE_HEALTH; + +/* Dehacked: "God mode health" + * This is what the health value is set to when cheating using + * the IDDQD god mode cheat. See st_responder in st_stuff.c + */ + +int deh_god_mode_health = DEH_DEFAULT_GOD_MODE_HEALTH; + +/* Dehacked: "IDFA Armor" + * This is what the armor is set to when using the IDFA cheat. + * See st_responder in st_stuff.c + */ + +int deh_idfa_armor = DEH_DEFAULT_IDFA_ARMOR; + +/* Dehacked: "IDFA Armor Class" + * This is what the armor class is set to when using the IDFA cheat. + * See st_responder in st_stuff.c + */ + +int deh_idfa_armor_class = DEH_DEFAULT_IDFA_ARMOR_CLASS; + +/* Dehacked: "IDKFA Armor" + * This is what the armor is set to when using the IDKFA cheat. + * See st_responder in st_stuff.c + */ + +int deh_idkfa_armor = DEH_DEFAULT_IDKFA_ARMOR; + +/* Dehacked: "IDKFA Armor Class" + * This is what the armor class is set to when using the IDKFA cheat. + * See st_responder in st_stuff.c + */ + +int deh_idkfa_armor_class = DEH_DEFAULT_IDKFA_ARMOR_CLASS; + +/* Dehacked: "BFG Cells/Shot" + * This is the number of CELLs firing the BFG uses up. + * See p_check_ammo and a_fire_bfg in p_pspr.c + */ + +int deh_bfg_cells_per_shot = DEH_DEFAULT_BFG_CELLS_PER_SHOT; + +/* Dehacked: "Monsters infight" + * This controls whether monsters can harm other monsters of the same + * species. For example, whether an imp fireball will damage other + * imps. The value of this in dehacked patches is weird - '202' means + * off, while '221' means on. + * + * See pit_check_thing in p_map.c + */ + +int deh_species_infighting = DEH_DEFAULT_SPECIES_INFIGHTING; + +deh_section_t deh_section_misc = +{ + "Misc", + NULL, + deh_misc_start, + deh_misc_parse_line, + NULL, + deh_misc_sha1_sum, +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct misc_settings misc_settings[] = +{ + {"Initial Health", &deh_initial_health}, + {"Initial Bullets", &deh_initial_bullets}, + {"Max Health", &deh_max_health}, + {"Max Armor", &deh_max_armor}, + {"Green Armor Class", &deh_green_armor_class}, + {"Blue Armor Class", &deh_blue_armor_class}, + {"Max Soulsphere", &deh_max_soulsphere}, + {"Soulsphere Health", &deh_soulsphere_health}, + {"Megasphere Health", &deh_megasphere_health}, + {"God Mode Health", &deh_god_mode_health}, + {"IDFA Armor", &deh_idfa_armor}, + {"IDFA Armor Class", &deh_idfa_armor_class}, + {"IDKFA Armor", &deh_idkfa_armor}, + {"IDKFA Armor Class", &deh_idkfa_armor_class}, + {"BFG Cells/Shot", &deh_bfg_cells_per_shot}, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_misc_start(deh_context_t *context, char *line) +{ + return NULL; +} + +static void deh_misc_parse_line(deh_context_t *context, char *line, + void *tag) +{ + char *variable_name; + char *value; + int ivalue; + size_t i; + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + ivalue = atoi(value); + + if (!strcasecmp(variable_name, "Monsters Infight")) + { + /* See notes above. */ + + if (ivalue == 202) + { + deh_species_infighting = 0; + } + else if (ivalue == 221) + { + deh_species_infighting = 1; + } + else + { + deh_warning(context, "Invalid value for 'Monsters Infight': %i", + ivalue); + } + + return; + } + + for (i = 0; i < arrlen(misc_settings); ++i) + { + if (!strcasecmp(variable_name, misc_settings[i].deh_name)) + { + *misc_settings[i].value = ivalue; + return; + } + } + + deh_warning(context, "Unknown Misc variable '%s'", variable_name); +} + +static void deh_misc_sha1_sum(SHA1_CTX *context) +{ + unsigned int i; + + for (i = 0; i < arrlen(misc_settings); ++i) + { + sha1_updateint32(context, *misc_settings[i].value); + } +} diff --git a/games/NXDoom/src/doom/deh_misc.h b/games/NXDoom/src/doom/deh_misc.h new file mode 100644 index 00000000000..a55eb64528c --- /dev/null +++ b/games/NXDoom/src/doom/deh_misc.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_misc.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Parses "Misc" sections in dehacked files + * + ****************************************************************************/ + +#ifndef DEH_MISC_H +#define DEH_MISC_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DEH_DEFAULT_INITIAL_HEALTH 100 +#define DEH_DEFAULT_INITIAL_BULLETS 50 +#define DEH_DEFAULT_MAX_HEALTH 200 +#define DEH_DEFAULT_MAX_ARMOR 200 +#define DEH_DEFAULT_GREEN_ARMOR_CLASS 1 +#define DEH_DEFAULT_BLUE_ARMOR_CLASS 2 +#define DEH_DEFAULT_MAX_SOULSPHERE 200 +#define DEH_DEFAULT_SOULSPHERE_HEALTH 100 +#define DEH_DEFAULT_MEGASPHERE_HEALTH 200 +#define DEH_DEFAULT_GOD_MODE_HEALTH 100 +#define DEH_DEFAULT_IDFA_ARMOR 200 +#define DEH_DEFAULT_IDFA_ARMOR_CLASS 2 +#define DEH_DEFAULT_IDKFA_ARMOR 200 +#define DEH_DEFAULT_IDKFA_ARMOR_CLASS 2 +#define DEH_DEFAULT_BFG_CELLS_PER_SHOT 40 +#define DEH_DEFAULT_SPECIES_INFIGHTING 0 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int deh_initial_health; +extern int deh_initial_bullets; +extern int deh_max_health; +extern int deh_max_armor; +extern int deh_green_armor_class; +extern int deh_blue_armor_class; +extern int deh_max_soulsphere; +extern int deh_soulsphere_health; +extern int deh_megasphere_health; +extern int deh_god_mode_health; +extern int deh_idfa_armor; +extern int deh_idfa_armor_class; +extern int deh_idkfa_armor; +extern int deh_idkfa_armor_class; +extern int deh_bfg_cells_per_shot; +extern int deh_species_infighting; + +#if 0 + +/* To compile without dehacked, it's possible to use these: */ + +#define deh_initial_health DEH_DEFAULT_INITIAL_HEALTH +#define deh_initial_bullets DEH_DEFAULT_INITIAL_BULLETS +#define deh_max_health DEH_DEFAULT_MAX_HEALTH +#define deh_max_armor DEH_DEFAULT_MAX_ARMOR +#define deh_green_armor_class DEH_DEFAULT_GREEN_ARMOR_CLASS +#define deh_blue_armor_class DEH_DEFAULT_BLUE_ARMOR_CLASS +#define deh_max_soulsphere DEH_DEFAULT_MAX_SOULSPHERE +#define deh_soulsphere_health DEH_DEFAULT_SOULSPHERE_HEALTH +#define deh_megasphere_health DEH_DEFAULT_MEGASPHERE_HEALTH +#define deh_god_mode_health DEH_DEFAULT_GOD_MODE_HEALTH +#define deh_idfa_armor DEH_DEFAULT_IDFA_ARMOR +#define deh_idfa_armor_class DEH_DEFAULT_IDFA_ARMOR_CLASS +#define deh_idkfa_armor DEH_DEFAULT_IDKFA_ARMOR +#define deh_idkfa_armor_class DEH_DEFAULT_IDKFA_ARMOR_CLASS +#define deh_bfg_cells_per_shot DEH_DEFAULT_BFG_CELLS_PER_SHOT +#define deh_species_infighting DEH_DEFAULT_SPECIES_INFIGHTING + +#endif + +#endif /* DEH_MISC_H */ diff --git a/games/NXDoom/src/doom/deh_ptr.c b/games/NXDoom/src/doom/deh_ptr.c new file mode 100644 index 00000000000..78c14c75e78 --- /dev/null +++ b/games/NXDoom/src/doom/deh_ptr.c @@ -0,0 +1,172 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_ptr.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Parses Action Pointer entries in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" +#include "info.h" + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void deh_pointer_init(void); +static void *deh_pointer_start(deh_context_t *context, char *line); +static void deh_pointer_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_pointer_sha1_sum(SHA1_CTX *context); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static actionf_t codeptrs[NUMSTATES]; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_pointer = +{ + "Pointer", + deh_pointer_init, + deh_pointer_start, + deh_pointer_parse_line, + NULL, + deh_pointer_sha1_sum, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int code_pointer_index(actionf_t *ptr) +{ + int i; + + for (i = 0; i < NUMSTATES; ++i) + { + if (!memcmp(&codeptrs[i], ptr, sizeof(actionf_t))) + { + return i; + } + } + + return -1; +} + +static void deh_pointer_init(void) +{ + int i; + + /* Initialize list of dehacked pointers */ + + for (i = 0; i < NUMSTATES; ++i) + codeptrs[i] = states[i].action; +} + +static void *deh_pointer_start(deh_context_t *context, char *line) +{ + int frame_number = 0; + + /* FIXME: can the third argument here be something other than "Frame" + * or are we ok? + */ + + if (sscanf(line, "Pointer %*i (%*s %i)", &frame_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + if (frame_number < 0 || frame_number >= NUMSTATES) + { + deh_warning(context, "Invalid frame number: %i", frame_number); + return NULL; + } + + return &states[frame_number]; +} + +static void deh_pointer_parse_line(deh_context_t *context, char *line, + void *tag) +{ + state_t *state; + char *variable_name; + char *value; + int ivalue; + + if (tag == NULL) return; + + state = (state_t *)tag; + + /* Parse the assignment */ + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + /* all values are integers */ + + ivalue = atoi(value); + + /* set the appropriate field */ + + if (!strcasecmp(variable_name, "Codep frame")) + { + if (ivalue < 0 || ivalue >= NUMSTATES) + { + deh_warning(context, "Invalid state '%i'", ivalue); + } + else + { + state->action = codeptrs[ivalue]; + } + } + else + { + deh_warning(context, "Unknown variable name '%s'", variable_name); + } +} + +static void deh_pointer_sha1_sum(SHA1_CTX *context) +{ + int i; + + for (i = 0; i < NUMSTATES; ++i) + { + sha1_updateint32(context, code_pointer_index(&states[i].action)); + } +} diff --git a/games/NXDoom/src/doom/deh_sound.c b/games/NXDoom/src/doom/deh_sound.c new file mode 100644 index 00000000000..e3a8d12c7f5 --- /dev/null +++ b/games/NXDoom/src/doom/deh_sound.c @@ -0,0 +1,133 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_sound.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Parses "Sound" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_defs.h" +#include "deh_main.h" +#include "deh_mapping.h" +#include "doomtype.h" +#include "sounds.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t) +DEH_UNSUPPORTED_MAPPING("Offset") +DEH_UNSUPPORTED_MAPPING("Zero/One") +DEH_MAPPING("Value", priority) +DEH_MAPPING("Zero 1", link) +DEH_MAPPING("Zero 2", pitch) +DEH_MAPPING("Zero 3", volume) +DEH_UNSUPPORTED_MAPPING("Zero 4") +DEH_MAPPING("Neg. One 1", usefulness) +DEH_MAPPING("Neg. One 2", lumpnum) +DEH_END_MAPPING + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_sound_start(deh_context_t *context, char *line); +static void deh_sound_parse_line(deh_context_t *context, char *line, + void *tag); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_sound = +{ + "Sound", + NULL, + deh_sound_start, + deh_sound_parse_line, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_sound_start(deh_context_t *context, char *line) +{ + int sound_number = 0; + + if (sscanf(line, "Sound %i", &sound_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + if (sound_number < 0 || sound_number >= SFX_NUMSFX) + { + deh_warning(context, "Invalid sound number: %i", sound_number); + return NULL; + } + + if (sound_number >= DEH_VANILLA_NUMSFX) + { + deh_warning(context, + "Attempt to modify SFX %i. This will cause " + "problems in Vanilla dehacked.", + sound_number); + } + + return &s_sfx[sound_number]; +} + +static void deh_sound_parse_line(deh_context_t *context, char *line, + void *tag) +{ + sfxinfo_t *sfx; + char *variable_name; + char *value; + int ivalue; + + if (tag == NULL) return; + + sfx = (sfxinfo_t *)tag; + + /* Parse the assignment */ + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + /* all values are integers */ + + ivalue = atoi(value); + + /* Set the field value */ + + deh_set_mapping(context, &sound_mapping, sfx, variable_name, ivalue); +} diff --git a/games/NXDoom/src/doom/deh_thing.c b/games/NXDoom/src/doom/deh_thing.c new file mode 100644 index 00000000000..964b423b2cb --- /dev/null +++ b/games/NXDoom/src/doom/deh_thing.c @@ -0,0 +1,161 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_thing.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Parses "Thing" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" + +#include "deh_defs.h" +#include "deh_main.h" +#include "deh_mapping.h" + +#include "info.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t) +DEH_MAPPING("Bits", flags) +DEH_MAPPING("Width", radius) +DEH_MAPPING("Height", height) +DEH_MAPPING("Mass", mass) +DEH_MAPPING("Speed", speed) +DEH_MAPPING("ID #", doomednum) +DEH_MAPPING("Hit points", spawnhealth) +DEH_MAPPING("Initial frame", spawnstate) +DEH_MAPPING("First moving frame", seestate) +DEH_MAPPING("Injury frame", painstate) +DEH_MAPPING("Pain chance", painchance) +DEH_MAPPING("Close attack frame", meleestate) +DEH_MAPPING("Far attack frame", missilestate) +DEH_MAPPING("Death frame", deathstate) +DEH_MAPPING("Exploding frame", xdeathstate) +DEH_MAPPING("Respawn frame", raisestate) +DEH_MAPPING("Missile damage", damage) +#ifdef CONFIG_GAMES_NXDOOM_SOUND +DEH_MAPPING("Reaction time", reactiontime) +DEH_MAPPING("Alert sound", seesound) +DEH_MAPPING("Attack sound", attacksound) +DEH_MAPPING("Pain sound", painsound) +DEH_MAPPING("Death sound", deathsound) +DEH_MAPPING("Action sound", activesound) +#endif +DEH_END_MAPPING + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_thing_start(deh_context_t *context, char *line); +static void deh_thing_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_thing_sha1_sum(SHA1_CTX *context); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_thing = +{ + "Thing", + NULL, + deh_thing_start, + deh_thing_parse_line, + NULL, + deh_thing_sha1_sum, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_thing_start(deh_context_t *context, char *line) +{ + int thing_number = 0; + mobjinfo_t *mobj; + + if (sscanf(line, "Thing %i", &thing_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + /* dehacked files are indexed from 1 */ + + --thing_number; + + if (thing_number < 0 || thing_number >= NUMMOBJTYPES) + { + deh_warning(context, "Invalid thing number: %i", thing_number); + return NULL; + } + + mobj = &mobjinfo[thing_number]; + + return mobj; +} + +static void deh_thing_parse_line(deh_context_t *context, char *line, + void *tag) +{ + mobjinfo_t *mobj; + char *variable_name; + char *value; + int ivalue; + + if (tag == NULL) return; + + mobj = (mobjinfo_t *)tag; + + /* Parse the assignment */ + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + /* all values are integers */ + + ivalue = atoi(value); + + /* Set the field value */ + + deh_set_mapping(context, &thing_mapping, mobj, variable_name, ivalue); +} + +static void deh_thing_sha1_sum(SHA1_CTX *context) +{ + int i; + + for (i = 0; i < NUMMOBJTYPES; ++i) + { + deh_struct_sha1_sum(context, &thing_mapping, &mobjinfo[i]); + } +} diff --git a/games/NXDoom/src/doom/deh_weapon.c b/games/NXDoom/src/doom/deh_weapon.c new file mode 100644 index 00000000000..7c2ea08c34e --- /dev/null +++ b/games/NXDoom/src/doom/deh_weapon.c @@ -0,0 +1,131 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_weapon.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Parses "Weapon" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" + +#include "d_items.h" + +#include "deh_defs.h" +#include "deh_main.h" +#include "deh_mapping.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t) +DEH_MAPPING("Ammo type", ammo) +DEH_MAPPING("Deselect frame", upstate) +DEH_MAPPING("Select frame", downstate) +DEH_MAPPING("Bobbing frame", readystate) +DEH_MAPPING("Shooting frame", atkstate) +DEH_MAPPING("Firing frame", flashstate) +DEH_END_MAPPING + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_weapon_start(deh_context_t *context, char *line); +static void deh_weapon_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_weapon_sha1_sum(SHA1_CTX *context); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_weapon = +{ + "Weapon", + NULL, + deh_weapon_start, + deh_weapon_parse_line, + NULL, + deh_weapon_sha1_sum, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_weapon_start(deh_context_t *context, char *line) +{ + int weapon_number = 0; + + if (sscanf(line, "Weapon %i", &weapon_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + if (weapon_number < 0 || weapon_number >= NUMWEAPONS) + { + deh_warning(context, "Invalid weapon number: %i", weapon_number); + return NULL; + } + + return &weaponinfo[weapon_number]; +} + +static void deh_weapon_parse_line(deh_context_t *context, char *line, + void *tag) +{ + char *variable_name; + char *value; + weaponinfo_t *weapon; + int ivalue; + + if (tag == NULL) return; + + weapon = (weaponinfo_t *)tag; + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + ivalue = atoi(value); + + deh_set_mapping(context, &weapon_mapping, weapon, variable_name, ivalue); +} + +static void deh_weapon_sha1_sum(SHA1_CTX *context) +{ + int i; + + for (i = 0; i < NUMWEAPONS; ++i) + { + deh_struct_sha1_sum(context, &weapon_mapping, &weaponinfo[i]); + } +} + diff --git a/games/NXDoom/src/doom/doomdata.h b/games/NXDoom/src/doom/doomdata.h new file mode 100644 index 00000000000..ede23cf5ee9 --- /dev/null +++ b/games/NXDoom/src/doom/doomdata.h @@ -0,0 +1,248 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/doomdata.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* The most basic types we use, portability. */ + +#include "doomtype.h" + +/* Some global defines, that configure the game. */ + +#include "doomdef.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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 always have the first row of the texture at the top + * pixel 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 + +/* Indicate a leaf. */ + +#define NF_SUBSECTOR 0x8000 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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_VERTICES, /* 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. */ + +begin_packed_struct struct mapvertex_t +{ + short x; + short y; +} end_packed_struct; + +typedef struct mapvertex_t mapvertex_t; + +/* A SideDef, defining the visual appearance of a wall, by setting textures + * and offsets. + */ + +begin_packed_struct struct mapsidedef_t +{ + short textureoffset; + short rowoffset; + char toptexture[8]; + char bottomtexture[8]; + char midtexture[8]; + short sector; +} end_packed_struct; + +typedef struct mapsidedef_t mapsidedef_t; + +/* A LineDef, as used for editing, and as input to the BSP builder. + */ + +begin_packed_struct struct maplinedef_t +{ + short v1; + short v2; + short flags; + short special; + short tag; + + /* sidenum[1] will be -1 if one sided */ + + short sidenum[2]; +} end_packed_struct; + +typedef struct maplinedef_t maplinedef_t; + +/* Sector definition, from editing. */ + +begin_packed_struct struct mapsector_t +{ + short floorheight; + short ceilingheight; + char floorpic[8]; + char ceilingpic[8]; + short lightlevel; + short special; + short tag; +} end_packed_struct; + +typedef struct mapsector_t mapsector_t; + +/* SubSector, as generated by BSP. */ + +begin_packed_struct struct mapsubsector_t +{ + short numsegs; + + /* Index of first one, segs are stored sequentially. */ + + short firstseg; +} end_packed_struct; + +typedef struct mapsubsector_t mapsubsector_t; + +/* LineSeg, generated by splitting LineDefs using partition lines selected by + * BSP builder. + */ + +begin_packed_struct struct mapseg_t +{ + short v1; + short v2; + short angle; + short linedef; + short side; + short offset; +} end_packed_struct; + +typedef struct mapseg_t mapseg_t; + +/* BSP node structure. */ + +begin_packed_struct struct mapnode_t +{ + /* 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]; +} end_packed_struct; + +typedef struct mapnode_t mapnode_t; + +/* Thing definition, position, orientation and type, plus skill/visibility + * flags and attributes. + */ + +begin_packed_struct struct mapthing_t +{ + short x; + short y; + short angle; + short type; + short options; +} end_packed_struct; + +typedef struct mapthing_t mapthing_t; + +#endif /* __DOOMDATA__ */ diff --git a/games/NXDoom/src/doom/doomdef.c b/games/NXDoom/src/doom/doomdef.c new file mode 100644 index 00000000000..5cc2278842f --- /dev/null +++ b/games/NXDoom/src/doom/doomdef.c @@ -0,0 +1,33 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/doomdef.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * DoomDef - basic defines for DOOM, e.g. Version, game mode + * and skill level, and display parameters. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/doomdef.h b/games/NXDoom/src/doom/doomdef.h new file mode 100644 index 00000000000..cf00f12ee18 --- /dev/null +++ b/games/NXDoom/src/doom/doomdef.h @@ -0,0 +1,171 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/doomdef.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Internally used data structures for virtually everything, lots of other + * stuff. + * + ****************************************************************************/ + +#ifndef __DOOMDEF__ +#define __DOOMDEF__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "d_mode.h" +#include "doomtype.h" +#include "i_timer.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* DOOM version */ + +#define DOOM_VERSION 109 + +/* Version code for cph's longtics hack ("v1.91") */ + +#define DOOM_191_VERSION 111 + +/* The maximum number of players, multiplayer/networking. */ + +#define MAXPLAYERS 4 + +/* 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 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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; + +typedef enum +{ + ga_nothing, + ga_loadlevel, + ga_newgame, + ga_loadgame, + ga_savegame, + ga_playdemo, + ga_completed, + ga_victory, + ga_worlddone, + ga_screenshot +} gameaction_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; + +#endif /* __DOOMDEF__ */ diff --git a/games/NXDoom/src/doom/doomstat.c b/games/NXDoom/src/doom/doomstat.c new file mode 100644 index 00000000000..5f38dd84ccf --- /dev/null +++ b/games/NXDoom/src/doom/doomstat.c @@ -0,0 +1,49 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/doomstat.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Put all global state variables here. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomstat.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Game Mode - identify IWAD as shareware, retail etc. */ + +game_mode_t gamemode = indetermined; +gamemission_t gamemission = doom; +game_version_t gameversion = exe_final2; +game_variant_t gamevariant = vanilla; + +/* Set if homebrew PWAD stuff has been added. */ + +boolean modifiedgame; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/doomstat.h b/games/NXDoom/src/doom/doomstat.h new file mode 100644 index 00000000000..55f31f13047 --- /dev/null +++ b/games/NXDoom/src/doom/doomstat.h @@ -0,0 +1,273 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/doomstat.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* We need globally shared data structures, for defining the global state + * variables. + */ + +#include "d_loop.h" +#include "doomdata.h" + +/* We need the player data structure as well. */ + +#include "d_player.h" + +/* Game mode/mission */ + +#include "d_mode.h" + +#include "net_defs.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Convenience macro. + * 'gamemission' can be equal to pack_chex or pack_hacx, but these are + * just modified versions of doom and doom2, and should be interpreted + * as the same most of the time. + */ + +#define logical_gamemission \ + (gamemission == pack_chex ? doom \ + : gamemission == pack_hacx ? doom2 \ + : gamemission) + +#define MAX_DM_STARTS 10 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Command line parameters. */ + +extern boolean nomonsters; /* checkparm of -nomonsters */ +extern boolean respawnparm; /* checkparm of -respawn */ +extern boolean fastparm; /* checkparm of -fast */ + +extern boolean devparm; /* DEBUG: launched with -devparm */ + +/* Game Mode - identify IWAD as shareware, retail etc. */ + +extern game_mode_t gamemode; +extern gamemission_t gamemission; +extern game_version_t gameversion; +extern game_variant_t gamevariant; + +/* Set if homebrew PWAD stuff has been added. */ + +extern boolean modifiedgame; + +/* Selected skill type, map etc. */ + +/* Defaults for menu, methinks. */ + +extern skill_t startskill; +extern int startepisode; +extern int startmap; + +/* Savegame slot to load on startup. This is the value provided to + * the -loadgame option. If this has not been provided, this is -1. + */ + +extern int startloadgame; + +extern boolean autostart; + +/* Selected by user. */ + +extern skill_t gameskill; +extern int gameepisode; +extern int gamemap; + +/* If non-zero, exit the level after this number of minutes */ + +extern int timelimit; + +/* Nightmare mode flag, single player. */ + +extern boolean respawnmonsters; + +/* Netgame? Only true if >1 player. */ + +extern boolean netgame; + +/* 0=Cooperative; 1=Deathmatch; 2=Altdeath */ + +extern int 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. + * From m_menu.c: + * Sound FX volume has default, 0 - 15 + * Music volume has default, 0 - 15 + * These are multiplied by 8. + */ + +extern int g_sfx_volume; +extern int g_music_volume; + +/* Status flags for refresh. */ + +/* Depending on view size - no status bar? + * Note that there is no way to disable the status bar explicitly. + */ + +extern boolean statusbaractive; + +extern boolean automapactive; /* In AutoMap mode? */ +extern boolean g_menuactive; /* Menu over-layed? */ +extern boolean paused; /* Game Pause? */ + +extern boolean viewactive; + +extern boolean nodrawers; + +extern boolean testcontrols; +extern int testcontrols_mousespeed; + +/* 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 boolean usergame; + +/* ? */ + +extern boolean demoplayback; +extern boolean demorecording; + +/* Round angleturn in ticcmds to the nearest 256. This is used when + * recording Vanilla demos in netgames. + */ + +extern boolean lowres_turn; + +/* Quit after playing a demo from cmdline. */ + +extern 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. + */ + +/* Bookkeeping on players - state. */ + +extern player_t players[MAXPLAYERS]; + +/* Alive? Disconnected? */ + +extern boolean playeringame[MAXPLAYERS]; + +/* Player spawn spots for deathmatch. */ + +extern mapthing_t deathmatchstarts[MAX_DM_STARTS]; +extern mapthing_t *deathmatch_p; + +/* Player spawn spots. */ + +extern mapthing_t playerstarts[MAXPLAYERS]; +extern boolean playerstartsingame[MAXPLAYERS]; + +/* Intermission stats. + * Parameters for world map / intermission. + */ + +extern wbstartstruct_t wminfo; + +/* Internal parameters, used for engine. */ + +/* File handling stuff. */ + +extern char *savegamedir; + +/* if true, load all graphics at level load */ + +extern boolean precache; + +/* wipegamestate can be set to -1 to force a wipe on the next draw */ + +extern gamestate_t wipegamestate; + +extern int g_mouse_sensitivity; + +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). */ + +extern int rndindex; + +extern ticcmd_t *netcmds; + +#endif /* __D_STATE__ */ diff --git a/games/NXDoom/src/doom/dstrings.c b/games/NXDoom/src/doom/dstrings.c new file mode 100644 index 00000000000..514e8c40865 --- /dev/null +++ b/games/NXDoom/src/doom/dstrings.c @@ -0,0 +1,71 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/dstrings.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Globally defined strings. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "dstrings.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const char *doom1_endmsg[] = +{ + DOOM1_ENDMSG +}; + +const char *doom2_endmsg[] = +{ + DOOM2_ENDMSG +}; + +/* UNUSED messages included in the source release */ + +#if 0 +char *endmsg[] = +{ + /* DOOM1 */ + + QUITMSG, + + /* 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.", +}; +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/dstrings.h b/games/NXDoom/src/doom/dstrings.h new file mode 100644 index 00000000000..b1330f66de2 --- /dev/null +++ b/games/NXDoom/src/doom/dstrings.h @@ -0,0 +1,62 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/dstrings.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * DOOM strings, by language. + * + ****************************************************************************/ + +#ifndef __DSTRINGS__ +#define __DSTRINGS__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/* All important printed strings. */ + +#if defined(CONFIG_GAMES_NXDOOM_LANG_EN) +#include "d_englsh.h" +#elif defined(CONFIG_GAMES_NXDOOM_LANG_FR) +#include "d_french.h" +#else +#error "Unsupported language choice." +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Misc. other strings. */ + +#define SAVEGAMENAME "doomsav" + +/* QuitDOOM messages; 8 per each game type */ + +#define NUM_QUITMESSAGES (8) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern const char *doom1_endmsg[]; +extern const char *doom2_endmsg[]; + +#endif /* __DSTRINGS__ */ diff --git a/games/NXDoom/src/doom/f_finale.c b/games/NXDoom/src/doom/f_finale.c new file mode 100644 index 00000000000..a9d964affba --- /dev/null +++ b/games/NXDoom/src/doom/f_finale.c @@ -0,0 +1,769 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/f_finale.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Game completion, final screen animation. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/* Functions. */ + +#include "deh_main.h" +#include "i_swap.h" +#include "i_system.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* Data. */ + +#include "d_main.h" +#include "dstrings.h" + +#include "doomstat.h" +#include "r_state.h" + +#include "hu_stuff.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TEXTSPEED 3 +#define TEXTWAIT 250 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + F_STAGE_TEXT, + F_STAGE_ARTSCREEN, + F_STAGE_CAST, +} finalestage_t; + +/* ? + * #include "doomstat.h" + * #include "r_local.h" + * #include "f_finale.h" + */ + +typedef struct +{ + gamemission_t mission; + int episode; + int level; + const char *background; + const char *text; +} textscreen_t; + +typedef struct +{ + const char *name; + mobjtype_t type; +} castinfo_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static textscreen_t g_textscreens[] = +{ + {doom, 1, 8, "FLOOR4_8", E1TEXT}, + {doom, 2, 8, "SFLR6_1", E2TEXT}, + {doom, 3, 8, "MFLR8_4", E3TEXT}, + {doom, 4, 8, "MFLR8_3", E4TEXT}, + {doom2, 1, 6, "SLIME16", C1TEXT}, + {doom2, 1, 11, "RROCK14", C2TEXT}, + {doom2, 1, 20, "RROCK07", C3TEXT}, + {doom2, 1, 30, "RROCK17", C4TEXT}, + {doom2, 1, 15, "RROCK13", C5TEXT}, + {doom2, 1, 31, "RROCK19", C6TEXT}, + {pack_tnt, 1, 6, "SLIME16", T1TEXT}, + {pack_tnt, 1, 11, "RROCK14", T2TEXT}, + {pack_tnt, 1, 20, "RROCK07", T3TEXT}, + {pack_tnt, 1, 30, "RROCK17", T4TEXT}, + {pack_tnt, 1, 15, "RROCK13", T5TEXT}, + {pack_tnt, 1, 31, "RROCK19", T6TEXT}, + {pack_plut, 1, 6, "SLIME16", P1TEXT}, + {pack_plut, 1, 11, "RROCK14", P2TEXT}, + {pack_plut, 1, 20, "RROCK07", P3TEXT}, + {pack_plut, 1, 30, "RROCK17", P4TEXT}, + {pack_plut, 1, 15, "RROCK13", P5TEXT}, + {pack_plut, 1, 31, "RROCK19", P6TEXT}, +}; + +/* Final DOOM 2 animation + * Casting by id Software. in order of appearance + */ + +static castinfo_t g_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}, + {NULL, 0}, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Stage of animation: */ + +finalestage_t finalestage; +unsigned int finalecount; + +const char *finaletext; +const char *finaleflat; + +int castnum; +int casttics; +state_t *caststate; +boolean castdeath; +int castframes; +int castonmelee; +boolean castattacking; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void f_start_cast(void) +{ + wipegamestate = -1; /* force a screen wipe */ + castnum = 0; + caststate = &states[mobjinfo[g_castorder[castnum].type].seestate]; + casttics = caststate->tics; + castdeath = false; + finalestage = F_STAGE_CAST; + castframes = 0; + castonmelee = 0; + castattacking = false; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_change_music(MUS_EVIL, true); +#endif +} + +static void f_text_write(void) +{ + byte *src; + pixel_t *dest; + int x; + int y; + int w; + signed int count; + const char *ch; + int c; + int cx; + int cy; + + /* erase the entire screen to a tiled background */ + + src = w_cache_lump_name(finaleflat, PU_CACHE); + dest = i_video_buffer; + + for (y = 0; y < SCREENHEIGHT; y++) + { + for (x = 0; x < SCREENWIDTH / 64; x++) + { + memcpy(dest, src + ((y & 63) << 6), 64); + dest += 64; + } + + if (SCREENWIDTH & 63) + { + memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); + dest += (SCREENWIDTH & 63); + } + } + + v_mark_rect(0, 0, SCREENWIDTH, SCREENHEIGHT); + + /* draw some of the text onto the screen */ + + cx = 10; + cy = 10; + ch = finaletext; + + count = ((signed int)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 = 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_draw_patch(cx, cy, hu_font[c]); + cx += w; + } +} + +static void f_cast_ticker(void) +{ + int st; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + int sfx; +#endif + + 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 (g_castorder[castnum].name == NULL) castnum = 0; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (mobjinfo[g_castorder[castnum].type].seesound) + { + s_start_sound(NULL, mobjinfo[g_castorder[castnum].type].seesound); + } + +#endif + + caststate = &states[mobjinfo[g_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.... */ + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + 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_start_sound(NULL, sfx); +#endif + } + + if (castframes == 12) + { + /* go into attack frame */ + + castattacking = true; + + if (castonmelee) + { + caststate = + &states[mobjinfo[g_castorder[castnum].type].meleestate]; + } + else + { + caststate = + &states[mobjinfo[g_castorder[castnum].type].missilestate]; + } + + castonmelee ^= 1; + + if (caststate == &states[S_NULL]) + { + if (castonmelee) + { + caststate = + &states[mobjinfo[g_castorder[castnum].type].meleestate]; + } + else + { + caststate = + &states[mobjinfo[g_castorder[castnum].type].missilestate]; + } + } + } + + if (castattacking) + { + if (castframes == 24 || + caststate == &states[mobjinfo[g_castorder[castnum].type].seestate]) + { + stopattack: + castattacking = false; + castframes = 0; + caststate = &states[mobjinfo[g_castorder[castnum].type].seestate]; + } + } + + casttics = caststate->tics; + if (casttics == -1) casttics = 15; +} + +static boolean f_cast_responder(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[g_castorder[castnum].type].deathstate]; + casttics = caststate->tics; + castframes = 0; + castattacking = false; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (mobjinfo[g_castorder[castnum].type].deathsound) + { + s_start_sound(NULL, mobjinfo[g_castorder[castnum].type].deathsound); + } +#endif + + return true; +} + +static void f_cast_print(const char *text) +{ + const char *ch; + int c; + int cx; + int w; + int width; + + /* find width */ + + ch = text; + width = 0; + + while (ch) + { + c = *ch++; + if (!c) break; + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + { + width += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + width += w; + } + + /* draw it */ + + cx = SCREENWIDTH / 2 - width / 2; + ch = text; + while (ch) + { + c = *ch++; + if (!c) break; + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + v_draw_patch(cx, 180, hu_font[c]); + cx += w; + } +} + +static void f_cast_drawer(void) +{ + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + boolean flip; + patch_t *patch; + + /* erase the entire screen to a background */ + + v_draw_patch(0, 0, w_cache_lump_name(("BOSSBACK"), PU_CACHE)); + + f_cast_print((g_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 = (boolean)sprframe->flip[0]; + + patch = w_cache_lump_num(lump + firstspritelump, PU_CACHE); + if (flip) + v_draw_patch_flipped(SCREENWIDTH / 2, 170, patch); + else + v_draw_patch(SCREENWIDTH / 2, 170, patch); +} + +static void f_draw_patch_col(int x, patch_t *patch, int col) +{ + column_t *column; + byte *source; + pixel_t *dest; + pixel_t *desttop; + int count; + + column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); + desttop = i_video_buffer + 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); + } +} + +static void f_bunny_scroll(void) +{ + signed int scrolled; + int x; + patch_t *p1; + patch_t *p2; + char name[10]; + int stage; + static int laststage; + + p1 = w_cache_lump_name(("PFUB2"), PU_LEVEL); + p2 = w_cache_lump_name(("PFUB1"), PU_LEVEL); + + v_mark_rect(0, 0, SCREENWIDTH, SCREENHEIGHT); + + scrolled = (SCREENWIDTH - ((signed int)finalecount - 230) / 2); + if (scrolled > SCREENWIDTH) scrolled = SCREENWIDTH; + if (scrolled < 0) scrolled = 0; + + for (x = 0; x < SCREENWIDTH; x++) + { + if (x + scrolled < SCREENWIDTH) + f_draw_patch_col(x, p1, x + scrolled); + else + f_draw_patch_col(x, p2, x + scrolled - SCREENWIDTH); + } + + if (finalecount < 1130) return; + if (finalecount < 1180) + { + v_draw_patch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2, + w_cache_lump_name(("END0"), PU_CACHE)); + laststage = 0; + return; + } + + stage = (finalecount - 1180) / 5; + if (stage > 6) stage = 6; + if (stage > laststage) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PISTOL); +#endif + laststage = stage; + } + + snprintf(name, 10, "END%i", stage); + v_draw_patch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2, + w_cache_lump_name(name, PU_CACHE)); +} + +static void f_art_screen_drawer(void) +{ + const char *lumpname; + + if (gameepisode == 3) + { + f_bunny_scroll(); + } + else + { + switch (gameepisode) + { + case 1: + if (gameversion >= exe_ultimate) + { + lumpname = "CREDIT"; + } + else + { + lumpname = "HELP2"; + } + break; + case 2: + lumpname = "VICTORY2"; + break; + case 4: + lumpname = "ENDPIC"; + break; + default: + return; + } + + lumpname = (lumpname); + + v_draw_patch(0, 0, w_cache_lump_name(lumpname, PU_CACHE)); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* f_start_finale */ + +void f_start_finale(void) +{ + size_t i; + + gameaction = ga_nothing; + gamestate = GS_FINALE; + viewactive = false; + automapactive = false; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (logical_gamemission == doom) + { + s_change_music(MUS_VICTOR, true); + } + else + { + s_change_music(MUS_READ_M, true); + } +#endif + + /* Find the right screen and set the text and background */ + + for (i = 0; i < arrlen(g_textscreens); ++i) + { + textscreen_t *screen = &g_textscreens[i]; + + /* Hack for Chex Quest */ + + if (gameversion == exe_chex && screen->mission == doom) + { + screen->level = 5; + } + + if (logical_gamemission == screen->mission && + (logical_gamemission != doom || gameepisode == screen->episode) && + gamemap == screen->level) + { + finaletext = screen->text; + finaleflat = screen->background; + } + } + + /* Do dehacked substitutions of strings */ + + finaletext = (finaletext); + finaleflat = (finaleflat); + + finalestage = F_STAGE_TEXT; + finalecount = 0; +} + +boolean f_responder(event_t *event) +{ + if (finalestage == F_STAGE_CAST) return f_cast_responder(event); + + return false; +} + +void f_ticker(void) +{ + size_t 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_start_cast(); + else + gameaction = ga_worlddone; + } + } + + /* advance animation */ + + finalecount++; + + if (finalestage == F_STAGE_CAST) + { + f_cast_ticker(); + return; + } + + if (gamemode == commercial) return; + + if (finalestage == F_STAGE_TEXT && + finalecount > strlen(finaletext) * TEXTSPEED + TEXTWAIT) + { + finalecount = 0; + finalestage = F_STAGE_ARTSCREEN; + wipegamestate = -1; /* force a wipe */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameepisode == 3) s_start_music(MUS_BUNNY); +#endif + } +} + +void f_drawer(void) +{ + switch (finalestage) + { + case F_STAGE_CAST: + f_cast_drawer(); + break; + case F_STAGE_TEXT: + f_text_write(); + break; + case F_STAGE_ARTSCREEN: + f_art_screen_drawer(); + break; + } +} diff --git a/games/NXDoom/src/doom/f_finale.h b/games/NXDoom/src/doom/f_finale.h new file mode 100644 index 00000000000..3e60e15a7e2 --- /dev/null +++ b/games/NXDoom/src/doom/f_finale.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/f_finale.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef __F_FINALE__ +#define __F_FINALE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "doomtype.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Called by main loop. */ + +boolean f_responder(event_t *ev); + +/* Called by main loop. */ + +void f_ticker(void); + +/* Called by main loop. */ + +void f_drawer(void); + +void f_start_finale(void); + +#endif /* __F_FINALE__ */ diff --git a/games/NXDoom/src/doom/f_wipe.c b/games/NXDoom/src/doom/f_wipe.c new file mode 100644 index 00000000000..e10a0bd21eb --- /dev/null +++ b/games/NXDoom/src/doom/f_wipe.c @@ -0,0 +1,296 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/f_wipe.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Mission begin melt/wipe screen special effect. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "i_video.h" +#include "m_random.h" +#include "v_video.h" +#include "z_zone.h" + +#include "doomtype.h" + +#include "f_wipe.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void wipe_shitty_col_major_xform(dpixel_t *array, int width, + int height); +static int wip_init_colour_xform(int width, int height, int ticks); +static int wipe_do_colour_xform(int width, int height, int ticks); +static int wip_exit_color_xform(int width, int height, int ticks); +static int wipe_init_melt(int width, int height, int ticks); +static int wipe_do_melt(int width, int height, int ticks); +static int wipe_exit_melt(int width, int height, int ticks); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* when zero, stop the wipe */ + +static boolean go = 0; + +static pixel_t *wipe_scr_start; +static pixel_t *wipe_scr_end; +static pixel_t *wipe_scr; + +static int *g_y; + +static int (*g_wipes[])(int, int, int) = +{ + wip_init_colour_xform, wipe_do_colour_xform, wip_exit_color_xform, + wipe_init_melt, wipe_do_melt, wipe_exit_melt, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void wipe_shitty_col_major_xform(dpixel_t *array, int width, + int height) +{ + int x; + int y; + dpixel_t *dest; + + dest = (dpixel_t *)z_malloc(width * height * sizeof(*dest), PU_STATIC, 0); + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + dest[x * height + y] = array[y * width + x]; + } + } + + memcpy(array, dest, width * height * sizeof(*dest)); + + z_free(dest); +} + +static int wip_init_colour_xform(int width, int height, int ticks) +{ + memcpy(wipe_scr, wipe_scr_start, width * height * sizeof(*wipe_scr)); + return 0; +} + +static int wipe_do_colour_xform(int width, int height, int ticks) +{ + boolean changed; + pixel_t *w; + pixel_t *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; +} + +static int wip_exit_color_xform(int width, int height, int ticks) +{ + return 0; +} + +static int wipe_init_melt(int width, int height, int ticks) +{ + int i; + int r; + + /* copy start screen to main screen */ + + memcpy(wipe_scr, wipe_scr_start, width * height * sizeof(*wipe_scr)); + + /* makes this wipe faster (in theory) + * to have stuff in column-major format + */ + + wipe_shitty_col_major_xform((dpixel_t *)wipe_scr_start, width / 2, height); + wipe_shitty_col_major_xform((dpixel_t *)wipe_scr_end, width / 2, height); + + /* setup initial column positions + * (y<0 => not ready to scroll yet) + */ + + g_y = (int *)z_malloc(width * sizeof(int), PU_STATIC, 0); + g_y[0] = -(m_random() % 16); + for (i = 1; i < width; i++) + { + r = (m_random() % 3) - 1; + g_y[i] = g_y[i - 1] + r; + if (g_y[i] > 0) + g_y[i] = 0; + else if (g_y[i] == -16) + g_y[i] = -15; + } + + return 0; +} + +static int wipe_do_melt(int width, int height, int ticks) +{ + int i; + int j; + int dy; + int idx; + + dpixel_t *s; + dpixel_t *d; + boolean done = true; + + width /= 2; + + while (ticks--) + { + for (i = 0; i < width; i++) + { + if (g_y[i] < 0) + { + g_y[i]++; + done = false; + } + else if (g_y[i] < height) + { + dy = (g_y[i] < 16) ? g_y[i] + 1 : 8; + if (g_y[i] + dy >= height) dy = height - g_y[i]; + s = &((dpixel_t *)wipe_scr_end)[i * height + g_y[i]]; + d = &((dpixel_t *)wipe_scr)[g_y[i] * width + i]; + idx = 0; + for (j = dy; j; j--) + { + d[idx] = *(s++); + idx += width; + } + + g_y[i] += dy; + s = &((dpixel_t *)wipe_scr_start)[i * height]; + d = &((dpixel_t *)wipe_scr)[g_y[i] * width + i]; + idx = 0; + for (j = height - g_y[i]; j; j--) + { + d[idx] = *(s++); + idx += width; + } + + done = false; + } + } + } + + return done; +} + +static int wipe_exit_melt(int width, int height, int ticks) +{ + z_free(g_y); + z_free(wipe_scr_start); + z_free(wipe_scr_end); + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int wipe_start_screen(int x, int y, int width, int height) +{ + wipe_scr_start = z_malloc( + SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_start), PU_STATIC, NULL); + i_read_screen(wipe_scr_start); + return 0; +} + +int wipe_endscreen(int x, int y, int width, int height) +{ + wipe_scr_end = z_malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_end), + PU_STATIC, NULL); + i_read_screen(wipe_scr_end); + v_draw_block(x, y, width, height, wipe_scr_start); /* restore start scr. */ + return 0; +} + +int wipe_screen_wipe(int wipeno, int x, int y, int width, int height, + int ticks) +{ + int rc; + + /* initial stuff */ + + if (!go) + { + go = 1; + wipe_scr = i_video_buffer; + (*g_wipes[wipeno * 3])(width, height, ticks); + } + + /* do a piece of wipe-in */ + + v_mark_rect(0, 0, width, height); + rc = (*g_wipes[wipeno * 3 + 1])(width, height, ticks); + + /* final stuff */ + + if (rc) + { + go = 0; + (*g_wipes[wipeno * 3 + 2])(width, height, ticks); + } + + return !go; +} diff --git a/games/NXDoom/src/doom/f_wipe.h b/games/NXDoom/src/doom/f_wipe.h new file mode 100644 index 00000000000..c421385a598 --- /dev/null +++ b/games/NXDoom/src/doom/f_wipe.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/f_wipe.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Mission start screen wipe/melt, special effects. + * + ****************************************************************************/ + +#ifndef __F_WIPE_H__ +#define __F_WIPE_H__ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* SCREEN WIPE PACKAGE */ + +enum +{ + /* simple gradual pixel change for 8-bit only */ + + WIPE_COLORXFORM, + WIPE_MELT, /* weird screen melt */ + WIPE_NUMWIPES +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int wipe_start_screen(int x, int y, int width, int height); + +int wipe_endscreen(int x, int y, int width, int height); + +int wipe_screen_wipe(int wipeno, int x, int y, int width, int height, + int ticks); + +#endif /* __F_WIPE_H__ */ diff --git a/games/NXDoom/src/doom/g_game.c b/games/NXDoom/src/doom/g_game.c new file mode 100644 index 00000000000..03589f06b24 --- /dev/null +++ b/games/NXDoom/src/doom/g_game.c @@ -0,0 +1,2462 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/g_game.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: none + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomdef.h" +#include "doomkeys.h" +#include "doomstat.h" + +#include "deh_main.h" +#include "deh_misc.h" + +#include "f_finale.h" +#include "i_input.h" +#include "i_joystick.h" +#include "i_swap.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_controls.h" +#include "m_menu.h" +#include "m_misc.h" +#include "m_random.h" +#include "z_zone.h" + +#include "p_saveg.h" +#include "p_setup.h" +#include "p_tick.h" + +#include "d_main.h" + +#include "am_map.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "statdump.h" +#include "wi_stuff.h" + +/* Needs access to LFB. */ + +#include "v_video.h" + +#include "w_wad.h" + +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* Data. */ + +#include "dstrings.h" + +/* SKY handling - still the wrong place. */ + +#include "r_data.h" +#include "r_sky.h" + +#include "g_game.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SAVEGAMESIZE 0x2c000 + +#define MAXPLMOVE (forwardmove[1]) + +#define TURBOTHRESHOLD 0x32 + +#define BODYQUESIZE 32 + +#define SLOWTURNTICS 6 + +#define NUMKEYS 256 +#define MAX_JOY_BUTTONS 20 + +#define DEMOMARKER 0x80 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct weapon_order +{ + weapontype_t weapon; + weapontype_t weapon_num; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void p_spawn_player(mapthing_t *mthing); +void g_player_reborn(int player); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Gamestate the last time g_ticker was called. */ + +gamestate_t oldgamestate; + +gameaction_t gameaction; +gamestate_t gamestate; +skill_t gameskill; +boolean respawnmonsters; +int gameepisode; +int gamemap; + +/* If non-zero, exit the level after this number of minutes. */ + +int timelimit; + +boolean paused; +boolean sendpause; /* send a pause event next tic */ +boolean sendsave; /* send a save event next tic */ +boolean usergame; /* ok to save / end game */ + +boolean timingdemo; /* if true, exit with report on completion */ +boolean nodrawers; /* for comparative timing purposes */ +int starttime; /* for comparative timing purposes */ + +boolean viewactive; + +int deathmatch; /* only if started as net death */ +boolean netgame; /* only true if packets are broadcast */ +boolean playeringame[MAXPLAYERS]; +player_t players[MAXPLAYERS]; + +boolean turbodetected[MAXPLAYERS]; + +int consoleplayer; /* player taking events and displaying */ +int displayplayer; /* view being displayed */ +int levelstarttic; /* gametic at level start */ +int totalkills; +int totalitems; +int totalsecret; /* for intermission */ + +char *demoname; +boolean demorecording; +boolean longtics; /* cph's doom 1.91 longtics hack */ +boolean lowres_turn; /* low resolution turning for longtics */ +boolean demoplayback; +boolean netdemo; +byte *demobuffer; +byte *demo_p; +byte *demoend; +boolean singledemo; /* quit after playing a demo from cmdline */ + +boolean precache = true; /* if true, load all graphics at start */ + +boolean testcontrols = false; /* Invoked by setup to test controls */ +int testcontrols_mousespeed; + +wbstartstruct_t wminfo; /* params for world map / intermission */ + +byte consistency[MAXPLAYERS][CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + +fixed_t forwardmove[2] = +{ + 0x19, 0x32 +}; + +fixed_t sidemove[2] = +{ + 0x18, 0x28 +}; + +/* + slow turn */ + +fixed_t angleturn[3] = +{ + 640, 1280, 320 +}; + +/* mouse values are used once */ + +int mousex; +int mousey; + +mobj_t *bodyque[BODYQUESIZE]; +int bodyqueslot; + +int vanilla_savegame_limit = 1; +int vanilla_demo_limit = 1; + +/* G_DoCompleted */ + +boolean secretexit; + +char savename[256]; + +skill_t d_skill; +int d_episode; +int d_map; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int *weapon_keys[] = +{ + &key_weapon1, &key_weapon2, &key_weapon3, &key_weapon4, + &key_weapon5, &key_weapon6, &key_weapon7, &key_weapon8, +}; + +/* Set to -1 or +1 to switch to the previous or next weapon. */ + +static int next_weapon = 0; + +/* Used for prev/next weapon keys. */ + +static const struct weapon_order weapon_order_table[] = +{ + {wp_fist, wp_fist}, + {wp_chainsaw, wp_fist}, + {wp_pistol, wp_pistol}, + {wp_shotgun, wp_shotgun}, + {wp_supershotgun, wp_shotgun}, + {wp_chaingun, wp_chaingun}, + {wp_missile, wp_missile}, + {wp_plasma, wp_plasma}, + {wp_bfg, wp_bfg}, +}; + +static boolean gamekeydown[NUMKEYS]; +static int turnheld; /* for accelerative turning */ + +static boolean mousearray[MAX_MOUSE_BUTTONS + 1]; +static boolean *mousebuttons = &mousearray[1]; /* allow [-1] */ + +static int dclicktime; +static boolean dclickstate; +static int dclicks; +static int dclicktime2; +static boolean dclickstate2; +static int dclicks2; + +/* joystick values are repeated */ + +static int joyxmove; +static int joyymove; +static int joystrafemove; +static boolean joyarray[MAX_JOY_BUTTONS + 1]; +static boolean *joybuttons = &joyarray[1]; /* allow [-1] */ + +static int savegameslot; +static char savedescription[32]; + +/* DOOM Par Times */ + +static const 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 */ + +static const 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 */ +}; + +/* Chex Quest Par Times */ + +static const int chexpars[6] = +{ + 0, 120, 360, 480, 200, 360, +}; + +static const char *defdemoname; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static boolean weapon_selectable(weapontype_t weapon) +{ + /* Can't select the super shotgun in Doom 1. */ + + if (weapon == wp_supershotgun && logical_gamemission == doom) + { + return false; + } + + /* These weapons aren't available in shareware. */ + + if ((weapon == wp_plasma || weapon == wp_bfg) && gamemission == doom && + gamemode == shareware) + { + return false; + } + + /* Can't select a weapon if we don't own it. */ + + if (!players[consoleplayer].weaponowned[weapon]) + { + return false; + } + + /* Can't select the fist if we have the chainsaw, unless + * we also have the berserk pack. + */ + + if (weapon == wp_fist && players[consoleplayer].weaponowned[wp_chainsaw] && + !players[consoleplayer].powers[pw_strength]) + { + return false; + } + + return true; +} + +static int g_next_weapon(int direction) +{ + weapontype_t weapon; + int start_i; + int i; + + /* Find index in the table. */ + + if (players[consoleplayer].pendingweapon == wp_nochange) + { + weapon = players[consoleplayer].readyweapon; + } + else + { + weapon = players[consoleplayer].pendingweapon; + } + + for (i = 0; i < arrlen(weapon_order_table); ++i) + { + if (weapon_order_table[i].weapon == weapon) + { + break; + } + } + + /* The current weapon must be present in weapon_order_table + * otherwise something has gone terribly wrong + */ + + if (i >= arrlen(weapon_order_table)) + { + i_error("Internal error: weapon %d not present in weapon_order_table", + weapon); + } + + /* Switch weapon. Don't loop forever. */ + + start_i = i; + do + { + i += direction; + i = (i + arrlen(weapon_order_table)) % arrlen(weapon_order_table); + } + while (i != start_i && !weapon_selectable(weapon_order_table[i].weapon)); + + return weapon_order_table[i].weapon_num; +} + +static int g_cmd_checksum(ticcmd_t *cmd) +{ + size_t i; + int sum = 0; + + for (i = 0; i < sizeof(*cmd) / 4 - 1; i++) + sum += ((int *)cmd)[i]; + + return sum; +} + +static void g_do_load_level(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_flat_num_for_name((SKYFLATNAME)); + + /* The "Sky never changes in Doom II" bug was fixed in + * the id Anthology version of doom2.exe for Final Doom. + */ + + if ((gamemode == commercial) && + (gameversion == exe_final2 || gameversion == exe_chex)) + { + const char *skytexturename; + + if (gamemap < 12) + { + skytexturename = "SKY1"; + } + else if (gamemap < 21) + { + skytexturename = "SKY2"; + } + else + { + skytexturename = "SKY3"; + } + + skytexturename = (skytexturename); + + skytexture = r_texture_num_for_name(skytexturename); + } + + levelstarttic = gametic; /* for time calculation */ + + if (wipegamestate == GS_LEVEL) wipegamestate = -1; /* force a wipe */ + + gamestate = GS_LEVEL; + + for (i = 0; i < MAXPLAYERS; i++) + { + turbodetected[i] = false; + if (playeringame[i] && players[i].playerstate == PST_DEAD) + players[i].playerstate = PST_REBORN; + memset(players[i].frags, 0, sizeof(players[i].frags)); + } + + p_setup_level(gameepisode, gamemap, 0, gameskill); + displayplayer = consoleplayer; /* view the guy you are playing */ + gameaction = ga_nothing; + z_check_heap(); + + /* clear cmd building stuff */ + + memset(gamekeydown, 0, sizeof(gamekeydown)); + joyxmove = joyymove = joystrafemove = 0; + mousex = mousey = 0; + sendpause = sendsave = paused = false; + memset(mousearray, 0, sizeof(mousearray)); + memset(joyarray, 0, sizeof(joyarray)); + + if (testcontrols) + { + players[consoleplayer].message = "Press escape to quit."; + } +} + +static void set_joy_buttons(unsigned int buttons_mask) +{ + int i; + + for (i = 0; i < MAX_JOY_BUTTONS; ++i) + { + int button_on = (buttons_mask & (1 << i)) != 0; + + /* Detect button press: */ + + if (!joybuttons[i] && button_on) + { + /* Weapon cycling: */ + + if (i == joybprevweapon) + { + next_weapon = -1; + } + else if (i == joybnextweapon) + { + next_weapon = 1; + } + } + + joybuttons[i] = button_on; + } +} + +static void set_mouse_buttons(unsigned int buttons_mask) +{ + int i; + + for (i = 0; i < MAX_MOUSE_BUTTONS; ++i) + { + unsigned int button_on = (buttons_mask & (1 << i)) != 0; + + /* Detect button press: */ + + if (!mousebuttons[i] && button_on) + { + if (i == mousebprevweapon) + { + next_weapon = -1; + } + else if (i == mousebnextweapon) + { + next_weapon = 1; + } + } + + mousebuttons[i] = button_on; + } +} + +/* g_check_spot + * Returns false if the player cannot be respawned + * at the given mapthing_t spot because something is occupying it + */ + +static boolean g_check_spot(int playernum, mapthing_t *mthing) +{ + fixed_t x; + fixed_t y; + subsector_t *ss; + 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_check_position(players[playernum].mo, x, y)) return false; + + /* flush an old corpse if needed */ + + if (bodyqueslot >= BODYQUESIZE) + { + p_remove_mobj(bodyque[bodyqueslot % BODYQUESIZE]); + } + + bodyque[bodyqueslot % BODYQUESIZE] = players[playernum].mo; + bodyqueslot++; + + /* spawn a teleport fog */ + + ss = r_point_in_subsector(x, y); + + /* The code in the released source looks like this: + * + * an = ( ANG45 * (((unsigned int) mthing->angle)/45) ) + * >> ANGLETOFINESHIFT; + * mo = p_spawn_mobj (x+20*finecosine[an], y+20*finesine[an] + * , ss->sector->floorheight + * , MT_TFOG); + * + * But 'an' can be a signed value in the DOS version. This means that + * we get a negative index and the lookups into finecosine/finesine + * end up dereferencing values in finetangent[]. + * A player spawning on a deathmatch start facing directly west spawns + * "silently" with no spawn fog. Emulate this. + * + * This code is imported from PrBoom+. + */ + + fixed_t xa; + fixed_t ya; + signed int an; + + /* This calculation overflows in Vanilla Doom, but here we deliberately + * avoid integer overflow as it is undefined behavior, so the value of + * 'an' will always be positive. + */ + + an = (ANG45 >> ANGLETOFINESHIFT) * ((signed int)mthing->angle / 45); + + switch (an) + { + case 4096: /* -4096: */ + xa = finetangent[2048]; /* finecosine[-4096] */ + ya = finetangent[0]; /* finesine[-4096] */ + break; + case 5120: /* -3072: */ + xa = finetangent[3072]; /* finecosine[-3072] */ + ya = finetangent[1024]; /* finesine[-3072] */ + break; + case 6144: /* -2048: */ + xa = finesine[0]; /* finecosine[-2048] */ + ya = finetangent[2048]; /* finesine[-2048] */ + break; + case 7168: /* -1024: */ + xa = finesine[1024]; /* finecosine[-1024] */ + ya = finetangent[3072]; /* finesine[-1024] */ + break; + case 0: + case 1024: + case 2048: + case 3072: + xa = finecosine[an]; + ya = finesine[an]; + break; + case 8192: /* 360 deg: */ + xa = tantoangle[0]; /* finecosine[8192] */ + ya = finesine[8192]; /* finesine[8192] */ + break; + default: + i_error("g_check_spot: unexpected angle %d\n", an); + xa = ya = 0; + break; + } + + mo = p_spawn_mobj(x + 20 * xa, y + 20 * ya, ss->sector->floorheight, + MT_TFOG); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (players[consoleplayer].viewz != 1) + { + /* don't start sound on first frame */ + + s_start_sound(mo, SFX_TELEPT); + } +#else + UNUSED(mo); +#endif + + return true; +} + +static void g_do_reborn(int playernum) +{ + int i; + + if (!netgame) + { + /* reload the level from scratch */ + + gameaction = ga_loadlevel; + } + else + { + /* respawn at the start first disassociate the corpse */ + + players[playernum].mo->player = NULL; + + /* spawn at random spot if in death match */ + + if (deathmatch) + { + g_death_match_spawn_player(playernum); + return; + } + + if (g_check_spot(playernum, &playerstarts[playernum])) + { + p_spawn_player(&playerstarts[playernum]); + return; + } + + /* try to spawn at one of the other players spots */ + + for (i = 0; i < MAXPLAYERS; i++) + { + if (g_check_spot(playernum, &playerstarts[i])) + { + playerstarts[i].type = playernum + 1; /* fake as other player */ + p_spawn_player(&playerstarts[i]); + playerstarts[i].type = i + 1; /* restore */ + return; + } + + /* he's going to be inside something. Too bad. */ + } + + p_spawn_player(&playerstarts[playernum]); + } +} + +static void g_do_new_game(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_init_new(d_skill, d_episode, d_map); + gameaction = ga_nothing; +} + +static void g_do_save_game(void) +{ + char *savegame_file; + char *temp_savegame_file; + char *recovery_savegame_file; + + recovery_savegame_file = NULL; + temp_savegame_file = p_temp_save_game_file(); + savegame_file = p_save_game_file(savegameslot); + + /* Open the savegame file for writing. We write to a temporary file + * and then rename it at the end if it was successfully written. + * This prevents an existing savegame from being overwritten by + * a corrupted one, or if a savegame buffer overrun occurs. + */ + + save_stream = fopen(temp_savegame_file, "wb"); + + if (save_stream == NULL) + { + /* Failed to save the game, so we're going to have to abort. But + * to be nice, save to somewhere else before we call i_error(). + */ + + recovery_savegame_file = m_temp_file("recovery.dsg"); + save_stream = fopen(recovery_savegame_file, "wb"); + if (save_stream == NULL) + { + i_error("Failed to open either '%s' or '%s' to write savegame.", + temp_savegame_file, recovery_savegame_file); + } + } + + savegame_error = false; + + p_write_save_game_header(savedescription); + + p_archive_players(); + p_archive_world(); + p_archive_thinkers(); + p_archive_specials(); + + p_write_save_game_eof(); + + /* Enforce the same savegame size limit as in Vanilla Doom, + * except if the vanilla_savegame_limit setting is turned off. + */ + + if (vanilla_savegame_limit && ftell(save_stream) > SAVEGAMESIZE) + { + i_error("Savegame buffer overrun"); + } + + /* Finish up, close the savegame file. */ + + fclose(save_stream); + + if (recovery_savegame_file != NULL) + { + /* We failed to save to the normal location, but we wrote a + * recovery file to the temp directory. Now we can bomb out + * with an error. + */ + + i_error("Failed to open savegame file '%s' for writing.\n" + "But your game has been saved to '%s' for recovery.", + temp_savegame_file, recovery_savegame_file); + } + + /* Now rename the temporary savegame file to the actual savegame + * file, overwriting the old savegame if there was one there. + */ + + remove(savegame_file); + rename(temp_savegame_file, savegame_file); + + gameaction = ga_nothing; + m_str_copy(savedescription, "", sizeof(savedescription)); + + players[consoleplayer].message = (GGSAVED); + + /* draw the pattern into the back screen */ + + r_fill_back_screen(); +} + +/* Generate a string describing a demo version */ + +static const char *demo_version_description(int version) +{ + static char resultbuf[16]; + + switch (version) + { + case 104: + return "v1.4"; + case 105: + return "v1.5"; + case 106: + return "v1.6/v1.666"; + case 107: + return "v1.7/v1.7a"; + case 108: + return "v1.8"; + case 109: + return "v1.9"; + case 111: + return "v1.91 hack demo?"; + default: + break; + } + + /* Unknown version. Perhaps this is a pre-v1.4 IWAD? If the version + * byte is in the range 0-4 then it can be a v1.0-v1.2 demo. + */ + + if (version >= 0 && version <= 4) + { + return "v1.0/v1.1/v1.2"; + } + else + { + snprintf(resultbuf, sizeof(resultbuf), "%i.%i (unknown)", + version / 100, version % 100); + return resultbuf; + } +} + +static void g_do_play_demo(void) +{ + skill_t skill; + int i; + int lumpnum; + int episode; + int map; + int demoversion; + boolean olddemo = false; + + lumpnum = w_get_num_for_name(defdemoname); + gameaction = ga_nothing; + demobuffer = w_cache_lump_num(lumpnum, PU_STATIC); + demo_p = demobuffer; + + demoversion = *demo_p++; + + if (demoversion >= 0 && demoversion <= 4) + { + olddemo = true; + demo_p--; + } + + longtics = false; + + /* Longtics demos use the modified format that is generated by cph's + * hacked "v1.91" doom exe. This is a non-vanilla extension. + */ + + if (d_non_vanilla_playback(demoversion == DOOM_191_VERSION, lumpnum, + "Doom 1.91 demo format")) + { + longtics = true; + } + else if (demoversion != g_vanilla_version_code() && + !(gameversion <= exe_doom_1_2 && olddemo)) + { + const char *message = "Demo is from a different game version!\n" + "(read %i, should be %i)\n" + "\n" + "*** You may need to upgrade your version " + "of Doom to v1.9. ***\n" + " See: https://www.doomworld.com/classicdoom" + "/info/patches.php\n" + " This appears to be %s."; + + i_error(message, demoversion, g_vanilla_version_code(), + demo_version_description(demoversion)); + } + + skill = *demo_p++; + episode = *demo_p++; + map = *demo_p++; + + if (!olddemo) + { + deathmatch = *demo_p++; + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + consoleplayer = *demo_p++; + } + else + { + deathmatch = 0; + respawnparm = 0; + fastparm = 0; + nomonsters = 0; + consoleplayer = 0; + } + + for (i = 0; i < MAXPLAYERS; i++) + playeringame[i] = *demo_p++; + + if (playeringame[1] || m_check_parm("-solo-net") > 0 || + m_check_parm("-netdemo") > 0) + { + netgame = true; + netdemo = true; + } + + /* don't spend a lot of time in loadlevel */ + + precache = false; + g_init_new(skill, episode, map); + precache = true; + starttime = i_get_time(); + + usergame = false; + demoplayback = true; +} + +/* g_playerfinishlevel + * Can when a player completes a level. + */ + +static void g_player_finish_level(int player) +{ + player_t *p; + + p = &players[player]; + + memset(p->powers, 0, sizeof(p->powers)); + 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; +} + +static void g_do_completed(void) +{ + int i; + + gameaction = ga_nothing; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + g_player_finish_level(i); /* take away cards and stuff */ + } + } + + if (automapactive) am_stop(); + + if (gamemode != commercial) + { + /* Chex Quest ends after 5 levels, rather than 8. */ + + if (gameversion == exe_chex) + { + if (gamemap == 5) + { + gameaction = ga_victory; + return; + } + } + else + { + switch (gamemap) + { + case 8: + gameaction = ga_victory; + return; + case 9: + for (i = 0; i < MAXPLAYERS; i++) + players[i].didsecret = true; + break; + } + } + } + + /* #if 0 Hmmm - why? */ + + 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; + } + + /* #endif */ + + 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; + + /* Set par time. Exceptions are added for purposes of + * statcheck regression testing. + */ + + if (gamemode == commercial) + { + /* map33 reads its par time from beyond the cpars[] array */ + + if (gamemap == 33) + { + int cpars32; + + memcpy(&cpars32, (GAMMALVL0), sizeof(int)); + cpars32 = LONG(cpars32); + + wminfo.partime = TICRATE * cpars32; + } + else + { + wminfo.partime = TICRATE * cpars[gamemap - 1]; + } + } + + /* Doom episode 4 doesn't have a par time, so this + * overflows into the cpars array. + */ + + else if (gameepisode < 4) + { + if (gameversion == exe_chex && gameepisode == 1 && gamemap < 6) + { + wminfo.partime = TICRATE * chexpars[gamemap]; + } + else + { + wminfo.partime = TICRATE * pars[gameepisode][gamemap]; + } + } + else + { + wminfo.partime = TICRATE * cpars[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; + memcpy(wminfo.plyr[i].frags, players[i].frags, + sizeof(wminfo.plyr[i].frags)); + } + + gamestate = GS_INTERMISSION; + viewactive = false; + automapactive = false; + + stat_copy(&wminfo); + + wi_start(&wminfo); +} + +static void g_do_world_done(void) +{ + gamestate = GS_LEVEL; + gamemap = wminfo.next + 1; + g_do_load_level(); + gameaction = ga_nothing; + viewactive = true; +} + +/* Increase the size of the demo buffer to allow unlimited demos */ + +static void increase_demo_buffer(void) +{ + int current_length; + byte *new_demobuffer; + byte *new_demop; + int new_length; + + /* Find the current size */ + + current_length = demoend - demobuffer; + + /* Generate a new buffer twice the size */ + + new_length = current_length * 2; + + new_demobuffer = z_malloc(new_length, PU_STATIC, 0); + new_demop = new_demobuffer + (demo_p - demobuffer); + + /* Copy over the old data */ + + memcpy(new_demobuffer, demobuffer, current_length); + + /* Free the old buffer and point the demo pointers at the new buffer. */ + + z_free(demobuffer); + + demobuffer = new_demobuffer; + demo_p = new_demop; + demoend = demobuffer + new_length; +} + +/* DEMO RECORDING */ + +static void g_read_demo_ticcmd(ticcmd_t *cmd) +{ + if (*demo_p == DEMOMARKER) + { + /* end of demo data stream */ + + g_check_demo_status(); + return; + } + + cmd->forwardmove = ((signed char)*demo_p++); + cmd->sidemove = ((signed char)*demo_p++); + + /* If this is a longtics demo, read back in higher resolution */ + + if (longtics) + { + cmd->angleturn = *demo_p++; + cmd->angleturn |= (*demo_p++) << 8; + } + else + { + cmd->angleturn = ((unsigned char)*demo_p++) << 8; + } + + cmd->buttons = (unsigned char)*demo_p++; +} + +static void g_write_demo_ticcmd(ticcmd_t *cmd) +{ + byte *demo_start; + + if (gamekeydown[key_demo_quit]) /* press q to end demo recording */ + g_check_demo_status(); + + demo_start = demo_p; + + *demo_p++ = cmd->forwardmove; + *demo_p++ = cmd->sidemove; + + /* If this is a longtics demo, record in higher resolution */ + + if (longtics) + { + *demo_p++ = (cmd->angleturn & 0xff); + *demo_p++ = (cmd->angleturn >> 8) & 0xff; + } + else + { + *demo_p++ = cmd->angleturn >> 8; + } + + *demo_p++ = cmd->buttons; + + /* reset demo pointer back */ + + demo_p = demo_start; + + if (demo_p > demoend - 16) + { + if (vanilla_demo_limit) + { + /* no more space */ + + g_check_demo_status(); + return; + } + else + { + /* Vanilla demo limit disabled: unlimited + * demo lengths! + */ + + increase_demo_buffer(); + } + } + + g_read_demo_ticcmd(cmd); /* make SURE it is exactly the same */ +} + +/* Called at the start. + * Called by the game initialization functions. + */ + +static void g_init_player(int player) +{ + /* clear everything else to defaults */ + + g_player_reborn(player); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* g_build_ticcmd + * 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_build_ticcmd(ticcmd_t *cmd, int maketic) +{ + int i; + boolean strafe; + boolean bstrafe; + int speed; + int tspeed; + int forward; + int side; + + memset(cmd, 0, sizeof(ticcmd_t)); + + cmd->consistency = + consistency[consoleplayer] + [maketic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] || + joybuttons[joybstrafe]; + + /* fraggle: support the old "joyb_speed = 31" hack which + * allowed an autorun effect + */ + + speed = key_speed >= NUMKEYS || joybspeed >= MAX_JOY_BUTTONS || + gamekeydown[key_speed] || joybuttons[joybspeed] || + mousebuttons[mousebspeed]; + + forward = side = 0; + + /* use two stage accelerative turning + * on the keyboard and joystick + */ + + if (joyxmove < 0 || joyxmove > 0 || gamekeydown[key_right] || + gamekeydown[key_left] || mousebuttons[mousebturnright] || + mousebuttons[mousebturnleft]) + { + 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] || mousebuttons[mousebturnright]) + { + side += sidemove[speed]; + } + + if (gamekeydown[key_left] || mousebuttons[mousebturnleft]) + { + side -= sidemove[speed]; + } + + if (use_analog && joyxmove) + { + joyxmove = joyxmove * joystick_move_sensitivity / 10; + joyxmove = (joyxmove > FRACUNIT) ? FRACUNIT : joyxmove; + joyxmove = (joyxmove < -FRACUNIT) ? -FRACUNIT : joyxmove; + side += fixed_mul(sidemove[speed], joyxmove); + } + else if (joystick_move_sensitivity) + { + if (joyxmove > 0) side += sidemove[speed]; + if (joyxmove < 0) side -= sidemove[speed]; + } + } + else + { + if (gamekeydown[key_right] || mousebuttons[mousebturnright]) + cmd->angleturn -= angleturn[tspeed]; + if (gamekeydown[key_left] || mousebuttons[mousebturnleft]) + cmd->angleturn += angleturn[tspeed]; + if (use_analog && joyxmove) + { + /* Cubic response curve allows for finer control when stick + * deflection is small. + */ + + joyxmove = fixed_mul(fixed_mul(joyxmove, joyxmove), joyxmove); + joyxmove = joyxmove * joystick_turn_sensitivity / 10; + cmd->angleturn -= fixed_mul(angleturn[1], joyxmove); + } + else if (joystick_turn_sensitivity) + { + 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 (use_analog && joyymove) + { + joyymove = joyymove * joystick_move_sensitivity / 10; + joyymove = (joyymove > FRACUNIT) ? FRACUNIT : joyymove; + joyymove = (joyymove < -FRACUNIT) ? -FRACUNIT : joyymove; + forward -= fixed_mul(forwardmove[speed], joyymove); + } + else if (joystick_move_sensitivity) + { + if (joyymove < 0) forward += forwardmove[speed]; + if (joyymove > 0) forward -= forwardmove[speed]; + } + + if (gamekeydown[key_strafeleft] || joybuttons[joybstrafeleft] || + mousebuttons[mousebstrafeleft]) + { + side -= sidemove[speed]; + } + + if (gamekeydown[key_straferight] || joybuttons[joybstraferight] || + mousebuttons[mousebstraferight]) + { + side += sidemove[speed]; + } + + if (use_analog && joystrafemove) + { + joystrafemove = joystrafemove * joystick_move_sensitivity / 10; + joystrafemove = (joystrafemove > FRACUNIT) ? FRACUNIT : joystrafemove; + joystrafemove = + (joystrafemove < -FRACUNIT) ? -FRACUNIT : joystrafemove; + side += fixed_mul(sidemove[speed], joystrafemove); + } + else if (joystick_move_sensitivity) + { + if (joystrafemove < 0) side -= sidemove[speed]; + if (joystrafemove > 0) side += sidemove[speed]; + } + + /* buttons */ + + cmd->chatchar = hu_dequeue_chat_char(); + + if (gamekeydown[key_fire] || mousebuttons[mousebfire] || + joybuttons[joybfire]) + cmd->buttons |= BT_ATTACK; + + if (gamekeydown[key_use] || joybuttons[joybuse] || mousebuttons[mousebuse]) + { + cmd->buttons |= BT_USE; + dclicks = 0; /* clear double clicks if hit use button */ + } + + /* If the previous or next weapon button is pressed, the + * next_weapon variable is set to change weapons when + * we generate a ticcmd. Choose a new weapon. + */ + + if (gamestate == GS_LEVEL && next_weapon != 0) + { + i = g_next_weapon(next_weapon); + cmd->buttons |= BT_CHANGE; + cmd->buttons |= i << BT_WEAPONSHIFT; + } + else + { + /* Check weapon keys. */ + + for (i = 0; i < arrlen(weapon_keys); ++i) + { + int key = *weapon_keys[i]; + + if (gamekeydown[key]) + { + cmd->buttons |= BT_CHANGE; + cmd->buttons |= i << BT_WEAPONSHIFT; + break; + } + } + } + + next_weapon = 0; + + /* mouse */ + + if (mousebuttons[mousebforward]) + { + forward += forwardmove[speed]; + } + + if (mousebuttons[mousebbackward]) + { + forward -= forwardmove[speed]; + } + + if (dclick_use) + { + /* 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; + } + } + } + + forward += mousey; + + if (strafe) + side += mousex * 2; + else + cmd->angleturn -= mousex * 0x8; + + if (mousex == 0) + { + /* No movement in the previous frame */ + + testcontrols_mousespeed = 0; + } + + 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); + } + + /* low-res turning */ + + if (lowres_turn) + { + static signed short carry = 0; + signed short desired_angleturn; + + desired_angleturn = cmd->angleturn + carry; + + /* round angleturn to the nearest 256 unit boundary + * for recording demos with single byte values for turn + */ + + cmd->angleturn = (desired_angleturn + 128) & 0xff00; + + /* Carry forward the error from the reduced resolution to the + * next tic, so that successive small movements can accumulate. + */ + + carry = desired_angleturn - cmd->angleturn; + } +} + +/* g_responder + * Get info needed to make ticcmd_ts for the players. + */ + +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_spy && (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_start_control_panel(); + joywait = i_get_time() + 5; + return true; + } + + return false; + } + + if (gamestate == GS_LEVEL) + { +#if 0 + if (devparm && ev->type == ev_keydown && ev->data1 == ';') + { + g_death_match_spawn_player(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 */ + } + + if (testcontrols && ev->type == ev_mouse) + { + /* If we are invoked by setup to test the controls, save the + * mouse speed so that we can display it on-screen. + * Perform a low pass filter on this so that the thermometer + * appears to move smoothly. + */ + + testcontrols_mousespeed = abs(ev->data2); + } + + /* If the next/previous weapon keys are pressed, set the next_weapon + * variable to change weapons when the next ticcmd is generated. + */ + + if (ev->type == ev_keydown && ev->data1 == key_prevweapon) + { + next_weapon = -1; + } + else if (ev->type == ev_keydown && ev->data1 == key_nextweapon) + { + next_weapon = 1; + } + + switch (ev->type) + { + case ev_keydown: + if (ev->data1 == key_pause) + { + sendpause = true; + } + else 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: + set_mouse_buttons(ev->data1); + mousex = ev->data2 * (g_mouse_sensitivity + 5) / 10; + mousey = ev->data3 * (g_mouse_sensitivity + 5) / 10; + return true; /* eat events */ + + case ev_joystick: + set_joy_buttons(ev->data1); + joyxmove = ev->data2; + joyymove = ev->data3; + joystrafemove = ev->data4; + 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_do_reborn(i); + } + } + + /* do things to change the game state */ + + while (gameaction != ga_nothing) + { + switch (gameaction) + { + case ga_loadlevel: + g_do_load_level(); + break; + case ga_newgame: + g_do_new_game(); + break; + case ga_loadgame: + g_do_load_game(); + break; + case ga_savegame: + g_do_save_game(); + break; + case ga_playdemo: + g_do_play_demo(); + break; + case ga_completed: + g_do_completed(); + break; + case ga_victory: + f_start_finale(); + break; + case ga_worlddone: + g_do_world_done(); + break; + case ga_screenshot: + v_screenshot("DOOM%02i.%s"); + players[consoleplayer].message = ("screen shot"); + gameaction = ga_nothing; + break; + case ga_nothing: + break; + } + } + + /* get commands, check consistency, and build new consistency check */ + + buf = (gametic / ticdup) % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + cmd = &players[i].cmd; + + memcpy(cmd, &netcmds[i], sizeof(ticcmd_t)); + + if (demoplayback) g_read_demo_ticcmd(cmd); + if (demorecording) g_write_demo_ticcmd(cmd); + + /* check for turbo cheats + * check ~ 4 seconds whether to display the turbo message. + * store if the turbo threshold was exceeded in any tics + * over the past 4 seconds. offset the checking period + * for each player so messages are not displayed at the + * same time. + */ + + if (cmd->forwardmove > TURBOTHRESHOLD) + { + turbodetected[i] = true; + } + + if ((gametic & 31) == 0 && ((gametic >> 5) % MAXPLAYERS) == i && + turbodetected[i]) + { + static char turbomessage[80]; + snprintf(turbomessage, sizeof(turbomessage), "%s is turbo!", + player_names[i]); + players[consoleplayer].message = turbomessage; + turbodetected[i] = false; + } + + if (netgame && !netdemo && !(gametic % ticdup)) + { + if (gametic > CONFIG_GAMES_NXDOOM_NET_BACKUPTICS && + consistency[i][buf] != cmd->consistency) + { + i_error("consistency failure (%i should be %i)", + cmd->consistency, consistency[i][buf]); + } + + if (players[i].mo) + consistency[i][buf] = players[i].mo->x; + else + consistency[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; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (paused) + s_pause_sound(); + else + s_resume_sound(); + break; +#endif + + case BTS_SAVEGAME: + if (!savedescription[0]) + { + m_str_copy(savedescription, "NET GAME", + sizeof(savedescription)); + } + + savegameslot = (players[i].cmd.buttons & BTS_SAVEMASK) >> + BTS_SAVESHIFT; + gameaction = ga_savegame; + break; + } + } + } + } + + /* Have we just finished displaying an intermission screen? */ + + if (oldgamestate == GS_INTERMISSION && gamestate != GS_INTERMISSION) + { + wi_end(); + } + + oldgamestate = gamestate; + + /* 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_page_ticker(); + break; + } +} + +/* PLAYER STRUCTURE FUNCTIONS + * also see p_spawn_player in P_Things + */ + +/* g_player_reborn + * Called after a player dies almost everything is cleared and initialized + */ + +void g_player_reborn(int player) +{ + player_t *p; + int i; + int frags[MAXPLAYERS]; + int killcount; + int itemcount; + int secretcount; + + memcpy(frags, players[player].frags, sizeof(frags)); + killcount = players[player].killcount; + itemcount = players[player].itemcount; + secretcount = players[player].secretcount; + + p = &players[player]; + memset(p, 0, sizeof(*p)); + + 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 = deh_initial_health; /* Use dehacked value */ + p->readyweapon = p->pendingweapon = wp_pistol; + p->weaponowned[wp_fist] = true; + p->weaponowned[wp_pistol] = true; + p->ammo[am_clip] = deh_initial_bullets; + + for (i = 0; i < NUMAMMO; i++) + p->maxammo[i] = maxammo[i]; +} + +/* g_death_match_spawn_player + * Spawns a player at one of the random death match spots + * called at level load and each death + */ + +void g_death_match_spawn_player(int playernum) +{ + int i; + int j; + int selections; + + selections = deathmatch_p - deathmatchstarts; + if (selections < 4) + i_error("Only %i deathmatch spots, 4 required", selections); + + for (j = 0; j < 20; j++) + { + i = p_random() % selections; + if (g_check_spot(playernum, &deathmatchstarts[i])) + { + deathmatchstarts[i].type = playernum + 1; + p_spawn_player(&deathmatchstarts[i]); + return; + } + } + + /* no good spot, so the player will probably get stuck */ + + p_spawn_player(&playerstarts[playernum]); +} + +void g_screenshot(void) +{ + gameaction = ga_screenshot; +} + +void g_exit_level(void) +{ + secretexit = false; + gameaction = ga_completed; +} + +/* Here's for the german edition. */ + +void g_secret_exit_level(void) +{ + /* IF NO WOLF3D LEVELS, NO SECRET EXIT! */ + + if ((gamemode == commercial) && (w_check_num_for_name("map31") < 0)) + secretexit = false; + else + secretexit = true; + gameaction = ga_completed; +} + +void g_world_done(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_start_finale(); + break; + } + } +} + +/* g_initfromsavegame + * Can be called by the startup code or the menu task. + */ + +void g_load_game(char *name) +{ + m_str_copy(savename, name, sizeof(savename)); + gameaction = ga_loadgame; +} + +void g_do_load_game(void) +{ + int savedleveltime; + + gameaction = ga_nothing; + + save_stream = fopen(savename, "rb"); + + if (save_stream == NULL) + { + i_error("Could not load savegame %s", savename); + } + + savegame_error = false; + + if (!p_read_save_game_header()) + { + fclose(save_stream); + return; + } + + savedleveltime = leveltime; + + /* load a base level */ + + g_init_new(gameskill, gameepisode, gamemap); + + leveltime = savedleveltime; + + /* dearchive all the modifications */ + + p_unarchive_players(); + p_unarchive_world(); + p_unarchive_thinkers(); + p_unarchive_specials(); + + if (!p_read_save_game_eof()) i_error("Bad savegame"); + + fclose(save_stream); + + if (setsizeneeded) r_execute_set_view_size(); + + /* draw the pattern into the back screen */ + + r_fill_back_screen(); +} + +/* g_save_game + * Called by the menu task. + * Description is a 24 byte text string + */ + +void g_save_game(int slot, char *description) +{ + savegameslot = slot; + m_str_copy(savedescription, description, sizeof(savedescription)); + sendsave = true; +} + +/* g_init_new + * Can be called by the startup code or the menu task, + * consoleplayer, displayplayer, playeringame[] should be set. + */ + +void g_deferred_init_new(skill_t skill, int episode, int map) +{ + d_skill = skill; + d_episode = episode; + d_map = map; + gameaction = ga_newgame; +} + +void g_init_new(skill_t skill, int episode, int map) +{ + const char *skytexturename; + int i; + + if (paused) + { + paused = false; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_resume_sound(); +#endif + } + + /* Note: This commented-out block of code was added at some point + * between the DOS version(s) and the Doom source release. It isn't + * found in disassemblies of the DOS version and causes IDCLEV and + * the -warp command line parameter to behave differently. + * This is left here for posterity. + * 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 (skill > sk_nightmare) skill = sk_nightmare; + + if (gameversion >= exe_ultimate) + { + if (episode == 0) + { + episode = 4; + } + } + else + { + if (episode < 1) + { + episode = 1; + } + + if (episode > 3) + { + episode = 3; + } + } + + if (episode > 1 && gamemode == shareware) + { + episode = 1; + } + + if (map < 1) map = 1; + + if ((map > 9) && (gamemode != commercial)) map = 9; + + m_clear_random(); + + 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; + + /* Set the sky to use. + * + * Note: This IS broken, but it is how Vanilla Doom behaves. + * See http: *doomwiki.org/wiki/Sky_never_changes_in_Doom_II. + * + * Because we set the sky here at the start of a game, not at the + * start of a level, the sky texture never changes unless we + * restore from a saved game. This was fixed before the Doom + * source release, but this IS the way Vanilla DOS Doom behaves. + */ + + if (gamemode == commercial) + { + skytexturename = ("SKY3"); + skytexture = r_texture_num_for_name(skytexturename); + if (gamemap < 21) + { + skytexturename = (gamemap < 12 ? "SKY1" : "SKY2"); + skytexture = r_texture_num_for_name(skytexturename); + } + } + else + { + switch (gameepisode) + { + default: + case 1: + skytexturename = "SKY1"; + break; + case 2: + skytexturename = "SKY2"; + break; + case 3: + skytexturename = "SKY3"; + break; + case 4: /* Special Edition sky */ + skytexturename = "SKY4"; + break; + } + + skytexturename = (skytexturename); + skytexture = r_texture_num_for_name(skytexturename); + } + + g_do_load_level(); +} + +void g_record_demo(const char *name) +{ + size_t demoname_size; + int i; + int maxsize; + + usergame = false; + demoname_size = strlen(name) + 5; + demoname = z_malloc(demoname_size, PU_STATIC, NULL); + snprintf(demoname, demoname_size, "%s.lmp", name); + maxsize = 0x20000; + + /* @arg + * @category demo + * @vanilla + * + * Specify the demo buffer size (KiB) + */ + + i = m_check_parm_with_args("-maxdemo", 1); + if (i) maxsize = atoi(myargv[i + 1]) * 1024; + demobuffer = z_malloc(maxsize, PU_STATIC, NULL); + demoend = demobuffer + maxsize; + + demorecording = true; +} + +/* Get the demo version code appropriate for the version set in gameversion. + */ + +int g_vanilla_version_code(void) +{ + switch (gameversion) + { + case exe_doom_1_666: + return 106; + case exe_doom_1_7: + return 107; + case exe_doom_1_8: + return 108; + case exe_doom_1_9: + default: /* All other versions are variants on v1.9: */ + return 109; + } +} + +void g_begin_recording(void) +{ + int i; + + demo_p = demobuffer; + + /* @category demo + * + * Record a high resolution "Doom 1.91" demo. + */ + + longtics = d_non_vanilla_record(m_parm_exists("-longtics"), + "Doom 1.91 demo format"); + + /* If not recording a longtics demo, record in low res */ + + lowres_turn = !longtics; + + if (longtics) + { + *demo_p++ = DOOM_191_VERSION; + } + else if (gameversion > exe_doom_1_2) + { + *demo_p++ = g_vanilla_version_code(); + } + + *demo_p++ = gameskill; + *demo_p++ = gameepisode; + *demo_p++ = gamemap; + if (longtics || gameversion > exe_doom_1_2) + { + *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]; +} + +void g_defered_play_demo(const char *name) +{ + defdemoname = name; + gameaction = ga_playdemo; +} + +void g_time_demo(char *name) +{ + /* @category video + * @vanilla + * + * Disable rendering the screen entirely. + */ + + nodrawers = m_check_parm("-nodraw"); + + timingdemo = true; + singletics = true; + + defdemoname = name; + gameaction = ga_playdemo; +} + +/* g_check_demo_status + * + * 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 + */ + +boolean g_check_demo_status(void) +{ + int endtime; + + if (timingdemo) + { + float fps; + int realtics; + + endtime = i_get_time(); + realtics = endtime - starttime; + fps = ((float)gametic * TICRATE) / realtics; + + /* Prevent recursive calls */ + + timingdemo = false; + demoplayback = false; + + i_error("timed %i gametics in %i realtics (%f fps)", gametic, realtics, + fps); + } + + if (demoplayback) + { + w_release_lump_name(defdemoname); + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = 0; + respawnparm = false; + fastparm = false; + nomonsters = false; + consoleplayer = 0; + + if (singledemo) + i_quit(); + else + d_advance_demo(); + + return true; + } + + if (demorecording) + { + *demo_p++ = DEMOMARKER; + m_write_file(demoname, demobuffer, demo_p - demobuffer); + z_free(demobuffer); + demorecording = false; + i_error("Demo %s recorded", demoname); + } + + return false; +} diff --git a/games/NXDoom/src/doom/g_game.h b/games/NXDoom/src/doom/g_game.h new file mode 100644 index 00000000000..80c3fe2ee2e --- /dev/null +++ b/games/NXDoom/src/doom/g_game.h @@ -0,0 +1,105 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/g_game.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Duh. + * + ****************************************************************************/ + +#ifndef __G_GAME__ +#define __G_GAME__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "d_ticcmd.h" +#include "doomdef.h" +#include "m_fixed.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int vanilla_savegame_limit; +extern int vanilla_demo_limit; + +extern fixed_t forwardmove[2]; +extern fixed_t sidemove[2]; + +extern boolean sendpause; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* GAME */ + +void g_death_match_spawn_player(int playernum); + +void g_init_new(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_deferred_init_new(skill_t skill, int episode, int map); + +void g_defered_play_demo(const char *demo); + +/* Can be called by the startup code or m_responder, + * calls p_setup_level or W_EnterWorld. + */ + +void g_load_game(char *name); + +void g_do_load_game(void); + +/* Called by m_responder. */ + +void g_save_game(int slot, char *description); + +/* Only called by startup code. */ + +void g_record_demo(const char *name); + +void g_begin_recording(void); + +void g_play_demo(char *name); +void g_time_demo(char *name); +boolean g_check_demo_status(void); + +void g_exit_level(void); +void g_secret_exit_level(void); + +void g_world_done(void); + +/* Read current data from inputs and build a player movement command. */ + +void g_build_ticcmd(ticcmd_t *cmd, int maketic); + +void g_ticker(void); +boolean g_responder(event_t *ev); + +void g_screenshot(void); + +int g_vanilla_version_code(void); + +#endif /* __G_GAME__ */ diff --git a/games/NXDoom/src/doom/hu_lib.c b/games/NXDoom/src/doom/hu_lib.c new file mode 100644 index 00000000000..cfa9623315c --- /dev/null +++ b/games/NXDoom/src/doom/hu_lib.c @@ -0,0 +1,318 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/hu_lib.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: heads-up text and input code + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomdef.h" +#include "doomkeys.h" +#include "doomstat.h" + +#include "i_swap.h" +#include "v_video.h" + +#include "hu_lib.h" +#include "r_draw.h" +#include "r_local.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* boolean : whether the screen is always erased */ + +#define noterased viewwindowx + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void hu_lib_clear_text(hu_textline_t *t) +{ + t->len = 0; + t->l[0] = 0; + t->needsupdate = true; +} + +void hu_lib_init_text_line(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; + hu_lib_clear_text(t); +} + +boolean hu_lib_add_char_to_text_line(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; + } +} + +boolean hu_lib_del_char_from_text_line(hu_textline_t *t) +{ + if (!t->len) + { + return false; + } + else + { + t->l[--t->len] = 0; + t->needsupdate = 4; + return true; + } +} + +void hu_lib_draw_text_line(hu_textline_t *l, 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 = toupper(l->l[i]); + if (c != ' ' && c >= l->sc && c <= '_') + { + w = SHORT(l->f[c - l->sc]->width); + if (x + w > SCREENWIDTH) break; + v_draw_patch_direct(x, l->y, 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_draw_patch_direct(x, l->y, l->f['_' - l->sc]); + } +} + +/* sorta called by hu_erase and just better darn get things straight */ + +void hu_lib_erase_text_line(hu_textline_t *l) +{ + int lh; + int y; + int yoffset; + + /* 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_video_erase(yoffset, SCREENWIDTH); /* erase entire line */ + else + { + r_video_erase(yoffset, viewwindowx); /* erase left border */ + r_video_erase(yoffset + viewwindowx + viewwidth, viewwindowx); + + /* erase right border */ + } + } + } + + if (l->needsupdate) l->needsupdate--; +} + +void hu_lib_init_stext(hu_stext_t *s, int x, int y, int h, patch_t **font, + int startchar, boolean *on) +{ + int i; + + s->h = h; + s->on = on; + s->laston = true; + s->cl = 0; + for (i = 0; i < h; i++) + hu_lib_init_text_line(&s->l[i], x, y - i * (SHORT(font[0]->height) + 1), + font, startchar); +} + +void hu_lib_add_line_to_stext(hu_stext_t *s) +{ + int i; + + /* add a clear line */ + + if (++s->cl == s->h) s->cl = 0; + hu_lib_clear_text(&s->l[s->cl]); + + /* everything needs updating */ + + for (i = 0; i < s->h; i++) + { + s->l[i].needsupdate = 4; + } +} + +void hu_lib_add_messsage_to_stext(hu_stext_t *s, const char *prefix, + const char *msg) +{ + hu_lib_add_line_to_stext(s); + + if (prefix) + { + while (*prefix) + { + hu_lib_add_char_to_text_line(&s->l[s->cl], *(prefix++)); + } + } + + while (*msg) + { + hu_lib_add_char_to_text_line(&s->l[s->cl], *(msg++)); + } +} + +void hu_lib_draw_stext(hu_stext_t *s) +{ + int i; + int 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 */ + + hu_lib_draw_text_line(l, false); /* no cursor, please */ + } +} + +void hu_lib_erase_stext(hu_stext_t *s) +{ + int i; + + for (i = 0; i < s->h; i++) + { + if (s->laston && !*s->on) s->l[i].needsupdate = 4; + hu_lib_erase_text_line(&s->l[i]); + } + + s->laston = *s->on; +} + +void hu_lib_init_itext(hu_itext_t *it, int x, int y, patch_t **font, + int startchar, boolean *on) +{ + it->lm = 0; /* default left margin is start of text */ + it->on = on; + it->laston = true; + hu_lib_init_text_line(&it->l, x, y, font, startchar); +} + +/* The following deletion routines adhere to the left margin restriction */ + +void hu_lib_del_char_from_itext(hu_itext_t *it) +{ + if (it->l.len != it->lm) hu_lib_del_char_from_text_line(&it->l); +} + +/* Resets left margin as well */ + +void hu_lib_reset_itext(hu_itext_t *it) +{ + it->lm = 0; + hu_lib_clear_text(&it->l); +} + +/* wrapper function for handling general keyed input. + * returns true if it ate the key + */ + +boolean hu_lib_key_in_itext(hu_itext_t *it, unsigned char ch) +{ + ch = toupper(ch); + + if (ch >= ' ' && ch <= '_') + { + hu_lib_add_char_to_text_line(&it->l, (char)ch); + } + else if (ch == KEY_BACKSPACE) + { + hu_lib_del_char_from_itext(it); + } + else if (ch != KEY_ENTER) + { + return false; /* did not eat key */ + } + + return true; /* ate the key */ +} + +void hu_lib_draw_itext(hu_itext_t *it) +{ + hu_textline_t *l = &it->l; + + if (!*it->on) return; + hu_lib_draw_text_line(l, true); /* draw the line w/ cursor */ +} + +void hu_lib_erase_itext(hu_itext_t *it) +{ + if (it->laston && !*it->on) it->l.needsupdate = 4; + hu_lib_erase_text_line(&it->l); + it->laston = *it->on; +} diff --git a/games/NXDoom/src/doom/hu_lib.h b/games/NXDoom/src/doom/hu_lib.h new file mode 100644 index 00000000000..e9ab12a2427 --- /dev/null +++ b/games/NXDoom/src/doom/hu_lib.h @@ -0,0 +1,178 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/hu_lib.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: none + * + ****************************************************************************/ + +#ifndef __HULIB__ +#define __HULIB__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* We are referring to patches. */ + +#include "r_defs.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* font stuff */ + +#define HU_CHARERASE KEY_BACKSPACE + +#define HU_MAXLINES 4 +#define HU_MAXLINELENGTH 80 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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 updated */ + + 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 boolean stating whether to update window */ + + boolean *on; + 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 boolean stating whether to update window */ + + boolean *on; + boolean laston; /* last value of *->on; */ +} hu_itext_t; + +/* Widget creation, access, and update routines */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* textline code */ + +/* clear a line of text */ + +void hu_lib_clear_text(hu_textline_t *t); + +void hu_lib_init_text_line(hu_textline_t *t, int x, int y, patch_t **f, + int sc); + +/* returns success */ + +boolean hu_lib_add_char_to_text_line(hu_textline_t *t, char ch); + +/* returns success */ + +boolean hu_lib_del_char_from_text_line(hu_textline_t *t); + +/* draws tline */ + +void hu_lib_draw_text_line(hu_textline_t *l, boolean drawcursor); + +/* erases text line */ + +void hu_lib_erase_text_line(hu_textline_t *l); + +/* Scrolling Text window widget routines */ + +void hu_lib_init_stext(hu_stext_t *s, int x, int y, int h, patch_t **font, + int startchar, boolean *on); + +/* add a new line */ + +void hu_lib_add_line_to_stext(hu_stext_t *s); + +/* ? */ + +void hu_lib_add_messsage_to_stext(hu_stext_t *s, const char *prefix, + const char *msg); + +/* draws stext */ + +void hu_lib_draw_stext(hu_stext_t *s); + +/* erases all stext lines */ + +void hu_lib_erase_stext(hu_stext_t *s); + +/* Input Text Line widget routines */ + +void hu_lib_init_itext(hu_itext_t *it, int x, int y, patch_t **font, + int startchar, boolean *on); + +/* enforces left margin */ + +void hu_lib_del_char_from_itext(hu_itext_t *it); + +/* resets line and left margin */ + +void hu_lib_reset_itext(hu_itext_t *it); + +/* whether eaten */ + +boolean hu_lib_key_in_itext(hu_itext_t *it, unsigned char ch); + +void hu_lib_draw_itext(hu_itext_t *it); + +/* erases all itext lines */ + +void hu_lib_erase_itext(hu_itext_t *it); + +#endif /* __HULIB__ */ diff --git a/games/NXDoom/src/doom/hu_stuff.c b/games/NXDoom/src/doom/hu_stuff.c new file mode 100644 index 00000000000..9dfd61abc38 --- /dev/null +++ b/games/NXDoom/src/doom/hu_stuff.c @@ -0,0 +1,641 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/hu_stuff.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: Heads-up displays + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomdef.h" +#include "doomkeys.h" + +#include "z_zone.h" + +#include "deh_main.h" +#include "i_input.h" +#include "i_swap.h" +#include "i_video.h" + +#include "hu_lib.h" +#include "hu_stuff.h" +#include "m_controls.h" +#include "m_menu.h" +#include "m_misc.h" +#include "w_wad.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "doomstat.h" + +/* Data. */ + +#include "dstrings.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Locally used constants, shortcuts. */ + +#define HU_TITLE (mapnames[(gameepisode - 1) * 9 + gamemap - 1]) +#define HU_TITLE2 (mapnames_commercial[gamemap - 1]) +#define HU_TITLEP (mapnames_commercial[gamemap - 1 + 32]) +#define HU_TITLET (mapnames_commercial[gamemap - 1 + 64]) +#define HU_TITLE_CHEX (mapnames_chex[(gameepisode - 1) * 9 + 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 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static player_t *plr; +static hu_textline_t w_title; +static hu_itext_t w_chat; +static boolean always_off = false; +static char chat_dest[MAXPLAYERS]; +static hu_itext_t w_inputbuffer[MAXPLAYERS]; + +static boolean message_on; +static boolean message_nottobefuckedwith; + +static hu_stext_t w_message; +static int message_counter; + +static boolean headsupactive = false; + +static char chatchars[QUEUESIZE]; +static int head = 0; +static int tail = 0; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +char *chat_macros[10]; + +const char *player_names[] = +{ + HUSTR_PLRGREEN, + HUSTR_PLRINDIGO, + HUSTR_PLRBROWN, + HUSTR_PLRRED, +}; + +char chat_char; /* remove later. */ + +patch_t *hu_font[HU_FONTSIZE]; +boolean chat_on; +boolean message_dontfuckwithme; + +/* Builtin map names. + * The actual names can be found in DStrings.h. + */ + +/* DOOM shareware/registered/retail (Ultimate) names. */ + +const char *mapnames[] = +{ + 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", +}; + +/* Chex Quest names. */ + +const char *mapnames_chex[] = +{ + HUSTR_E1M1, HUSTR_E1M2, HUSTR_E1M3, HUSTR_E1M4, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", + "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", +}; + +/* List of names for levels in commercial IWADs + * (doom2.wad, plutonia.wad, tnt.wad). These are stored in a + * single large array; WADs like pl2.wad have a MAP33, and rely on + * the layout in the Vanilla executable, where it is possible to + * overflow the end of one array into the next. + */ + +const char *mapnames_commercial[] = +{ + HUSTR_1, /* DOOM 2 map names. */ + 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, + PHUSTR_1, /* Plutonia WAD map names. */ + 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, + THUSTR_1, /* TNT WAD map names. */ + 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, + + /* Emulation: TNT maps 33-35 can be warped to and played if they exist + * so include blank names instead of spilling over + */ + + "", + "", + "", +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void hu_stop(void) +{ + headsupactive = false; +} + +static void hu_queue_chat_char(char c) +{ + if (((head + 1) & (QUEUESIZE - 1)) == tail) + { + plr->message = (HUSTR_MSGU); + } + else + { + chatchars[head] = c; + head = (head + 1) & (QUEUESIZE - 1); + } +} + +static void start_chat_input(int dest) +{ + chat_on = true; + hu_lib_reset_itext(&w_chat); + hu_queue_chat_char(HU_BROADCAST); + + i_start_text_input(0, 8, SCREENWIDTH, 16); +} + +static void stop_chat_input(void) +{ + chat_on = false; + i_stop_text_input(); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void hu_init(void) +{ + uint8_t i; + uint8_t j; + char buffer[9]; + + /* load the heads-up font */ + + j = HU_FONTSTART; + for (i = 0; i < HU_FONTSIZE; i++) + { + snprintf(buffer, 9, "STCFN%.3u", j++); + hu_font[i] = (patch_t *)w_cache_lump_name(buffer, PU_STATIC); + } +} + +void hu_start(void) +{ + int i; + const 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 */ + + hu_lib_init_stext(&w_message, HU_MSGX, HU_MSGY, HU_MSGHEIGHT, hu_font, + HU_FONTSTART, &message_on); + + /* create the map title widget */ + + hu_lib_init_text_line(&w_title, HU_TITLEX, HU_TITLEY, hu_font, + HU_FONTSTART); + + switch (logical_gamemission) + { + case doom: + s = HU_TITLE; + break; + case doom2: + s = HU_TITLE2; + + /* Pre-Final Doom compatibility: map33-map35 names don't spill over */ + + if (gameversion <= exe_doom_1_9 && gamemap >= 33) + { + s = ""; + } + + break; + case pack_plut: + s = HU_TITLEP; + break; + case pack_tnt: + s = HU_TITLET; + break; + default: + s = "Unknown level"; + break; + } + + if (logical_gamemission == doom && gameversion == exe_chex) + { + s = HU_TITLE_CHEX; + } + + /* dehacked substitution to get modified level name */ + + s = (s); + while (*s) + { + hu_lib_add_char_to_text_line(&w_title, *(s++)); + } + + /* create the chat widget */ + + hu_lib_init_itext(&w_chat, HU_INPUTX, HU_INPUTY, hu_font, HU_FONTSTART, + &chat_on); + + /* create the inputbuffer widgets */ + + for (i = 0; i < MAXPLAYERS; i++) + { + hu_lib_init_itext(&w_inputbuffer[i], 0, 0, 0, 0, &always_off); + } + + headsupactive = true; +} + +void hu_drawer(void) +{ + hu_lib_draw_stext(&w_message); + hu_lib_draw_itext(&w_chat); + if (automapactive) hu_lib_draw_text_line(&w_title, false); +} + +void hu_erase(void) +{ + hu_lib_erase_stext(&w_message); + hu_lib_erase_itext(&w_chat); + hu_lib_erase_text_line(&w_title); +} + +void hu_ticker(void) +{ + int i; + int rc; + char c; + + /* tick down message counter if message is up */ + + if (message_counter && !--message_counter) + { + message_on = false; + message_nottobefuckedwith = false; + } + + if (g_show_messages || message_dontfuckwithme) + { + /* display message if necessary */ + + if ((plr->message && !message_nottobefuckedwith) || + (plr->message && message_dontfuckwithme)) + { + hu_lib_add_messsage_to_stext(&w_message, 0, plr->message); + plr->message = 0; + message_on = true; + message_counter = HU_MSGTIMEOUT; + message_nottobefuckedwith = message_dontfuckwithme; + message_dontfuckwithme = 0; + } + } + + /* 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 + { + rc = hu_lib_key_in_itext(&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)) + { + hu_lib_add_messsage_to_stext(&w_message, + (player_names[i]), + w_inputbuffer[i].l.l); + + message_nottobefuckedwith = true; + message_on = true; + message_counter = HU_MSGTIMEOUT; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gamemode == commercial) + s_start_sound(0, SFX_RADIO); + else if (gameversion > exe_doom_1_2) + s_start_sound(0, SFX_TINK); +#endif + } + + hu_lib_reset_itext(&w_inputbuffer[i]); + } + } + + players[i].cmd.chatchar = 0; + } + } + } +} + +char hu_dequeue_chat_char(void) +{ + char c; + + if (head != tail) + { + c = chatchars[tail]; + tail = (tail + 1) & (QUEUESIZE - 1); + } + else + { + c = 0; + } + + return c; +} + +boolean hu_responder(event_t *ev) +{ + static char lastmessage[HU_MAXLINELENGTH + 1]; + const char *macromessage; + boolean eatkey = false; + static boolean altdown = false; + unsigned char c; + int i; + int numplayers; + + static int num_nobrainers = 0; + + numplayers = 0; + for (i = 0; i < MAXPLAYERS; i++) + numplayers += playeringame[i]; + + if (ev->data1 == KEY_RSHIFT) + { + 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 == key_message_refresh) + { + message_on = true; + message_counter = HU_MSGTIMEOUT; + eatkey = true; + } + else if (netgame && ev->data2 == key_multi_msg) + { + eatkey = true; + start_chat_input(HU_BROADCAST); + } + else if (netgame && numplayers > 2) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (ev->data2 == key_multi_msgplayer[i]) + { + if (playeringame[i] && i != consoleplayer) + { + eatkey = true; + start_chat_input(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 + { + /* send a macro */ + + if (altdown) + { + c = ev->data1 - '0'; + if (c > 9) return false; + macromessage = chat_macros[c]; + + /* kill last message with a '\n' */ + + hu_queue_chat_char(KEY_ENTER); /* DEBUG!!! */ + + /* send the macro message */ + + while (*macromessage) + { + hu_queue_chat_char(*macromessage++); + } + + hu_queue_chat_char(KEY_ENTER); + + /* leave chat mode and notify that it was sent */ + + stop_chat_input(); + m_str_copy(lastmessage, chat_macros[c], sizeof(lastmessage)); + plr->message = lastmessage; + eatkey = true; + } + else + { + c = ev->data3; + + eatkey = hu_lib_key_in_itext(&w_chat, c); + if (eatkey) + { + hu_queue_chat_char(c); + } + + if (c == KEY_ENTER) + { + stop_chat_input(); + if (w_chat.l.len) + { + m_str_copy(lastmessage, w_chat.l.l, sizeof(lastmessage)); + plr->message = lastmessage; + } + } + else if (c == KEY_ESCAPE) + { + stop_chat_input(); + } + } + } + + return eatkey; +} diff --git a/games/NXDoom/src/doom/hu_stuff.h b/games/NXDoom/src/doom/hu_stuff.h new file mode 100644 index 00000000000..646a916c245 --- /dev/null +++ b/games/NXDoom/src/doom/hu_stuff.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/hu_stuff.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: Head up display + * + ****************************************************************************/ + +#ifndef __HU_STUFF_H__ +#define __HU_STUFF_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "v_patch.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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_MSGX 0 +#define HU_MSGY 0 +#define HU_MSGWIDTH 64 /* in characters */ +#define HU_MSGHEIGHT 1 /* in lines */ + +#define HU_MSGTIMEOUT (4 * TICRATE) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern const char *player_names[4]; +extern char *chat_macros[10]; + +extern patch_t *hu_font[HU_FONTSIZE]; + +extern boolean message_dontfuckwithme; + +extern boolean chat_on; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* HEADS UP TEXT */ + +void hu_init(void); +void hu_start(void); + +boolean hu_responder(event_t *ev); + +void hu_ticker(void); +void hu_drawer(void); +char hu_dequeue_chat_char(void); +void hu_erase(void); + +#endif /* __HU_STUFF_H__ */ diff --git a/games/NXDoom/src/doom/info.c b/games/NXDoom/src/doom/info.c new file mode 100644 index 00000000000..24cc869378f --- /dev/null +++ b/games/NXDoom/src/doom/info.c @@ -0,0 +1,5356 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/info.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Thing frame/state LUT, generated by multigen utility. + * This one is the original DOOM version, preserved. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "m_fixed.h" +#include "sounds.h" + +#include "info.h" + +#include "p_mobj.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if 0 + +/* Using misc1 & misc2 */ + +#define STATE_ENTRY(s, f, t, a, ns) \ + { \ + .sprite = (s), \ + .frame = (f), \ + .tics = (t), \ + .action = {(a)}, \ + .nextstate = (ns), \ + .misc1 = 0, \ + .misc2 = 0, \ + } +#else + +/* No misc1 & misc2 members */ + +#define STATE_ENTRY(s, f, t, a, ns) \ + { \ + .sprite = (s), \ + .frame = (f), \ + .tics = (t), \ + .action = {(a)}, \ + .nextstate = (ns), \ + } +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const char *sprnames[] = +{ + "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", NULL, +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Doesn't work with g++, needs actionf_p1 */ + +void a_light0(); +void a_weapon_ready(); +void a_lower(); +void a_raise(); +void a_punch(); +void a_refire(); +void a_fire_pistol(); +void a_light1(); +void a_fire_shotgun(); +void a_light2(); +void a_fire_shotgun2(); +void a_check_reload(); +void a_open_shotgun2(); +void a_load_shotgun2(); +void a_close_shotgun2(); +void a_fire_cgun(); +void a_gun_flash(); +void a_fire_missile(); +void a_saw(); +void a_fire_plasma(); +void a_bfg_sound(); +void a_fire_bfg(); +void a_bfg_spray(); +void a_explode(); +void a_pain(); +void a_player_screm(); +void a_fall(); +void a_xscream(); +void a_look(); +void a_chase(); +void a_face_target(); +void a_pos_attack(); +void a_scream(); +void a_s_pos_attack(); +void a_vile_chase(); +void a_vile_start(); +void a_vile_target(); +void a_vile_attack(); +void a_start_fire(); +void a_fire(); +void a_fire_crackle(); +void a_tracer(); +void a_skel_woosh(); +void a_skel_fist(); +void a_skel_missile(); +void a_fat_raise(); +void a_fat_attack1(); +void a_fat_attack2(); +void a_fat_attack3(); +void a_boss_death(); +void a_c_pos_attack(); +void a_c_pos_refire(); +void a_troop_attack(); +void a_sarg_attack(); +void a_head_attack(); +void a_bruis_attack(); +void a_skull_attack(); +void a_metal(); +void a_spid_refire(); +void a_baby_metal(); +void a_bspi_attack(); +void a_hoof(); +void a_cyber_attack(); +void a_pain_attack(); +void a_pain_die(); +void a_keen_die(); +void a_brain_pain(); +void a_brain_scream(); +void a_brain_die(); +void a_brain_awake(); +void a_brain_split(); +void a_spawn_sound(); +void a_spawn_fly(); +void a_brain_explode(); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +state_t states[NUMSTATES] = +{ + STATE_ENTRY(SPR_TROO, 0, -1, NULL, S_NULL), /* S_NULL */ + STATE_ENTRY(SPR_SHTG, 4, 0, a_light0, S_NULL), /* S_LIGHTDONE */ + STATE_ENTRY(SPR_PUNG, 0, 1, a_weapon_ready, S_PUNCH), /* S_PUNCH */ + STATE_ENTRY(SPR_PUNG, 0, 1, a_lower, S_PUNCHDOWN), /* S_PUNCHDOWN */ + STATE_ENTRY(SPR_PUNG, 0, 1, a_raise, S_PUNCHUP), /* S_PUNCHUP */ + STATE_ENTRY(SPR_PUNG, 1, 4, NULL, S_PUNCH2), /* S_PUNCH1 */ + STATE_ENTRY(SPR_PUNG, 2, 4, a_punch, S_PUNCH3), /* S_PUNCH2 */ + STATE_ENTRY(SPR_PUNG, 3, 5, NULL, S_PUNCH4), /* S_PUNCH3 */ + STATE_ENTRY(SPR_PUNG, 2, 4, NULL, S_PUNCH5), /* S_PUNCH4 */ + STATE_ENTRY(SPR_PUNG, 1, 5, a_refire, S_PUNCH), /* S_PUNCH5 */ + STATE_ENTRY(SPR_PISG, 0, 1, a_weapon_ready, S_PISTOL), /* S_PISTOL */ + STATE_ENTRY(SPR_PISG, 0, 1, a_lower, S_PISTOLDOWN), /* S_PISTOLDOWN */ + STATE_ENTRY(SPR_PISG, 0, 1, a_raise, S_PISTOLUP), /* S_PISTOLUP */ + STATE_ENTRY(SPR_PISG, 0, 4, NULL, S_PISTOL2), /* S_PISTOL1 */ + STATE_ENTRY(SPR_PISG, 1, 6, a_fire_pistol, S_PISTOL3), /* S_PISTOL2 */ + STATE_ENTRY(SPR_PISG, 2, 4, NULL, S_PISTOL4), /* S_PISTOL3 */ + STATE_ENTRY(SPR_PISG, 1, 5, a_refire, S_PISTOL), /* S_PISTOL4 */ + STATE_ENTRY(SPR_PISF, 32768, 7, a_light1, + S_LIGHTDONE), /* S_PISTOLFLASH */ + STATE_ENTRY(SPR_SHTG, 0, 1, a_weapon_ready, S_SGUN), /* S_SGUN */ + STATE_ENTRY(SPR_SHTG, 0, 1, a_lower, S_SGUNDOWN), /* S_SGUNDOWN */ + STATE_ENTRY(SPR_SHTG, 0, 1, a_raise, S_SGUNUP), /* S_SGUNUP */ + STATE_ENTRY(SPR_SHTG, 0, 3, NULL, S_SGUN2), /* S_SGUN1 */ + STATE_ENTRY(SPR_SHTG, 0, 7, a_fire_shotgun, S_SGUN3), /* S_SGUN2 */ + STATE_ENTRY(SPR_SHTG, 1, 5, NULL, S_SGUN4), /* S_SGUN3 */ + STATE_ENTRY(SPR_SHTG, 2, 5, NULL, S_SGUN5), /* S_SGUN4 */ + STATE_ENTRY(SPR_SHTG, 3, 4, NULL, S_SGUN6), /* S_SGUN5 */ + STATE_ENTRY(SPR_SHTG, 2, 5, NULL, S_SGUN7), /* S_SGUN6 */ + STATE_ENTRY(SPR_SHTG, 1, 5, NULL, S_SGUN8), /* S_SGUN7 */ + STATE_ENTRY(SPR_SHTG, 0, 3, NULL, S_SGUN9), /* S_SGUN8 */ + STATE_ENTRY(SPR_SHTG, 0, 7, a_refire, S_SGUN), /* S_SGUN9 */ + STATE_ENTRY(SPR_SHTF, 32768, 4, a_light1, + S_SGUNFLASH2), /* S_SGUNFLASH1 */ + STATE_ENTRY(SPR_SHTF, 32769, 3, a_light2, S_LIGHTDONE), /* S_SGUNFLASH2 */ + STATE_ENTRY(SPR_SHT2, 0, 1, a_weapon_ready, S_DSGUN), /* S_DSGUN */ + STATE_ENTRY(SPR_SHT2, 0, 1, a_lower, S_DSGUNDOWN), /* S_DSGUNDOWN */ + STATE_ENTRY(SPR_SHT2, 0, 1, a_raise, S_DSGUNUP), /* S_DSGUNUP */ + STATE_ENTRY(SPR_SHT2, 0, 3, NULL, S_DSGUN2), /* S_DSGUN1 */ + STATE_ENTRY(SPR_SHT2, 0, 7, a_fire_shotgun2, S_DSGUN3), /* S_DSGUN2 */ + STATE_ENTRY(SPR_SHT2, 1, 7, NULL, S_DSGUN4), /* S_DSGUN3 */ + STATE_ENTRY(SPR_SHT2, 2, 7, a_check_reload, S_DSGUN5), /* S_DSGUN4 */ + STATE_ENTRY(SPR_SHT2, 3, 7, a_open_shotgun2, S_DSGUN6), /* S_DSGUN5 */ + STATE_ENTRY(SPR_SHT2, 4, 7, NULL, S_DSGUN7), /* S_DSGUN6 */ + STATE_ENTRY(SPR_SHT2, 5, 7, a_load_shotgun2, S_DSGUN8), /* S_DSGUN7 */ + STATE_ENTRY(SPR_SHT2, 6, 6, NULL, S_DSGUN9), /* S_DSGUN8 */ + STATE_ENTRY(SPR_SHT2, 7, 6, a_close_shotgun2, S_DSGUN10), /* S_DSGUN9 */ + STATE_ENTRY(SPR_SHT2, 0, 5, a_refire, S_DSGUN), /* S_DSGUN10 */ + STATE_ENTRY(SPR_SHT2, 1, 7, NULL, S_DSNR2), /* S_DSNR1 */ + STATE_ENTRY(SPR_SHT2, 0, 3, NULL, S_DSGUNDOWN), /* S_DSNR2 */ + STATE_ENTRY(SPR_SHT2, 32776, 5, a_light1, + S_DSGUNFLASH2), /* S_DSGUNFLASH1 */ + STATE_ENTRY(SPR_SHT2, 32777, 4, a_light2, + S_LIGHTDONE), /* S_DSGUNFLASH2 */ + STATE_ENTRY(SPR_CHGG, 0, 1, a_weapon_ready, S_CHAIN), /* S_CHAIN */ + STATE_ENTRY(SPR_CHGG, 0, 1, a_lower, S_CHAINDOWN), /* S_CHAINDOWN */ + STATE_ENTRY(SPR_CHGG, 0, 1, a_raise, S_CHAINUP), /* S_CHAINUP */ + STATE_ENTRY(SPR_CHGG, 0, 4, a_fire_cgun, S_CHAIN2), /* S_CHAIN1 */ + STATE_ENTRY(SPR_CHGG, 1, 4, a_fire_cgun, S_CHAIN3), /* S_CHAIN2 */ + STATE_ENTRY(SPR_CHGG, 1, 0, a_refire, S_CHAIN), /* S_CHAIN3 */ + STATE_ENTRY(SPR_CHGF, 32768, 5, a_light1, + S_LIGHTDONE), /* S_CHAINFLASH1 */ + STATE_ENTRY(SPR_CHGF, 32769, 5, a_light2, + S_LIGHTDONE), /* S_CHAINFLASH2 */ + STATE_ENTRY(SPR_MISG, 0, 1, a_weapon_ready, S_MISSILE), /* S_MISSILE */ + STATE_ENTRY(SPR_MISG, 0, 1, a_lower, S_MISSILEDOWN), /* S_MISSILEDOWN */ + STATE_ENTRY(SPR_MISG, 0, 1, a_raise, S_MISSILEUP), /* S_MISSILEUP */ + STATE_ENTRY(SPR_MISG, 1, 8, a_gun_flash, S_MISSILE2), /* S_MISSILE1 */ + STATE_ENTRY(SPR_MISG, 1, 12, a_fire_missile, S_MISSILE3), /* S_MISSILE2 */ + STATE_ENTRY(SPR_MISG, 1, 0, a_refire, S_MISSILE), /* S_MISSILE3 */ + STATE_ENTRY(SPR_MISF, 32768, 3, a_light1, + S_MISSILEFLASH2), /* S_MISSILEFLASH1 */ + STATE_ENTRY(SPR_MISF, 32769, 4, NULL, + S_MISSILEFLASH3), /* S_MISSILEFLASH2 */ + STATE_ENTRY(SPR_MISF, 32770, 4, a_light2, + S_MISSILEFLASH4), /* S_MISSILEFLASH3 */ + STATE_ENTRY(SPR_MISF, 32771, 4, a_light2, + S_LIGHTDONE), /* S_MISSILEFLASH4 */ + STATE_ENTRY(SPR_SAWG, 2, 4, a_weapon_ready, S_SAWB), /* S_SAW */ + STATE_ENTRY(SPR_SAWG, 3, 4, a_weapon_ready, S_SAW), /* S_SAWB */ + STATE_ENTRY(SPR_SAWG, 2, 1, a_lower, S_SAWDOWN), /* S_SAWDOWN */ + STATE_ENTRY(SPR_SAWG, 2, 1, a_raise, S_SAWUP), /* S_SAWUP */ + STATE_ENTRY(SPR_SAWG, 0, 4, a_saw, S_SAW2), /* S_SAW1 */ + STATE_ENTRY(SPR_SAWG, 1, 4, a_saw, S_SAW3), /* S_SAW2 */ + STATE_ENTRY(SPR_SAWG, 1, 0, a_refire, S_SAW), /* S_SAW3 */ + STATE_ENTRY(SPR_PLSG, 0, 1, a_weapon_ready, S_PLASMA), /* S_PLASMA */ + STATE_ENTRY(SPR_PLSG, 0, 1, a_lower, S_PLASMADOWN), /* S_PLASMADOWN */ + STATE_ENTRY(SPR_PLSG, 0, 1, a_raise, S_PLASMAUP), /* S_PLASMAUP */ + STATE_ENTRY(SPR_PLSG, 0, 3, a_fire_plasma, S_PLASMA2), /* S_PLASMA1 */ + STATE_ENTRY(SPR_PLSG, 1, 20, a_refire, S_PLASMA), /* S_PLASMA2 */ + STATE_ENTRY(SPR_PLSF, 32768, 4, a_light1, + S_LIGHTDONE), /* S_PLASMAFLASH1 */ + STATE_ENTRY(SPR_PLSF, 32769, 4, a_light1, + S_LIGHTDONE), /* S_PLASMAFLASH2 */ + STATE_ENTRY(SPR_BFGG, 0, 1, a_weapon_ready, S_BFG), /* S_BFG */ + STATE_ENTRY(SPR_BFGG, 0, 1, a_lower, S_BFGDOWN), /* S_BFGDOWN */ + STATE_ENTRY(SPR_BFGG, 0, 1, a_raise, S_BFGUP), /* S_BFGUP */ + STATE_ENTRY(SPR_BFGG, 0, 20, a_bfg_sound, S_BFG2), /* S_BFG1 */ + STATE_ENTRY(SPR_BFGG, 1, 10, a_gun_flash, S_BFG3), /* S_BFG2 */ + STATE_ENTRY(SPR_BFGG, 1, 10, a_fire_bfg, S_BFG4), /* S_BFG3 */ + STATE_ENTRY(SPR_BFGG, 1, 20, a_refire, S_BFG), /* S_BFG4 */ + STATE_ENTRY(SPR_BFGF, 32768, 11, a_light1, S_BFGFLASH2), /* S_BFGFLASH1 */ + STATE_ENTRY(SPR_BFGF, 32769, 6, a_light2, S_LIGHTDONE), /* S_BFGFLASH2 */ + STATE_ENTRY(SPR_BLUD, 2, 8, NULL, S_BLOOD2), /* S_BLOOD1 */ + STATE_ENTRY(SPR_BLUD, 1, 8, NULL, S_BLOOD3), /* S_BLOOD2 */ + STATE_ENTRY(SPR_BLUD, 0, 8, NULL, S_NULL), /* S_BLOOD3 */ + STATE_ENTRY(SPR_PUFF, 32768, 4, NULL, S_PUFF2), /* S_PUFF1 */ + STATE_ENTRY(SPR_PUFF, 1, 4, NULL, S_PUFF3), /* S_PUFF2 */ + STATE_ENTRY(SPR_PUFF, 2, 4, NULL, S_PUFF4), /* S_PUFF3 */ + STATE_ENTRY(SPR_PUFF, 3, 4, NULL, S_NULL), /* S_PUFF4 */ + STATE_ENTRY(SPR_BAL1, 32768, 4, NULL, S_TBALL2), /* S_TBALL1 */ + STATE_ENTRY(SPR_BAL1, 32769, 4, NULL, S_TBALL1), /* S_TBALL2 */ + STATE_ENTRY(SPR_BAL1, 32770, 6, NULL, S_TBALLX2), /* S_TBALLX1 */ + STATE_ENTRY(SPR_BAL1, 32771, 6, NULL, S_TBALLX3), /* S_TBALLX2 */ + STATE_ENTRY(SPR_BAL1, 32772, 6, NULL, S_NULL), /* S_TBALLX3 */ + STATE_ENTRY(SPR_BAL2, 32768, 4, NULL, S_RBALL2), /* S_RBALL1 */ + STATE_ENTRY(SPR_BAL2, 32769, 4, NULL, S_RBALL1), /* S_RBALL2 */ + STATE_ENTRY(SPR_BAL2, 32770, 6, NULL, S_RBALLX2), /* S_RBALLX1 */ + STATE_ENTRY(SPR_BAL2, 32771, 6, NULL, S_RBALLX3), /* S_RBALLX2 */ + STATE_ENTRY(SPR_BAL2, 32772, 6, NULL, S_NULL), /* S_RBALLX3 */ + STATE_ENTRY(SPR_PLSS, 32768, 6, NULL, S_PLASBALL2), /* S_PLASBALL */ + STATE_ENTRY(SPR_PLSS, 32769, 6, NULL, S_PLASBALL), /* S_PLASBALL2 */ + STATE_ENTRY(SPR_PLSE, 32768, 4, NULL, S_PLASEXP2), /* S_PLASEXP */ + STATE_ENTRY(SPR_PLSE, 32769, 4, NULL, S_PLASEXP3), /* S_PLASEXP2 */ + STATE_ENTRY(SPR_PLSE, 32770, 4, NULL, S_PLASEXP4), /* S_PLASEXP3 */ + STATE_ENTRY(SPR_PLSE, 32771, 4, NULL, S_PLASEXP5), /* S_PLASEXP4 */ + STATE_ENTRY(SPR_PLSE, 32772, 4, NULL, S_NULL), /* S_PLASEXP5 */ + STATE_ENTRY(SPR_MISL, 32768, 1, NULL, S_ROCKET), /* S_ROCKET */ + STATE_ENTRY(SPR_BFS1, 32768, 4, NULL, S_BFGSHOT2), /* S_BFGSHOT */ + STATE_ENTRY(SPR_BFS1, 32769, 4, NULL, S_BFGSHOT), /* S_BFGSHOT2 */ + STATE_ENTRY(SPR_BFE1, 32768, 8, NULL, S_BFGLAND2), /* S_BFGLAND */ + STATE_ENTRY(SPR_BFE1, 32769, 8, NULL, S_BFGLAND3), /* S_BFGLAND2 */ + STATE_ENTRY(SPR_BFE1, 32770, 8, a_bfg_spray, S_BFGLAND4), /* S_BFGLAND3 */ + STATE_ENTRY(SPR_BFE1, 32771, 8, NULL, S_BFGLAND5), /* S_BFGLAND4 */ + STATE_ENTRY(SPR_BFE1, 32772, 8, NULL, S_BFGLAND6), /* S_BFGLAND5 */ + STATE_ENTRY(SPR_BFE1, 32773, 8, NULL, S_NULL), /* S_BFGLAND6 */ + STATE_ENTRY(SPR_BFE2, 32768, 8, NULL, S_BFGEXP2), /* S_BFGEXP */ + STATE_ENTRY(SPR_BFE2, 32769, 8, NULL, S_BFGEXP3), /* S_BFGEXP2 */ + STATE_ENTRY(SPR_BFE2, 32770, 8, NULL, S_BFGEXP4), /* S_BFGEXP3 */ + STATE_ENTRY(SPR_BFE2, 32771, 8, NULL, S_NULL), /* S_BFGEXP4 */ + STATE_ENTRY(SPR_MISL, 32769, 8, a_explode, S_EXPLODE2), /* S_EXPLODE1 */ + STATE_ENTRY(SPR_MISL, 32770, 6, NULL, S_EXPLODE3), /* S_EXPLODE2 */ + STATE_ENTRY(SPR_MISL, 32771, 4, NULL, S_NULL), /* S_EXPLODE3 */ + STATE_ENTRY(SPR_TFOG, 32768, 6, NULL, S_TFOG01), /* S_TFOG */ + STATE_ENTRY(SPR_TFOG, 32769, 6, NULL, S_TFOG02), /* S_TFOG01 */ + STATE_ENTRY(SPR_TFOG, 32768, 6, NULL, S_TFOG2), /* S_TFOG02 */ + STATE_ENTRY(SPR_TFOG, 32769, 6, NULL, S_TFOG3), /* S_TFOG2 */ + STATE_ENTRY(SPR_TFOG, 32770, 6, NULL, S_TFOG4), /* S_TFOG3 */ + STATE_ENTRY(SPR_TFOG, 32771, 6, NULL, S_TFOG5), /* S_TFOG4 */ + STATE_ENTRY(SPR_TFOG, 32772, 6, NULL, S_TFOG6), /* S_TFOG5 */ + STATE_ENTRY(SPR_TFOG, 32773, 6, NULL, S_TFOG7), /* S_TFOG6 */ + STATE_ENTRY(SPR_TFOG, 32774, 6, NULL, S_TFOG8), /* S_TFOG7 */ + STATE_ENTRY(SPR_TFOG, 32775, 6, NULL, S_TFOG9), /* S_TFOG8 */ + STATE_ENTRY(SPR_TFOG, 32776, 6, NULL, S_TFOG10), /* S_TFOG9 */ + STATE_ENTRY(SPR_TFOG, 32777, 6, NULL, S_NULL), /* S_TFOG10 */ + STATE_ENTRY(SPR_IFOG, 32768, 6, NULL, S_IFOG01), /* S_IFOG */ + STATE_ENTRY(SPR_IFOG, 32769, 6, NULL, S_IFOG02), /* S_IFOG01 */ + STATE_ENTRY(SPR_IFOG, 32768, 6, NULL, S_IFOG2), /* S_IFOG02 */ + STATE_ENTRY(SPR_IFOG, 32769, 6, NULL, S_IFOG3), /* S_IFOG2 */ + STATE_ENTRY(SPR_IFOG, 32770, 6, NULL, S_IFOG4), /* S_IFOG3 */ + STATE_ENTRY(SPR_IFOG, 32771, 6, NULL, S_IFOG5), /* S_IFOG4 */ + STATE_ENTRY(SPR_IFOG, 32772, 6, NULL, S_NULL), /* S_IFOG5 */ + STATE_ENTRY(SPR_PLAY, 0, -1, NULL, S_NULL), /* S_PLAY */ + STATE_ENTRY(SPR_PLAY, 0, 4, NULL, S_PLAY_RUN2), /* S_PLAY_RUN1 */ + STATE_ENTRY(SPR_PLAY, 1, 4, NULL, S_PLAY_RUN3), /* S_PLAY_RUN2 */ + STATE_ENTRY(SPR_PLAY, 2, 4, NULL, S_PLAY_RUN4), /* S_PLAY_RUN3 */ + STATE_ENTRY(SPR_PLAY, 3, 4, NULL, S_PLAY_RUN1), /* S_PLAY_RUN4 */ + STATE_ENTRY(SPR_PLAY, 4, 12, NULL, S_PLAY), /* S_PLAY_ATK1 */ + STATE_ENTRY(SPR_PLAY, 32773, 6, NULL, S_PLAY_ATK1), /* S_PLAY_ATK2 */ + STATE_ENTRY(SPR_PLAY, 6, 4, NULL, S_PLAY_PAIN2), /* S_PLAY_PAIN */ + STATE_ENTRY(SPR_PLAY, 6, 4, a_pain, S_PLAY), /* S_PLAY_PAIN2 */ + STATE_ENTRY(SPR_PLAY, 7, 10, NULL, S_PLAY_DIE2), /* S_PLAY_DIE1 */ + STATE_ENTRY(SPR_PLAY, 8, 10, a_player_screm, + S_PLAY_DIE3), /* S_PLAY_DIE2 */ + STATE_ENTRY(SPR_PLAY, 9, 10, a_fall, S_PLAY_DIE4), /* S_PLAY_DIE3 */ + STATE_ENTRY(SPR_PLAY, 10, 10, NULL, S_PLAY_DIE5), /* S_PLAY_DIE4 */ + STATE_ENTRY(SPR_PLAY, 11, 10, NULL, S_PLAY_DIE6), /* S_PLAY_DIE5 */ + STATE_ENTRY(SPR_PLAY, 12, 10, NULL, S_PLAY_DIE7), /* S_PLAY_DIE6 */ + STATE_ENTRY(SPR_PLAY, 13, -1, NULL, S_NULL), /* S_PLAY_DIE7 */ + STATE_ENTRY(SPR_PLAY, 14, 5, NULL, S_PLAY_XDIE2), /* S_PLAY_XDIE1 */ + STATE_ENTRY(SPR_PLAY, 15, 5, a_xscream, S_PLAY_XDIE3), /* S_PLAY_XDIE2 */ + STATE_ENTRY(SPR_PLAY, 16, 5, a_fall, S_PLAY_XDIE4), /* S_PLAY_XDIE3 */ + STATE_ENTRY(SPR_PLAY, 17, 5, NULL, S_PLAY_XDIE5), /* S_PLAY_XDIE4 */ + STATE_ENTRY(SPR_PLAY, 18, 5, NULL, S_PLAY_XDIE6), /* S_PLAY_XDIE5 */ + STATE_ENTRY(SPR_PLAY, 19, 5, NULL, S_PLAY_XDIE7), /* S_PLAY_XDIE6 */ + STATE_ENTRY(SPR_PLAY, 20, 5, NULL, S_PLAY_XDIE8), /* S_PLAY_XDIE7 */ + STATE_ENTRY(SPR_PLAY, 21, 5, NULL, S_PLAY_XDIE9), /* S_PLAY_XDIE8 */ + STATE_ENTRY(SPR_PLAY, 22, -1, NULL, S_NULL), /* S_PLAY_XDIE9 */ + STATE_ENTRY(SPR_POSS, 0, 10, a_look, S_POSS_STND2), /* S_POSS_STND */ + STATE_ENTRY(SPR_POSS, 1, 10, a_look, S_POSS_STND), /* S_POSS_STND2 */ + STATE_ENTRY(SPR_POSS, 0, 4, a_chase, S_POSS_RUN2), /* S_POSS_RUN1 */ + STATE_ENTRY(SPR_POSS, 0, 4, a_chase, S_POSS_RUN3), /* S_POSS_RUN2 */ + STATE_ENTRY(SPR_POSS, 1, 4, a_chase, S_POSS_RUN4), /* S_POSS_RUN3 */ + STATE_ENTRY(SPR_POSS, 1, 4, a_chase, S_POSS_RUN5), /* S_POSS_RUN4 */ + STATE_ENTRY(SPR_POSS, 2, 4, a_chase, S_POSS_RUN6), /* S_POSS_RUN5 */ + STATE_ENTRY(SPR_POSS, 2, 4, a_chase, S_POSS_RUN7), /* S_POSS_RUN6 */ + STATE_ENTRY(SPR_POSS, 3, 4, a_chase, S_POSS_RUN8), /* S_POSS_RUN7 */ + STATE_ENTRY(SPR_POSS, 3, 4, a_chase, S_POSS_RUN1), /* S_POSS_RUN8 */ + STATE_ENTRY(SPR_POSS, 4, 10, a_face_target, + S_POSS_ATK2), /* S_POSS_ATK1 */ + STATE_ENTRY(SPR_POSS, 5, 8, a_pos_attack, S_POSS_ATK3), /* S_POSS_ATK2 */ + STATE_ENTRY(SPR_POSS, 4, 8, NULL, S_POSS_RUN1), /* S_POSS_ATK3 */ + STATE_ENTRY(SPR_POSS, 6, 3, NULL, S_POSS_PAIN2), /* S_POSS_PAIN */ + STATE_ENTRY(SPR_POSS, 6, 3, a_pain, S_POSS_RUN1), /* S_POSS_PAIN2 */ + STATE_ENTRY(SPR_POSS, 7, 5, NULL, S_POSS_DIE2), /* S_POSS_DIE1 */ + STATE_ENTRY(SPR_POSS, 8, 5, a_scream, S_POSS_DIE3), /* S_POSS_DIE2 */ + STATE_ENTRY(SPR_POSS, 9, 5, a_fall, S_POSS_DIE4), /* S_POSS_DIE3 */ + STATE_ENTRY(SPR_POSS, 10, 5, NULL, S_POSS_DIE5), /* S_POSS_DIE4 */ + STATE_ENTRY(SPR_POSS, 11, -1, NULL, S_NULL), /* S_POSS_DIE5 */ + STATE_ENTRY(SPR_POSS, 12, 5, NULL, S_POSS_XDIE2), /* S_POSS_XDIE1 */ + STATE_ENTRY(SPR_POSS, 13, 5, a_xscream, S_POSS_XDIE3), /* S_POSS_XDIE2 */ + STATE_ENTRY(SPR_POSS, 14, 5, a_fall, S_POSS_XDIE4), /* S_POSS_XDIE3 */ + STATE_ENTRY(SPR_POSS, 15, 5, NULL, S_POSS_XDIE5), /* S_POSS_XDIE4 */ + STATE_ENTRY(SPR_POSS, 16, 5, NULL, S_POSS_XDIE6), /* S_POSS_XDIE5 */ + STATE_ENTRY(SPR_POSS, 17, 5, NULL, S_POSS_XDIE7), /* S_POSS_XDIE6 */ + STATE_ENTRY(SPR_POSS, 18, 5, NULL, S_POSS_XDIE8), /* S_POSS_XDIE7 */ + STATE_ENTRY(SPR_POSS, 19, 5, NULL, S_POSS_XDIE9), /* S_POSS_XDIE8 */ + STATE_ENTRY(SPR_POSS, 20, -1, NULL, S_NULL), /* S_POSS_XDIE9 */ + STATE_ENTRY(SPR_POSS, 10, 5, NULL, S_POSS_RAISE2), /* S_POSS_RAISE1 */ + STATE_ENTRY(SPR_POSS, 9, 5, NULL, S_POSS_RAISE3), /* S_POSS_RAISE2 */ + STATE_ENTRY(SPR_POSS, 8, 5, NULL, S_POSS_RAISE4), /* S_POSS_RAISE3 */ + STATE_ENTRY(SPR_POSS, 7, 5, NULL, S_POSS_RUN1), /* S_POSS_RAISE4 */ + STATE_ENTRY(SPR_SPOS, 0, 10, a_look, S_SPOS_STND2), /* S_SPOS_STND */ + STATE_ENTRY(SPR_SPOS, 1, 10, a_look, S_SPOS_STND), /* S_SPOS_STND2 */ + STATE_ENTRY(SPR_SPOS, 0, 3, a_chase, S_SPOS_RUN2), /* S_SPOS_RUN1 */ + STATE_ENTRY(SPR_SPOS, 0, 3, a_chase, S_SPOS_RUN3), /* S_SPOS_RUN2 */ + STATE_ENTRY(SPR_SPOS, 1, 3, a_chase, S_SPOS_RUN4), /* S_SPOS_RUN3 */ + STATE_ENTRY(SPR_SPOS, 1, 3, a_chase, S_SPOS_RUN5), /* S_SPOS_RUN4 */ + STATE_ENTRY(SPR_SPOS, 2, 3, a_chase, S_SPOS_RUN6), /* S_SPOS_RUN5 */ + STATE_ENTRY(SPR_SPOS, 2, 3, a_chase, S_SPOS_RUN7), /* S_SPOS_RUN6 */ + STATE_ENTRY(SPR_SPOS, 3, 3, a_chase, S_SPOS_RUN8), /* S_SPOS_RUN7 */ + STATE_ENTRY(SPR_SPOS, 3, 3, a_chase, S_SPOS_RUN1), /* S_SPOS_RUN8 */ + STATE_ENTRY(SPR_SPOS, 4, 10, a_face_target, + S_SPOS_ATK2), /* S_SPOS_ATK1 */ + STATE_ENTRY(SPR_SPOS, 32773, 10, a_s_pos_attack, + S_SPOS_ATK3), /* S_SPOS_ATK2 */ + STATE_ENTRY(SPR_SPOS, 4, 10, NULL, S_SPOS_RUN1), /* S_SPOS_ATK3 */ + STATE_ENTRY(SPR_SPOS, 6, 3, NULL, S_SPOS_PAIN2), /* S_SPOS_PAIN */ + STATE_ENTRY(SPR_SPOS, 6, 3, a_pain, S_SPOS_RUN1), /* S_SPOS_PAIN2 */ + STATE_ENTRY(SPR_SPOS, 7, 5, NULL, S_SPOS_DIE2), /* S_SPOS_DIE1 */ + STATE_ENTRY(SPR_SPOS, 8, 5, a_scream, S_SPOS_DIE3), /* S_SPOS_DIE2 */ + STATE_ENTRY(SPR_SPOS, 9, 5, a_fall, S_SPOS_DIE4), /* S_SPOS_DIE3 */ + STATE_ENTRY(SPR_SPOS, 10, 5, NULL, S_SPOS_DIE5), /* S_SPOS_DIE4 */ + STATE_ENTRY(SPR_SPOS, 11, -1, NULL, S_NULL), /* S_SPOS_DIE5 */ + STATE_ENTRY(SPR_SPOS, 12, 5, NULL, S_SPOS_XDIE2), /* S_SPOS_XDIE1 */ + STATE_ENTRY(SPR_SPOS, 13, 5, a_xscream, S_SPOS_XDIE3), /* S_SPOS_XDIE2 */ + STATE_ENTRY(SPR_SPOS, 14, 5, a_fall, S_SPOS_XDIE4), /* S_SPOS_XDIE3 */ + STATE_ENTRY(SPR_SPOS, 15, 5, NULL, S_SPOS_XDIE5), /* S_SPOS_XDIE4 */ + STATE_ENTRY(SPR_SPOS, 16, 5, NULL, S_SPOS_XDIE6), /* S_SPOS_XDIE5 */ + STATE_ENTRY(SPR_SPOS, 17, 5, NULL, S_SPOS_XDIE7), /* S_SPOS_XDIE6 */ + STATE_ENTRY(SPR_SPOS, 18, 5, NULL, S_SPOS_XDIE8), /* S_SPOS_XDIE7 */ + STATE_ENTRY(SPR_SPOS, 19, 5, NULL, S_SPOS_XDIE9), /* S_SPOS_XDIE8 */ + STATE_ENTRY(SPR_SPOS, 20, -1, NULL, S_NULL), /* S_SPOS_XDIE9 */ + STATE_ENTRY(SPR_SPOS, 11, 5, NULL, S_SPOS_RAISE2), /* S_SPOS_RAISE1 */ + STATE_ENTRY(SPR_SPOS, 10, 5, NULL, S_SPOS_RAISE3), /* S_SPOS_RAISE2 */ + STATE_ENTRY(SPR_SPOS, 9, 5, NULL, S_SPOS_RAISE4), /* S_SPOS_RAISE3 */ + STATE_ENTRY(SPR_SPOS, 8, 5, NULL, S_SPOS_RAISE5), /* S_SPOS_RAISE4 */ + STATE_ENTRY(SPR_SPOS, 7, 5, NULL, S_SPOS_RUN1), /* S_SPOS_RAISE5 */ + STATE_ENTRY(SPR_VILE, 0, 10, a_look, S_VILE_STND2), /* S_VILE_STND */ + STATE_ENTRY(SPR_VILE, 1, 10, a_look, S_VILE_STND), /* S_VILE_STND2 */ + STATE_ENTRY(SPR_VILE, 0, 2, a_vile_chase, S_VILE_RUN2), /* S_VILE_RUN1 */ + STATE_ENTRY(SPR_VILE, 0, 2, a_vile_chase, S_VILE_RUN3), /* S_VILE_RUN2 */ + STATE_ENTRY(SPR_VILE, 1, 2, a_vile_chase, S_VILE_RUN4), /* S_VILE_RUN3 */ + STATE_ENTRY(SPR_VILE, 1, 2, a_vile_chase, S_VILE_RUN5), /* S_VILE_RUN4 */ + STATE_ENTRY(SPR_VILE, 2, 2, a_vile_chase, S_VILE_RUN6), /* S_VILE_RUN5 */ + STATE_ENTRY(SPR_VILE, 2, 2, a_vile_chase, S_VILE_RUN7), /* S_VILE_RUN6 */ + STATE_ENTRY(SPR_VILE, 3, 2, a_vile_chase, S_VILE_RUN8), /* S_VILE_RUN7 */ + STATE_ENTRY(SPR_VILE, 3, 2, a_vile_chase, S_VILE_RUN9), /* S_VILE_RUN8 */ + STATE_ENTRY(SPR_VILE, 4, 2, a_vile_chase, S_VILE_RUN10), /* S_VILE_RUN9 */ + STATE_ENTRY(SPR_VILE, 4, 2, a_vile_chase, + S_VILE_RUN11), /* S_VILE_RUN10 */ + STATE_ENTRY(SPR_VILE, 5, 2, a_vile_chase, + S_VILE_RUN12), /* S_VILE_RUN11 */ + STATE_ENTRY(SPR_VILE, 5, 2, a_vile_chase, S_VILE_RUN1), /* S_VILE_RUN12 */ + STATE_ENTRY(SPR_VILE, 32774, 0, a_vile_start, + S_VILE_ATK2), /* S_VILE_ATK1 */ + STATE_ENTRY(SPR_VILE, 32774, 10, a_face_target, + S_VILE_ATK3), /* S_VILE_ATK2 */ + STATE_ENTRY(SPR_VILE, 32775, 8, a_vile_target, + S_VILE_ATK4), /* S_VILE_ATK3 */ + STATE_ENTRY(SPR_VILE, 32776, 8, a_face_target, + S_VILE_ATK5), /* S_VILE_ATK4 */ + STATE_ENTRY(SPR_VILE, 32777, 8, a_face_target, + S_VILE_ATK6), /* S_VILE_ATK5 */ + STATE_ENTRY(SPR_VILE, 32778, 8, a_face_target, + S_VILE_ATK7), /* S_VILE_ATK6 */ + STATE_ENTRY(SPR_VILE, 32779, 8, a_face_target, + S_VILE_ATK8), /* S_VILE_ATK7 */ + STATE_ENTRY(SPR_VILE, 32780, 8, a_face_target, + S_VILE_ATK9), /* S_VILE_ATK8 */ + STATE_ENTRY(SPR_VILE, 32781, 8, a_face_target, + S_VILE_ATK10), /* S_VILE_ATK9 */ + STATE_ENTRY(SPR_VILE, 32782, 8, a_vile_attack, + S_VILE_ATK11), /* S_VILE_ATK10 */ + STATE_ENTRY(SPR_VILE, 32783, 20, NULL, S_VILE_RUN1), /* S_VILE_ATK11 */ + STATE_ENTRY(SPR_VILE, 32794, 10, NULL, S_VILE_HEAL2), /* S_VILE_HEAL1 */ + STATE_ENTRY(SPR_VILE, 32795, 10, NULL, S_VILE_HEAL3), /* S_VILE_HEAL2 */ + STATE_ENTRY(SPR_VILE, 32796, 10, NULL, S_VILE_RUN1), /* S_VILE_HEAL3 */ + STATE_ENTRY(SPR_VILE, 16, 5, NULL, S_VILE_PAIN2), /* S_VILE_PAIN */ + STATE_ENTRY(SPR_VILE, 16, 5, a_pain, S_VILE_RUN1), /* S_VILE_PAIN2 */ + STATE_ENTRY(SPR_VILE, 16, 7, NULL, S_VILE_DIE2), /* S_VILE_DIE1 */ + STATE_ENTRY(SPR_VILE, 17, 7, a_scream, S_VILE_DIE3), /* S_VILE_DIE2 */ + STATE_ENTRY(SPR_VILE, 18, 7, a_fall, S_VILE_DIE4), /* S_VILE_DIE3 */ + STATE_ENTRY(SPR_VILE, 19, 7, NULL, S_VILE_DIE5), /* S_VILE_DIE4 */ + STATE_ENTRY(SPR_VILE, 20, 7, NULL, S_VILE_DIE6), /* S_VILE_DIE5 */ + STATE_ENTRY(SPR_VILE, 21, 7, NULL, S_VILE_DIE7), /* S_VILE_DIE6 */ + STATE_ENTRY(SPR_VILE, 22, 7, NULL, S_VILE_DIE8), /* S_VILE_DIE7 */ + STATE_ENTRY(SPR_VILE, 23, 5, NULL, S_VILE_DIE9), /* S_VILE_DIE8 */ + STATE_ENTRY(SPR_VILE, 24, 5, NULL, S_VILE_DIE10), /* S_VILE_DIE9 */ + STATE_ENTRY(SPR_VILE, 25, -1, NULL, S_NULL), /* S_VILE_DIE10 */ + STATE_ENTRY(SPR_FIRE, 32768, 2, a_start_fire, + S_FIRE2), /* S_FIRE1 */ + STATE_ENTRY(SPR_FIRE, 32769, 2, a_fire, S_FIRE3), /* S_FIRE2 */ + STATE_ENTRY(SPR_FIRE, 32768, 2, a_fire, S_FIRE4), /* S_FIRE3 */ + STATE_ENTRY(SPR_FIRE, 32769, 2, a_fire, S_FIRE5), /* S_FIRE4 */ + STATE_ENTRY(SPR_FIRE, 32770, 2, a_fire_crackle, + S_FIRE6), /* S_FIRE5 */ + STATE_ENTRY(SPR_FIRE, 32769, 2, a_fire, S_FIRE7), /* S_FIRE6 */ + STATE_ENTRY(SPR_FIRE, 32770, 2, a_fire, S_FIRE8), /* S_FIRE7 */ + STATE_ENTRY(SPR_FIRE, 32769, 2, a_fire, S_FIRE9), /* S_FIRE8 */ + STATE_ENTRY(SPR_FIRE, 32770, 2, a_fire, S_FIRE10), /* S_FIRE9 */ + STATE_ENTRY(SPR_FIRE, 32771, 2, a_fire, S_FIRE11), /* S_FIRE10 */ + STATE_ENTRY(SPR_FIRE, 32770, 2, a_fire, S_FIRE12), /* S_FIRE11 */ + STATE_ENTRY(SPR_FIRE, 32771, 2, a_fire, S_FIRE13), /* S_FIRE12 */ + STATE_ENTRY(SPR_FIRE, 32770, 2, a_fire, S_FIRE14), /* S_FIRE13 */ + STATE_ENTRY(SPR_FIRE, 32771, 2, a_fire, S_FIRE15), /* S_FIRE14 */ + STATE_ENTRY(SPR_FIRE, 32772, 2, a_fire, S_FIRE16), /* S_FIRE15 */ + STATE_ENTRY(SPR_FIRE, 32771, 2, a_fire, S_FIRE17), /* S_FIRE16 */ + STATE_ENTRY(SPR_FIRE, 32772, 2, a_fire, S_FIRE18), /* S_FIRE17 */ + STATE_ENTRY(SPR_FIRE, 32771, 2, a_fire, S_FIRE19), /* S_FIRE18 */ + STATE_ENTRY(SPR_FIRE, 32772, 2, a_fire_crackle, + S_FIRE20), /* S_FIRE19 */ + STATE_ENTRY(SPR_FIRE, 32773, 2, a_fire, S_FIRE21), /* S_FIRE20 */ + STATE_ENTRY(SPR_FIRE, 32772, 2, a_fire, S_FIRE22), /* S_FIRE21 */ + STATE_ENTRY(SPR_FIRE, 32773, 2, a_fire, S_FIRE23), /* S_FIRE22 */ + STATE_ENTRY(SPR_FIRE, 32772, 2, a_fire, S_FIRE24), /* S_FIRE23 */ + STATE_ENTRY(SPR_FIRE, 32773, 2, a_fire, S_FIRE25), /* S_FIRE24 */ + STATE_ENTRY(SPR_FIRE, 32774, 2, a_fire, S_FIRE26), /* S_FIRE25 */ + STATE_ENTRY(SPR_FIRE, 32775, 2, a_fire, S_FIRE27), /* S_FIRE26 */ + STATE_ENTRY(SPR_FIRE, 32774, 2, a_fire, S_FIRE28), /* S_FIRE27 */ + STATE_ENTRY(SPR_FIRE, 32775, 2, a_fire, S_FIRE29), /* S_FIRE28 */ + STATE_ENTRY(SPR_FIRE, 32774, 2, a_fire, S_FIRE30), /* S_FIRE29 */ + STATE_ENTRY(SPR_FIRE, 32775, 2, a_fire, S_NULL), /* S_FIRE30 */ + STATE_ENTRY(SPR_PUFF, 1, 4, NULL, S_SMOKE2), /* S_SMOKE1 */ + STATE_ENTRY(SPR_PUFF, 2, 4, NULL, S_SMOKE3), /* S_SMOKE2 */ + STATE_ENTRY(SPR_PUFF, 1, 4, NULL, S_SMOKE4), /* S_SMOKE3 */ + STATE_ENTRY(SPR_PUFF, 2, 4, NULL, S_SMOKE5), /* S_SMOKE4 */ + STATE_ENTRY(SPR_PUFF, 3, 4, NULL, S_NULL), /* S_SMOKE5 */ + STATE_ENTRY(SPR_FATB, 32768, 2, a_tracer, S_TRACER2), /* S_TRACER */ + STATE_ENTRY(SPR_FATB, 32769, 2, a_tracer, S_TRACER), /* S_TRACER2 */ + STATE_ENTRY(SPR_FBXP, 32768, 8, NULL, S_TRACEEXP2), /* S_TRACEEXP1 */ + STATE_ENTRY(SPR_FBXP, 32769, 6, NULL, S_TRACEEXP3), /* S_TRACEEXP2 */ + STATE_ENTRY(SPR_FBXP, 32770, 4, NULL, S_NULL), /* S_TRACEEXP3 */ + STATE_ENTRY(SPR_SKEL, 0, 10, a_look, S_SKEL_STND2), /* S_SKEL_STND */ + STATE_ENTRY(SPR_SKEL, 1, 10, a_look, S_SKEL_STND), /* S_SKEL_STND2 */ + STATE_ENTRY(SPR_SKEL, 0, 2, a_chase, S_SKEL_RUN2), /* S_SKEL_RUN1 */ + STATE_ENTRY(SPR_SKEL, 0, 2, a_chase, S_SKEL_RUN3), /* S_SKEL_RUN2 */ + STATE_ENTRY(SPR_SKEL, 1, 2, a_chase, S_SKEL_RUN4), /* S_SKEL_RUN3 */ + STATE_ENTRY(SPR_SKEL, 1, 2, a_chase, S_SKEL_RUN5), /* S_SKEL_RUN4 */ + STATE_ENTRY(SPR_SKEL, 2, 2, a_chase, S_SKEL_RUN6), /* S_SKEL_RUN5 */ + STATE_ENTRY(SPR_SKEL, 2, 2, a_chase, S_SKEL_RUN7), /* S_SKEL_RUN6 */ + STATE_ENTRY(SPR_SKEL, 3, 2, a_chase, S_SKEL_RUN8), /* S_SKEL_RUN7 */ + STATE_ENTRY(SPR_SKEL, 3, 2, a_chase, S_SKEL_RUN9), /* S_SKEL_RUN8 */ + STATE_ENTRY(SPR_SKEL, 4, 2, a_chase, S_SKEL_RUN10), /* S_SKEL_RUN9 */ + STATE_ENTRY(SPR_SKEL, 4, 2, a_chase, S_SKEL_RUN11), /* S_SKEL_RUN10 */ + STATE_ENTRY(SPR_SKEL, 5, 2, a_chase, S_SKEL_RUN12), /* S_SKEL_RUN11 */ + STATE_ENTRY(SPR_SKEL, 5, 2, a_chase, S_SKEL_RUN1), /* S_SKEL_RUN12 */ + STATE_ENTRY(SPR_SKEL, 6, 0, a_face_target, + S_SKEL_FIST2), /* S_SKEL_FIST1 */ + STATE_ENTRY(SPR_SKEL, 6, 6, a_skel_woosh, + S_SKEL_FIST3), /* S_SKEL_FIST2 */ + STATE_ENTRY(SPR_SKEL, 7, 6, a_face_target, + S_SKEL_FIST4), /* S_SKEL_FIST3 */ + STATE_ENTRY(SPR_SKEL, 8, 6, a_skel_fist, S_SKEL_RUN1), /* S_SKEL_FIST4 */ + STATE_ENTRY(SPR_SKEL, 32777, 0, a_face_target, + S_SKEL_MISS2), /* S_SKEL_MISS1 */ + STATE_ENTRY(SPR_SKEL, 32777, 10, a_face_target, + S_SKEL_MISS3), /* S_SKEL_MISS2 */ + STATE_ENTRY(SPR_SKEL, 10, 10, a_skel_missile, + S_SKEL_MISS4), /* S_SKEL_MISS3 */ + STATE_ENTRY(SPR_SKEL, 10, 10, a_face_target, + S_SKEL_RUN1), /* S_SKEL_MISS4 */ + STATE_ENTRY(SPR_SKEL, 11, 5, NULL, S_SKEL_PAIN2), /* S_SKEL_PAIN */ + STATE_ENTRY(SPR_SKEL, 11, 5, a_pain, S_SKEL_RUN1), /* S_SKEL_PAIN2 */ + STATE_ENTRY(SPR_SKEL, 11, 7, NULL, S_SKEL_DIE2), /* S_SKEL_DIE1 */ + STATE_ENTRY(SPR_SKEL, 12, 7, NULL, S_SKEL_DIE3), /* S_SKEL_DIE2 */ + STATE_ENTRY(SPR_SKEL, 13, 7, a_scream, S_SKEL_DIE4), /* S_SKEL_DIE3 */ + STATE_ENTRY(SPR_SKEL, 14, 7, a_fall, S_SKEL_DIE5), /* S_SKEL_DIE4 */ + STATE_ENTRY(SPR_SKEL, 15, 7, NULL, S_SKEL_DIE6), /* S_SKEL_DIE5 */ + STATE_ENTRY(SPR_SKEL, 16, -1, NULL, S_NULL), /* S_SKEL_DIE6 */ + STATE_ENTRY(SPR_SKEL, 16, 5, NULL, S_SKEL_RAISE2), /* S_SKEL_RAISE1 */ + STATE_ENTRY(SPR_SKEL, 15, 5, NULL, S_SKEL_RAISE3), /* S_SKEL_RAISE2 */ + STATE_ENTRY(SPR_SKEL, 14, 5, NULL, S_SKEL_RAISE4), /* S_SKEL_RAISE3 */ + STATE_ENTRY(SPR_SKEL, 13, 5, NULL, S_SKEL_RAISE5), /* S_SKEL_RAISE4 */ + STATE_ENTRY(SPR_SKEL, 12, 5, NULL, S_SKEL_RAISE6), /* S_SKEL_RAISE5 */ + STATE_ENTRY(SPR_SKEL, 11, 5, NULL, S_SKEL_RUN1), /* S_SKEL_RAISE6 */ + STATE_ENTRY(SPR_MANF, 32768, 4, NULL, S_FATSHOT2), /* S_FATSHOT1 */ + STATE_ENTRY(SPR_MANF, 32769, 4, NULL, S_FATSHOT1), /* S_FATSHOT2 */ + STATE_ENTRY(SPR_MISL, 32769, 8, NULL, S_FATSHOTX2), /* S_FATSHOTX1 */ + STATE_ENTRY(SPR_MISL, 32770, 6, NULL, S_FATSHOTX3), /* S_FATSHOTX2 */ + STATE_ENTRY(SPR_MISL, 32771, 4, NULL, S_NULL), /* S_FATSHOTX3 */ + STATE_ENTRY(SPR_FATT, 0, 15, a_look, S_FATT_STND2), /* S_FATT_STND */ + STATE_ENTRY(SPR_FATT, 1, 15, a_look, S_FATT_STND), /* S_FATT_STND2 */ + STATE_ENTRY(SPR_FATT, 0, 4, a_chase, S_FATT_RUN2), /* S_FATT_RUN1 */ + STATE_ENTRY(SPR_FATT, 0, 4, a_chase, S_FATT_RUN3), /* S_FATT_RUN2 */ + STATE_ENTRY(SPR_FATT, 1, 4, a_chase, S_FATT_RUN4), /* S_FATT_RUN3 */ + STATE_ENTRY(SPR_FATT, 1, 4, a_chase, S_FATT_RUN5), /* S_FATT_RUN4 */ + STATE_ENTRY(SPR_FATT, 2, 4, a_chase, S_FATT_RUN6), /* S_FATT_RUN5 */ + STATE_ENTRY(SPR_FATT, 2, 4, a_chase, S_FATT_RUN7), /* S_FATT_RUN6 */ + STATE_ENTRY(SPR_FATT, 3, 4, a_chase, S_FATT_RUN8), /* S_FATT_RUN7 */ + STATE_ENTRY(SPR_FATT, 3, 4, a_chase, S_FATT_RUN9), /* S_FATT_RUN8 */ + STATE_ENTRY(SPR_FATT, 4, 4, a_chase, S_FATT_RUN10), /* S_FATT_RUN9 */ + STATE_ENTRY(SPR_FATT, 4, 4, a_chase, S_FATT_RUN11), /* S_FATT_RUN10 */ + STATE_ENTRY(SPR_FATT, 5, 4, a_chase, S_FATT_RUN12), /* S_FATT_RUN11 */ + STATE_ENTRY(SPR_FATT, 5, 4, a_chase, S_FATT_RUN1), /* S_FATT_RUN12 */ + STATE_ENTRY(SPR_FATT, 6, 20, a_fat_raise, S_FATT_ATK2), /* S_FATT_ATK1 */ + STATE_ENTRY(SPR_FATT, 32775, 10, a_fat_attack1, + S_FATT_ATK3), /* S_FATT_ATK2 */ + STATE_ENTRY(SPR_FATT, 8, 5, a_face_target, S_FATT_ATK4), /* S_FATT_ATK3 */ + STATE_ENTRY(SPR_FATT, 6, 5, a_face_target, S_FATT_ATK5), /* S_FATT_ATK4 */ + STATE_ENTRY(SPR_FATT, 32775, 10, a_fat_attack2, + S_FATT_ATK6), /* S_FATT_ATK5 */ + STATE_ENTRY(SPR_FATT, 8, 5, a_face_target, S_FATT_ATK7), /* S_FATT_ATK6 */ + STATE_ENTRY(SPR_FATT, 6, 5, a_face_target, S_FATT_ATK8), /* S_FATT_ATK7 */ + STATE_ENTRY(SPR_FATT, 32775, 10, a_fat_attack3, + S_FATT_ATK9), /* S_FATT_ATK8 */ + STATE_ENTRY(SPR_FATT, 8, 5, a_face_target, + S_FATT_ATK10), /* S_FATT_ATK9 */ + STATE_ENTRY(SPR_FATT, 6, 5, a_face_target, + S_FATT_RUN1), /* S_FATT_ATK10 */ + STATE_ENTRY(SPR_FATT, 9, 3, NULL, S_FATT_PAIN2), /* S_FATT_PAIN */ + STATE_ENTRY(SPR_FATT, 9, 3, a_pain, S_FATT_RUN1), /* S_FATT_PAIN2 */ + STATE_ENTRY(SPR_FATT, 10, 6, NULL, S_FATT_DIE2), /* S_FATT_DIE1 */ + STATE_ENTRY(SPR_FATT, 11, 6, a_scream, S_FATT_DIE3), /* S_FATT_DIE2 */ + STATE_ENTRY(SPR_FATT, 12, 6, a_fall, S_FATT_DIE4), /* S_FATT_DIE3 */ + STATE_ENTRY(SPR_FATT, 13, 6, NULL, S_FATT_DIE5), /* S_FATT_DIE4 */ + STATE_ENTRY(SPR_FATT, 14, 6, NULL, S_FATT_DIE6), /* S_FATT_DIE5 */ + STATE_ENTRY(SPR_FATT, 15, 6, NULL, S_FATT_DIE7), /* S_FATT_DIE6 */ + STATE_ENTRY(SPR_FATT, 16, 6, NULL, S_FATT_DIE8), /* S_FATT_DIE7 */ + STATE_ENTRY(SPR_FATT, 17, 6, NULL, S_FATT_DIE9), /* S_FATT_DIE8 */ + STATE_ENTRY(SPR_FATT, 18, 6, NULL, S_FATT_DIE10), /* S_FATT_DIE9 */ + STATE_ENTRY(SPR_FATT, 19, -1, a_boss_death, S_NULL), /* S_FATT_DIE10 */ + STATE_ENTRY(SPR_FATT, 17, 5, NULL, S_FATT_RAISE2), /* S_FATT_RAISE1 */ + STATE_ENTRY(SPR_FATT, 16, 5, NULL, S_FATT_RAISE3), /* S_FATT_RAISE2 */ + STATE_ENTRY(SPR_FATT, 15, 5, NULL, S_FATT_RAISE4), /* S_FATT_RAISE3 */ + STATE_ENTRY(SPR_FATT, 14, 5, NULL, S_FATT_RAISE5), /* S_FATT_RAISE4 */ + STATE_ENTRY(SPR_FATT, 13, 5, NULL, S_FATT_RAISE6), /* S_FATT_RAISE5 */ + STATE_ENTRY(SPR_FATT, 12, 5, NULL, S_FATT_RAISE7), /* S_FATT_RAISE6 */ + STATE_ENTRY(SPR_FATT, 11, 5, NULL, S_FATT_RAISE8), /* S_FATT_RAISE7 */ + STATE_ENTRY(SPR_FATT, 10, 5, NULL, S_FATT_RUN1), /* S_FATT_RAISE8 */ + STATE_ENTRY(SPR_CPOS, 0, 10, a_look, S_CPOS_STND2), /* S_CPOS_STND */ + STATE_ENTRY(SPR_CPOS, 1, 10, a_look, S_CPOS_STND), /* S_CPOS_STND2 */ + STATE_ENTRY(SPR_CPOS, 0, 3, a_chase, S_CPOS_RUN2), /* S_CPOS_RUN1 */ + STATE_ENTRY(SPR_CPOS, 0, 3, a_chase, S_CPOS_RUN3), /* S_CPOS_RUN2 */ + STATE_ENTRY(SPR_CPOS, 1, 3, a_chase, S_CPOS_RUN4), /* S_CPOS_RUN3 */ + STATE_ENTRY(SPR_CPOS, 1, 3, a_chase, S_CPOS_RUN5), /* S_CPOS_RUN4 */ + STATE_ENTRY(SPR_CPOS, 2, 3, a_chase, S_CPOS_RUN6), /* S_CPOS_RUN5 */ + STATE_ENTRY(SPR_CPOS, 2, 3, a_chase, S_CPOS_RUN7), /* S_CPOS_RUN6 */ + STATE_ENTRY(SPR_CPOS, 3, 3, a_chase, S_CPOS_RUN8), /* S_CPOS_RUN7 */ + STATE_ENTRY(SPR_CPOS, 3, 3, a_chase, S_CPOS_RUN1), /* S_CPOS_RUN8 */ + STATE_ENTRY(SPR_CPOS, 4, 10, a_face_target, + S_CPOS_ATK2), /* S_CPOS_ATK1 */ + STATE_ENTRY(SPR_CPOS, 32773, 4, a_c_pos_attack, + S_CPOS_ATK3), /* S_CPOS_ATK2 */ + STATE_ENTRY(SPR_CPOS, 32772, 4, a_c_pos_attack, + S_CPOS_ATK4), /* S_CPOS_ATK3 */ + STATE_ENTRY(SPR_CPOS, 5, 1, a_c_pos_refire, + S_CPOS_ATK2), /* S_CPOS_ATK4 */ + STATE_ENTRY(SPR_CPOS, 6, 3, NULL, S_CPOS_PAIN2), /* S_CPOS_PAIN */ + STATE_ENTRY(SPR_CPOS, 6, 3, a_pain, S_CPOS_RUN1), /* S_CPOS_PAIN2 */ + STATE_ENTRY(SPR_CPOS, 7, 5, NULL, S_CPOS_DIE2), /* S_CPOS_DIE1 */ + STATE_ENTRY(SPR_CPOS, 8, 5, a_scream, S_CPOS_DIE3), /* S_CPOS_DIE2 */ + STATE_ENTRY(SPR_CPOS, 9, 5, a_fall, S_CPOS_DIE4), /* S_CPOS_DIE3 */ + STATE_ENTRY(SPR_CPOS, 10, 5, NULL, S_CPOS_DIE5), /* S_CPOS_DIE4 */ + STATE_ENTRY(SPR_CPOS, 11, 5, NULL, S_CPOS_DIE6), /* S_CPOS_DIE5 */ + STATE_ENTRY(SPR_CPOS, 12, 5, NULL, S_CPOS_DIE7), /* S_CPOS_DIE6 */ + STATE_ENTRY(SPR_CPOS, 13, -1, NULL, S_NULL), /* S_CPOS_DIE7 */ + STATE_ENTRY(SPR_CPOS, 14, 5, NULL, S_CPOS_XDIE2), /* S_CPOS_XDIE1 */ + STATE_ENTRY(SPR_CPOS, 15, 5, a_xscream, S_CPOS_XDIE3), /* S_CPOS_XDIE2 */ + STATE_ENTRY(SPR_CPOS, 16, 5, a_fall, S_CPOS_XDIE4), /* S_CPOS_XDIE3 */ + STATE_ENTRY(SPR_CPOS, 17, 5, NULL, S_CPOS_XDIE5), /* S_CPOS_XDIE4 */ + STATE_ENTRY(SPR_CPOS, 18, 5, NULL, S_CPOS_XDIE6), /* S_CPOS_XDIE5 */ + STATE_ENTRY(SPR_CPOS, 19, -1, NULL, S_NULL), /* S_CPOS_XDIE6 */ + STATE_ENTRY(SPR_CPOS, 13, 5, NULL, S_CPOS_RAISE2), /* S_CPOS_RAISE1 */ + STATE_ENTRY(SPR_CPOS, 12, 5, NULL, S_CPOS_RAISE3), /* S_CPOS_RAISE2 */ + STATE_ENTRY(SPR_CPOS, 11, 5, NULL, S_CPOS_RAISE4), /* S_CPOS_RAISE3 */ + STATE_ENTRY(SPR_CPOS, 10, 5, NULL, S_CPOS_RAISE5), /* S_CPOS_RAISE4 */ + STATE_ENTRY(SPR_CPOS, 9, 5, NULL, S_CPOS_RAISE6), /* S_CPOS_RAISE5 */ + STATE_ENTRY(SPR_CPOS, 8, 5, NULL, S_CPOS_RAISE7), /* S_CPOS_RAISE6 */ + STATE_ENTRY(SPR_CPOS, 7, 5, NULL, S_CPOS_RUN1), /* S_CPOS_RAISE7 */ + STATE_ENTRY(SPR_TROO, 0, 10, a_look, S_TROO_STND2), /* S_TROO_STND */ + STATE_ENTRY(SPR_TROO, 1, 10, a_look, S_TROO_STND), /* S_TROO_STND2 */ + STATE_ENTRY(SPR_TROO, 0, 3, a_chase, S_TROO_RUN2), /* S_TROO_RUN1 */ + STATE_ENTRY(SPR_TROO, 0, 3, a_chase, S_TROO_RUN3), /* S_TROO_RUN2 */ + STATE_ENTRY(SPR_TROO, 1, 3, a_chase, S_TROO_RUN4), /* S_TROO_RUN3 */ + STATE_ENTRY(SPR_TROO, 1, 3, a_chase, S_TROO_RUN5), /* S_TROO_RUN4 */ + STATE_ENTRY(SPR_TROO, 2, 3, a_chase, S_TROO_RUN6), /* S_TROO_RUN5 */ + STATE_ENTRY(SPR_TROO, 2, 3, a_chase, S_TROO_RUN7), /* S_TROO_RUN6 */ + STATE_ENTRY(SPR_TROO, 3, 3, a_chase, S_TROO_RUN8), /* S_TROO_RUN7 */ + STATE_ENTRY(SPR_TROO, 3, 3, a_chase, S_TROO_RUN1), /* S_TROO_RUN8 */ + STATE_ENTRY(SPR_TROO, 4, 8, a_face_target, S_TROO_ATK2), /* S_TROO_ATK1 */ + STATE_ENTRY(SPR_TROO, 5, 8, a_face_target, S_TROO_ATK3), /* S_TROO_ATK2 */ + STATE_ENTRY(SPR_TROO, 6, 6, a_troop_attack, + S_TROO_RUN1), /* S_TROO_ATK3 */ + STATE_ENTRY(SPR_TROO, 7, 2, NULL, S_TROO_PAIN2), /* S_TROO_PAIN */ + STATE_ENTRY(SPR_TROO, 7, 2, a_pain, S_TROO_RUN1), /* S_TROO_PAIN2 */ + STATE_ENTRY(SPR_TROO, 8, 8, NULL, S_TROO_DIE2), /* S_TROO_DIE1 */ + STATE_ENTRY(SPR_TROO, 9, 8, a_scream, S_TROO_DIE3), /* S_TROO_DIE2 */ + STATE_ENTRY(SPR_TROO, 10, 6, NULL, S_TROO_DIE4), /* S_TROO_DIE3 */ + STATE_ENTRY(SPR_TROO, 11, 6, a_fall, S_TROO_DIE5), /* S_TROO_DIE4 */ + STATE_ENTRY(SPR_TROO, 12, -1, NULL, S_NULL), /* S_TROO_DIE5 */ + STATE_ENTRY(SPR_TROO, 13, 5, NULL, S_TROO_XDIE2), /* S_TROO_XDIE1 */ + STATE_ENTRY(SPR_TROO, 14, 5, a_xscream, S_TROO_XDIE3), /* S_TROO_XDIE2 */ + STATE_ENTRY(SPR_TROO, 15, 5, NULL, S_TROO_XDIE4), /* S_TROO_XDIE3 */ + STATE_ENTRY(SPR_TROO, 16, 5, a_fall, S_TROO_XDIE5), /* S_TROO_XDIE4 */ + STATE_ENTRY(SPR_TROO, 17, 5, NULL, S_TROO_XDIE6), /* S_TROO_XDIE5 */ + STATE_ENTRY(SPR_TROO, 18, 5, NULL, S_TROO_XDIE7), /* S_TROO_XDIE6 */ + STATE_ENTRY(SPR_TROO, 19, 5, NULL, S_TROO_XDIE8), /* S_TROO_XDIE7 */ + STATE_ENTRY(SPR_TROO, 20, -1, NULL, S_NULL), /* S_TROO_XDIE8 */ + STATE_ENTRY(SPR_TROO, 12, 8, NULL, S_TROO_RAISE2), /* S_TROO_RAISE1 */ + STATE_ENTRY(SPR_TROO, 11, 8, NULL, S_TROO_RAISE3), /* S_TROO_RAISE2 */ + STATE_ENTRY(SPR_TROO, 10, 6, NULL, S_TROO_RAISE4), /* S_TROO_RAISE3 */ + STATE_ENTRY(SPR_TROO, 9, 6, NULL, S_TROO_RAISE5), /* S_TROO_RAISE4 */ + STATE_ENTRY(SPR_TROO, 8, 6, NULL, S_TROO_RUN1), /* S_TROO_RAISE5 */ + STATE_ENTRY(SPR_SARG, 0, 10, a_look, S_SARG_STND2), /* S_SARG_STND */ + STATE_ENTRY(SPR_SARG, 1, 10, a_look, S_SARG_STND), /* S_SARG_STND2 */ + STATE_ENTRY(SPR_SARG, 0, 2, a_chase, S_SARG_RUN2), /* S_SARG_RUN1 */ + STATE_ENTRY(SPR_SARG, 0, 2, a_chase, S_SARG_RUN3), /* S_SARG_RUN2 */ + STATE_ENTRY(SPR_SARG, 1, 2, a_chase, S_SARG_RUN4), /* S_SARG_RUN3 */ + STATE_ENTRY(SPR_SARG, 1, 2, a_chase, S_SARG_RUN5), /* S_SARG_RUN4 */ + STATE_ENTRY(SPR_SARG, 2, 2, a_chase, S_SARG_RUN6), /* S_SARG_RUN5 */ + STATE_ENTRY(SPR_SARG, 2, 2, a_chase, S_SARG_RUN7), /* S_SARG_RUN6 */ + STATE_ENTRY(SPR_SARG, 3, 2, a_chase, S_SARG_RUN8), /* S_SARG_RUN7 */ + STATE_ENTRY(SPR_SARG, 3, 2, a_chase, S_SARG_RUN1), /* S_SARG_RUN8 */ + STATE_ENTRY(SPR_SARG, 4, 8, a_face_target, S_SARG_ATK2), /* S_SARG_ATK1 */ + STATE_ENTRY(SPR_SARG, 5, 8, a_face_target, S_SARG_ATK3), /* S_SARG_ATK2 */ + STATE_ENTRY(SPR_SARG, 6, 8, a_sarg_attack, S_SARG_RUN1), /* S_SARG_ATK3 */ + STATE_ENTRY(SPR_SARG, 7, 2, NULL, S_SARG_PAIN2), /* S_SARG_PAIN */ + STATE_ENTRY(SPR_SARG, 7, 2, a_pain, S_SARG_RUN1), /* S_SARG_PAIN2 */ + STATE_ENTRY(SPR_SARG, 8, 8, NULL, S_SARG_DIE2), /* S_SARG_DIE1 */ + STATE_ENTRY(SPR_SARG, 9, 8, a_scream, S_SARG_DIE3), /* S_SARG_DIE2 */ + STATE_ENTRY(SPR_SARG, 10, 4, NULL, S_SARG_DIE4), /* S_SARG_DIE3 */ + STATE_ENTRY(SPR_SARG, 11, 4, a_fall, S_SARG_DIE5), /* S_SARG_DIE4 */ + STATE_ENTRY(SPR_SARG, 12, 4, NULL, S_SARG_DIE6), /* S_SARG_DIE5 */ + STATE_ENTRY(SPR_SARG, 13, -1, NULL, S_NULL), /* S_SARG_DIE6 */ + STATE_ENTRY(SPR_SARG, 13, 5, NULL, S_SARG_RAISE2), /* S_SARG_RAISE1 */ + STATE_ENTRY(SPR_SARG, 12, 5, NULL, S_SARG_RAISE3), /* S_SARG_RAISE2 */ + STATE_ENTRY(SPR_SARG, 11, 5, NULL, S_SARG_RAISE4), /* S_SARG_RAISE3 */ + STATE_ENTRY(SPR_SARG, 10, 5, NULL, S_SARG_RAISE5), /* S_SARG_RAISE4 */ + STATE_ENTRY(SPR_SARG, 9, 5, NULL, S_SARG_RAISE6), /* S_SARG_RAISE5 */ + STATE_ENTRY(SPR_SARG, 8, 5, NULL, S_SARG_RUN1), /* S_SARG_RAISE6 */ + STATE_ENTRY(SPR_HEAD, 0, 10, a_look, S_HEAD_STND), /* S_HEAD_STND */ + STATE_ENTRY(SPR_HEAD, 0, 3, a_chase, S_HEAD_RUN1), /* S_HEAD_RUN1 */ + STATE_ENTRY(SPR_HEAD, 1, 5, a_face_target, S_HEAD_ATK2), /* S_HEAD_ATK1 */ + STATE_ENTRY(SPR_HEAD, 2, 5, a_face_target, S_HEAD_ATK3), /* S_HEAD_ATK2 */ + STATE_ENTRY(SPR_HEAD, 32771, 5, a_head_attack, + S_HEAD_RUN1), /* S_HEAD_ATK3 */ + STATE_ENTRY(SPR_HEAD, 4, 3, NULL, S_HEAD_PAIN2), /* S_HEAD_PAIN */ + STATE_ENTRY(SPR_HEAD, 4, 3, a_pain, S_HEAD_PAIN3), /* S_HEAD_PAIN2 */ + STATE_ENTRY(SPR_HEAD, 5, 6, NULL, S_HEAD_RUN1), /* S_HEAD_PAIN3 */ + STATE_ENTRY(SPR_HEAD, 6, 8, NULL, S_HEAD_DIE2), /* S_HEAD_DIE1 */ + STATE_ENTRY(SPR_HEAD, 7, 8, a_scream, S_HEAD_DIE3), /* S_HEAD_DIE2 */ + STATE_ENTRY(SPR_HEAD, 8, 8, NULL, S_HEAD_DIE4), /* S_HEAD_DIE3 */ + STATE_ENTRY(SPR_HEAD, 9, 8, NULL, S_HEAD_DIE5), /* S_HEAD_DIE4 */ + STATE_ENTRY(SPR_HEAD, 10, 8, a_fall, S_HEAD_DIE6), /* S_HEAD_DIE5 */ + STATE_ENTRY(SPR_HEAD, 11, -1, NULL, S_NULL), /* S_HEAD_DIE6 */ + STATE_ENTRY(SPR_HEAD, 11, 8, NULL, S_HEAD_RAISE2), /* S_HEAD_RAISE1 */ + STATE_ENTRY(SPR_HEAD, 10, 8, NULL, S_HEAD_RAISE3), /* S_HEAD_RAISE2 */ + STATE_ENTRY(SPR_HEAD, 9, 8, NULL, S_HEAD_RAISE4), /* S_HEAD_RAISE3 */ + STATE_ENTRY(SPR_HEAD, 8, 8, NULL, S_HEAD_RAISE5), /* S_HEAD_RAISE4 */ + STATE_ENTRY(SPR_HEAD, 7, 8, NULL, S_HEAD_RAISE6), /* S_HEAD_RAISE5 */ + STATE_ENTRY(SPR_HEAD, 6, 8, NULL, S_HEAD_RUN1), /* S_HEAD_RAISE6 */ + STATE_ENTRY(SPR_BAL7, 32768, 4, NULL, S_BRBALL2), /* S_BRBALL1 */ + STATE_ENTRY(SPR_BAL7, 32769, 4, NULL, S_BRBALL1), /* S_BRBALL2 */ + STATE_ENTRY(SPR_BAL7, 32770, 6, NULL, S_BRBALLX2), /* S_BRBALLX1 */ + STATE_ENTRY(SPR_BAL7, 32771, 6, NULL, S_BRBALLX3), /* S_BRBALLX2 */ + STATE_ENTRY(SPR_BAL7, 32772, 6, NULL, S_NULL), /* S_BRBALLX3 */ + STATE_ENTRY(SPR_BOSS, 0, 10, a_look, S_BOSS_STND2), /* S_BOSS_STND */ + STATE_ENTRY(SPR_BOSS, 1, 10, a_look, S_BOSS_STND), /* S_BOSS_STND2 */ + STATE_ENTRY(SPR_BOSS, 0, 3, a_chase, S_BOSS_RUN2), /* S_BOSS_RUN1 */ + STATE_ENTRY(SPR_BOSS, 0, 3, a_chase, S_BOSS_RUN3), /* S_BOSS_RUN2 */ + STATE_ENTRY(SPR_BOSS, 1, 3, a_chase, S_BOSS_RUN4), /* S_BOSS_RUN3 */ + STATE_ENTRY(SPR_BOSS, 1, 3, a_chase, S_BOSS_RUN5), /* S_BOSS_RUN4 */ + STATE_ENTRY(SPR_BOSS, 2, 3, a_chase, S_BOSS_RUN6), /* S_BOSS_RUN5 */ + STATE_ENTRY(SPR_BOSS, 2, 3, a_chase, S_BOSS_RUN7), /* S_BOSS_RUN6 */ + STATE_ENTRY(SPR_BOSS, 3, 3, a_chase, S_BOSS_RUN8), /* S_BOSS_RUN7 */ + STATE_ENTRY(SPR_BOSS, 3, 3, a_chase, S_BOSS_RUN1), /* S_BOSS_RUN8 */ + STATE_ENTRY(SPR_BOSS, 4, 8, a_face_target, S_BOSS_ATK2), /* S_BOSS_ATK1 */ + STATE_ENTRY(SPR_BOSS, 5, 8, a_face_target, S_BOSS_ATK3), /* S_BOSS_ATK2 */ + STATE_ENTRY(SPR_BOSS, 6, 8, a_bruis_attack, + S_BOSS_RUN1), /* S_BOSS_ATK3 */ + STATE_ENTRY(SPR_BOSS, 7, 2, NULL, S_BOSS_PAIN2), /* S_BOSS_PAIN */ + STATE_ENTRY(SPR_BOSS, 7, 2, a_pain, S_BOSS_RUN1), /* S_BOSS_PAIN2 */ + STATE_ENTRY(SPR_BOSS, 8, 8, NULL, S_BOSS_DIE2), /* S_BOSS_DIE1 */ + STATE_ENTRY(SPR_BOSS, 9, 8, a_scream, S_BOSS_DIE3), /* S_BOSS_DIE2 */ + STATE_ENTRY(SPR_BOSS, 10, 8, NULL, S_BOSS_DIE4), /* S_BOSS_DIE3 */ + STATE_ENTRY(SPR_BOSS, 11, 8, a_fall, S_BOSS_DIE5), /* S_BOSS_DIE4 */ + STATE_ENTRY(SPR_BOSS, 12, 8, NULL, S_BOSS_DIE6), /* S_BOSS_DIE5 */ + STATE_ENTRY(SPR_BOSS, 13, 8, NULL, S_BOSS_DIE7), /* S_BOSS_DIE6 */ + STATE_ENTRY(SPR_BOSS, 14, -1, a_boss_death, S_NULL), /* S_BOSS_DIE7 */ + STATE_ENTRY(SPR_BOSS, 14, 8, NULL, S_BOSS_RAISE2), /* S_BOSS_RAISE1 */ + STATE_ENTRY(SPR_BOSS, 13, 8, NULL, S_BOSS_RAISE3), /* S_BOSS_RAISE2 */ + STATE_ENTRY(SPR_BOSS, 12, 8, NULL, S_BOSS_RAISE4), /* S_BOSS_RAISE3 */ + STATE_ENTRY(SPR_BOSS, 11, 8, NULL, S_BOSS_RAISE5), /* S_BOSS_RAISE4 */ + STATE_ENTRY(SPR_BOSS, 10, 8, NULL, S_BOSS_RAISE6), /* S_BOSS_RAISE5 */ + STATE_ENTRY(SPR_BOSS, 9, 8, NULL, S_BOSS_RAISE7), /* S_BOSS_RAISE6 */ + STATE_ENTRY(SPR_BOSS, 8, 8, NULL, S_BOSS_RUN1), /* S_BOSS_RAISE7 */ + STATE_ENTRY(SPR_BOS2, 0, 10, a_look, S_BOS2_STND2), /* S_BOS2_STND */ + STATE_ENTRY(SPR_BOS2, 1, 10, a_look, S_BOS2_STND), /* S_BOS2_STND2 */ + STATE_ENTRY(SPR_BOS2, 0, 3, a_chase, S_BOS2_RUN2), /* S_BOS2_RUN1 */ + STATE_ENTRY(SPR_BOS2, 0, 3, a_chase, S_BOS2_RUN3), /* S_BOS2_RUN2 */ + STATE_ENTRY(SPR_BOS2, 1, 3, a_chase, S_BOS2_RUN4), /* S_BOS2_RUN3 */ + STATE_ENTRY(SPR_BOS2, 1, 3, a_chase, S_BOS2_RUN5), /* S_BOS2_RUN4 */ + STATE_ENTRY(SPR_BOS2, 2, 3, a_chase, S_BOS2_RUN6), /* S_BOS2_RUN5 */ + STATE_ENTRY(SPR_BOS2, 2, 3, a_chase, S_BOS2_RUN7), /* S_BOS2_RUN6 */ + STATE_ENTRY(SPR_BOS2, 3, 3, a_chase, S_BOS2_RUN8), /* S_BOS2_RUN7 */ + STATE_ENTRY(SPR_BOS2, 3, 3, a_chase, S_BOS2_RUN1), /* S_BOS2_RUN8 */ + STATE_ENTRY(SPR_BOS2, 4, 8, a_face_target, S_BOS2_ATK2), /* S_BOS2_ATK1 */ + STATE_ENTRY(SPR_BOS2, 5, 8, a_face_target, S_BOS2_ATK3), /* S_BOS2_ATK2 */ + STATE_ENTRY(SPR_BOS2, 6, 8, a_bruis_attack, + S_BOS2_RUN1), /* S_BOS2_ATK3 */ + STATE_ENTRY(SPR_BOS2, 7, 2, NULL, S_BOS2_PAIN2), /* S_BOS2_PAIN */ + STATE_ENTRY(SPR_BOS2, 7, 2, a_pain, S_BOS2_RUN1), /* S_BOS2_PAIN2 */ + STATE_ENTRY(SPR_BOS2, 8, 8, NULL, S_BOS2_DIE2), /* S_BOS2_DIE1 */ + STATE_ENTRY(SPR_BOS2, 9, 8, a_scream, S_BOS2_DIE3), /* S_BOS2_DIE2 */ + STATE_ENTRY(SPR_BOS2, 10, 8, NULL, S_BOS2_DIE4), /* S_BOS2_DIE3 */ + STATE_ENTRY(SPR_BOS2, 11, 8, a_fall, S_BOS2_DIE5), /* S_BOS2_DIE4 */ + STATE_ENTRY(SPR_BOS2, 12, 8, NULL, S_BOS2_DIE6), /* S_BOS2_DIE5 */ + STATE_ENTRY(SPR_BOS2, 13, 8, NULL, S_BOS2_DIE7), /* S_BOS2_DIE6 */ + STATE_ENTRY(SPR_BOS2, 14, -1, NULL, S_NULL), /* S_BOS2_DIE7 */ + STATE_ENTRY(SPR_BOS2, 14, 8, NULL, S_BOS2_RAISE2), /* S_BOS2_RAISE1 */ + STATE_ENTRY(SPR_BOS2, 13, 8, NULL, S_BOS2_RAISE3), /* S_BOS2_RAISE2 */ + STATE_ENTRY(SPR_BOS2, 12, 8, NULL, S_BOS2_RAISE4), /* S_BOS2_RAISE3 */ + STATE_ENTRY(SPR_BOS2, 11, 8, NULL, S_BOS2_RAISE5), /* S_BOS2_RAISE4 */ + STATE_ENTRY(SPR_BOS2, 10, 8, NULL, S_BOS2_RAISE6), /* S_BOS2_RAISE5 */ + STATE_ENTRY(SPR_BOS2, 9, 8, NULL, S_BOS2_RAISE7), /* S_BOS2_RAISE6 */ + STATE_ENTRY(SPR_BOS2, 8, 8, NULL, S_BOS2_RUN1), /* S_BOS2_RAISE7 */ + STATE_ENTRY(SPR_SKUL, 32768, 10, a_look, + S_SKULL_STND2), /* S_SKULL_STND */ + STATE_ENTRY(SPR_SKUL, 32769, 10, a_look, + S_SKULL_STND), /* S_SKULL_STND2 */ + STATE_ENTRY(SPR_SKUL, 32768, 6, a_chase, + S_SKULL_RUN2), /* S_SKULL_RUN1 */ + STATE_ENTRY(SPR_SKUL, 32769, 6, a_chase, + S_SKULL_RUN1), /* S_SKULL_RUN2 */ + STATE_ENTRY(SPR_SKUL, 32770, 10, a_face_target, + S_SKULL_ATK2), /* S_SKULL_ATK1 */ + STATE_ENTRY(SPR_SKUL, 32771, 4, a_skull_attack, + S_SKULL_ATK3), /* S_SKULL_ATK2 */ + STATE_ENTRY(SPR_SKUL, 32770, 4, NULL, S_SKULL_ATK4), /* S_SKULL_ATK3 */ + STATE_ENTRY(SPR_SKUL, 32771, 4, NULL, S_SKULL_ATK3), /* S_SKULL_ATK4 */ + STATE_ENTRY(SPR_SKUL, 32772, 3, NULL, S_SKULL_PAIN2), /* S_SKULL_PAIN */ + STATE_ENTRY(SPR_SKUL, 32772, 3, a_pain, S_SKULL_RUN1), /* S_SKULL_PAIN2 */ + STATE_ENTRY(SPR_SKUL, 32773, 6, NULL, S_SKULL_DIE2), /* S_SKULL_DIE1 */ + STATE_ENTRY(SPR_SKUL, 32774, 6, a_scream, + S_SKULL_DIE3), /* S_SKULL_DIE2 */ + STATE_ENTRY(SPR_SKUL, 32775, 6, NULL, S_SKULL_DIE4), /* S_SKULL_DIE3 */ + STATE_ENTRY(SPR_SKUL, 32776, 6, a_fall, S_SKULL_DIE5), /* S_SKULL_DIE4 */ + STATE_ENTRY(SPR_SKUL, 9, 6, NULL, S_SKULL_DIE6), /* S_SKULL_DIE5 */ + STATE_ENTRY(SPR_SKUL, 10, 6, NULL, S_NULL), /* S_SKULL_DIE6 */ + STATE_ENTRY(SPR_SPID, 0, 10, a_look, S_SPID_STND2), /* S_SPID_STND */ + STATE_ENTRY(SPR_SPID, 1, 10, a_look, S_SPID_STND), /* S_SPID_STND2 */ + STATE_ENTRY(SPR_SPID, 0, 3, a_metal, S_SPID_RUN2), /* S_SPID_RUN1 */ + STATE_ENTRY(SPR_SPID, 0, 3, a_chase, S_SPID_RUN3), /* S_SPID_RUN2 */ + STATE_ENTRY(SPR_SPID, 1, 3, a_chase, S_SPID_RUN4), /* S_SPID_RUN3 */ + STATE_ENTRY(SPR_SPID, 1, 3, a_chase, S_SPID_RUN5), /* S_SPID_RUN4 */ + STATE_ENTRY(SPR_SPID, 2, 3, a_metal, S_SPID_RUN6), /* S_SPID_RUN5 */ + STATE_ENTRY(SPR_SPID, 2, 3, a_chase, S_SPID_RUN7), /* S_SPID_RUN6 */ + STATE_ENTRY(SPR_SPID, 3, 3, a_chase, S_SPID_RUN8), /* S_SPID_RUN7 */ + STATE_ENTRY(SPR_SPID, 3, 3, a_chase, S_SPID_RUN9), /* S_SPID_RUN8 */ + STATE_ENTRY(SPR_SPID, 4, 3, a_metal, S_SPID_RUN10), /* S_SPID_RUN9 */ + STATE_ENTRY(SPR_SPID, 4, 3, a_chase, S_SPID_RUN11), /* S_SPID_RUN10 */ + STATE_ENTRY(SPR_SPID, 5, 3, a_chase, S_SPID_RUN12), /* S_SPID_RUN11 */ + STATE_ENTRY(SPR_SPID, 5, 3, a_chase, S_SPID_RUN1), /* S_SPID_RUN12 */ + STATE_ENTRY(SPR_SPID, 32768, 20, a_face_target, + S_SPID_ATK2), /* S_SPID_ATK1 */ + STATE_ENTRY(SPR_SPID, 32774, 4, a_s_pos_attack, + S_SPID_ATK3), /* S_SPID_ATK2 */ + STATE_ENTRY(SPR_SPID, 32775, 4, a_s_pos_attack, + S_SPID_ATK4), /* S_SPID_ATK3 */ + STATE_ENTRY(SPR_SPID, 32775, 1, a_spid_refire, + S_SPID_ATK2), /* S_SPID_ATK4 */ + STATE_ENTRY(SPR_SPID, 8, 3, NULL, S_SPID_PAIN2), /* S_SPID_PAIN */ + STATE_ENTRY(SPR_SPID, 8, 3, a_pain, S_SPID_RUN1), /* S_SPID_PAIN2 */ + STATE_ENTRY(SPR_SPID, 9, 20, a_scream, S_SPID_DIE2), /* S_SPID_DIE1 */ + STATE_ENTRY(SPR_SPID, 10, 10, a_fall, S_SPID_DIE3), /* S_SPID_DIE2 */ + STATE_ENTRY(SPR_SPID, 11, 10, NULL, S_SPID_DIE4), /* S_SPID_DIE3 */ + STATE_ENTRY(SPR_SPID, 12, 10, NULL, S_SPID_DIE5), /* S_SPID_DIE4 */ + STATE_ENTRY(SPR_SPID, 13, 10, NULL, S_SPID_DIE6), /* S_SPID_DIE5 */ + STATE_ENTRY(SPR_SPID, 14, 10, NULL, S_SPID_DIE7), /* S_SPID_DIE6 */ + STATE_ENTRY(SPR_SPID, 15, 10, NULL, S_SPID_DIE8), /* S_SPID_DIE7 */ + STATE_ENTRY(SPR_SPID, 16, 10, NULL, S_SPID_DIE9), /* S_SPID_DIE8 */ + STATE_ENTRY(SPR_SPID, 17, 10, NULL, S_SPID_DIE10), /* S_SPID_DIE9 */ + STATE_ENTRY(SPR_SPID, 18, 30, NULL, S_SPID_DIE11), /* S_SPID_DIE10 */ + STATE_ENTRY(SPR_SPID, 18, -1, a_boss_death, S_NULL), /* S_SPID_DIE11 */ + STATE_ENTRY(SPR_BSPI, 0, 10, a_look, S_BSPI_STND2), /* S_BSPI_STND */ + STATE_ENTRY(SPR_BSPI, 1, 10, a_look, S_BSPI_STND), /* S_BSPI_STND2 */ + STATE_ENTRY(SPR_BSPI, 0, 20, NULL, S_BSPI_RUN1), /* S_BSPI_SIGHT */ + STATE_ENTRY(SPR_BSPI, 0, 3, a_baby_metal, S_BSPI_RUN2), /* S_BSPI_RUN1 */ + STATE_ENTRY(SPR_BSPI, 0, 3, a_chase, S_BSPI_RUN3), /* S_BSPI_RUN2 */ + STATE_ENTRY(SPR_BSPI, 1, 3, a_chase, S_BSPI_RUN4), /* S_BSPI_RUN3 */ + STATE_ENTRY(SPR_BSPI, 1, 3, a_chase, S_BSPI_RUN5), /* S_BSPI_RUN4 */ + STATE_ENTRY(SPR_BSPI, 2, 3, a_chase, S_BSPI_RUN6), /* S_BSPI_RUN5 */ + STATE_ENTRY(SPR_BSPI, 2, 3, a_chase, S_BSPI_RUN7), /* S_BSPI_RUN6 */ + STATE_ENTRY(SPR_BSPI, 3, 3, a_baby_metal, S_BSPI_RUN8), /* S_BSPI_RUN7 */ + STATE_ENTRY(SPR_BSPI, 3, 3, a_chase, S_BSPI_RUN9), /* S_BSPI_RUN8 */ + STATE_ENTRY(SPR_BSPI, 4, 3, a_chase, S_BSPI_RUN10), /* S_BSPI_RUN9 */ + STATE_ENTRY(SPR_BSPI, 4, 3, a_chase, S_BSPI_RUN11), /* S_BSPI_RUN10 */ + STATE_ENTRY(SPR_BSPI, 5, 3, a_chase, S_BSPI_RUN12), /* S_BSPI_RUN11 */ + STATE_ENTRY(SPR_BSPI, 5, 3, a_chase, S_BSPI_RUN1), /* S_BSPI_RUN12 */ + STATE_ENTRY(SPR_BSPI, 32768, 20, a_face_target, + S_BSPI_ATK2), /* S_BSPI_ATK1 */ + STATE_ENTRY(SPR_BSPI, 32774, 4, a_bspi_attack, + S_BSPI_ATK3), /* S_BSPI_ATK2 */ + STATE_ENTRY(SPR_BSPI, 32775, 4, NULL, S_BSPI_ATK4), /* S_BSPI_ATK3 */ + STATE_ENTRY(SPR_BSPI, 32775, 1, a_spid_refire, + S_BSPI_ATK2), /* S_BSPI_ATK4 */ + STATE_ENTRY(SPR_BSPI, 8, 3, NULL, S_BSPI_PAIN2), /* S_BSPI_PAIN */ + STATE_ENTRY(SPR_BSPI, 8, 3, a_pain, S_BSPI_RUN1), /* S_BSPI_PAIN2 */ + STATE_ENTRY(SPR_BSPI, 9, 20, a_scream, S_BSPI_DIE2), /* S_BSPI_DIE1 */ + STATE_ENTRY(SPR_BSPI, 10, 7, a_fall, S_BSPI_DIE3), /* S_BSPI_DIE2 */ + STATE_ENTRY(SPR_BSPI, 11, 7, NULL, S_BSPI_DIE4), /* S_BSPI_DIE3 */ + STATE_ENTRY(SPR_BSPI, 12, 7, NULL, S_BSPI_DIE5), /* S_BSPI_DIE4 */ + STATE_ENTRY(SPR_BSPI, 13, 7, NULL, S_BSPI_DIE6), /* S_BSPI_DIE5 */ + STATE_ENTRY(SPR_BSPI, 14, 7, NULL, S_BSPI_DIE7), /* S_BSPI_DIE6 */ + STATE_ENTRY(SPR_BSPI, 15, -1, a_boss_death, S_NULL), /* S_BSPI_DIE7 */ + STATE_ENTRY(SPR_BSPI, 15, 5, NULL, S_BSPI_RAISE2), /* S_BSPI_RAISE1 */ + STATE_ENTRY(SPR_BSPI, 14, 5, NULL, S_BSPI_RAISE3), /* S_BSPI_RAISE2 */ + STATE_ENTRY(SPR_BSPI, 13, 5, NULL, S_BSPI_RAISE4), /* S_BSPI_RAISE3 */ + STATE_ENTRY(SPR_BSPI, 12, 5, NULL, S_BSPI_RAISE5), /* S_BSPI_RAISE4 */ + STATE_ENTRY(SPR_BSPI, 11, 5, NULL, S_BSPI_RAISE6), /* S_BSPI_RAISE5 */ + STATE_ENTRY(SPR_BSPI, 10, 5, NULL, S_BSPI_RAISE7), /* S_BSPI_RAISE6 */ + STATE_ENTRY(SPR_BSPI, 9, 5, NULL, S_BSPI_RUN1), /* S_BSPI_RAISE7 */ + STATE_ENTRY(SPR_APLS, 32768, 5, NULL, S_ARACH_PLAZ2), /* S_ARACH_PLAZ */ + STATE_ENTRY(SPR_APLS, 32769, 5, NULL, S_ARACH_PLAZ), /* S_ARACH_PLAZ2 */ + STATE_ENTRY(SPR_APBX, 32768, 5, NULL, S_ARACH_PLEX2), /* S_ARACH_PLEX */ + STATE_ENTRY(SPR_APBX, 32769, 5, NULL, S_ARACH_PLEX3), /* S_ARACH_PLEX2 */ + STATE_ENTRY(SPR_APBX, 32770, 5, NULL, S_ARACH_PLEX4), /* S_ARACH_PLEX3 */ + STATE_ENTRY(SPR_APBX, 32771, 5, NULL, S_ARACH_PLEX5), /* S_ARACH_PLEX4 */ + STATE_ENTRY(SPR_APBX, 32772, 5, NULL, S_NULL), /* S_ARACH_PLEX5 */ + STATE_ENTRY(SPR_CYBR, 0, 10, a_look, S_CYBER_STND2), /* S_CYBER_STND */ + STATE_ENTRY(SPR_CYBR, 1, 10, a_look, S_CYBER_STND), /* S_CYBER_STND2 */ + STATE_ENTRY(SPR_CYBR, 0, 3, a_hoof, S_CYBER_RUN2), /* S_CYBER_RUN1 */ + STATE_ENTRY(SPR_CYBR, 0, 3, a_chase, S_CYBER_RUN3), /* S_CYBER_RUN2 */ + STATE_ENTRY(SPR_CYBR, 1, 3, a_chase, S_CYBER_RUN4), /* S_CYBER_RUN3 */ + STATE_ENTRY(SPR_CYBR, 1, 3, a_chase, S_CYBER_RUN5), /* S_CYBER_RUN4 */ + STATE_ENTRY(SPR_CYBR, 2, 3, a_chase, S_CYBER_RUN6), /* S_CYBER_RUN5 */ + STATE_ENTRY(SPR_CYBR, 2, 3, a_chase, S_CYBER_RUN7), /* S_CYBER_RUN6 */ + STATE_ENTRY(SPR_CYBR, 3, 3, a_metal, S_CYBER_RUN8), /* S_CYBER_RUN7 */ + STATE_ENTRY(SPR_CYBR, 3, 3, a_chase, S_CYBER_RUN1), /* S_CYBER_RUN8 */ + STATE_ENTRY(SPR_CYBR, 4, 6, a_face_target, + S_CYBER_ATK2), /* S_CYBER_ATK1 */ + STATE_ENTRY(SPR_CYBR, 5, 12, a_cyber_attack, + S_CYBER_ATK3), /* S_CYBER_ATK2 */ + STATE_ENTRY(SPR_CYBR, 4, 12, a_face_target, + S_CYBER_ATK4), /* S_CYBER_ATK3 */ + STATE_ENTRY(SPR_CYBR, 5, 12, a_cyber_attack, + S_CYBER_ATK5), /* S_CYBER_ATK4 */ + STATE_ENTRY(SPR_CYBR, 4, 12, a_face_target, + S_CYBER_ATK6), /* S_CYBER_ATK5 */ + STATE_ENTRY(SPR_CYBR, 5, 12, a_cyber_attack, + S_CYBER_RUN1), /* S_CYBER_ATK6 */ + STATE_ENTRY(SPR_CYBR, 6, 10, a_pain, S_CYBER_RUN1), /* S_CYBER_PAIN */ + STATE_ENTRY(SPR_CYBR, 7, 10, NULL, S_CYBER_DIE2), /* S_CYBER_DIE1 */ + STATE_ENTRY(SPR_CYBR, 8, 10, a_scream, S_CYBER_DIE3), /* S_CYBER_DIE2 */ + STATE_ENTRY(SPR_CYBR, 9, 10, NULL, S_CYBER_DIE4), /* S_CYBER_DIE3 */ + STATE_ENTRY(SPR_CYBR, 10, 10, NULL, S_CYBER_DIE5), /* S_CYBER_DIE4 */ + STATE_ENTRY(SPR_CYBR, 11, 10, NULL, S_CYBER_DIE6), /* S_CYBER_DIE5 */ + STATE_ENTRY(SPR_CYBR, 12, 10, a_fall, S_CYBER_DIE7), /* S_CYBER_DIE6 */ + STATE_ENTRY(SPR_CYBR, 13, 10, NULL, S_CYBER_DIE8), /* S_CYBER_DIE7 */ + STATE_ENTRY(SPR_CYBR, 14, 10, NULL, S_CYBER_DIE9), /* S_CYBER_DIE8 */ + STATE_ENTRY(SPR_CYBR, 15, 30, NULL, S_CYBER_DIE10), /* S_CYBER_DIE9 */ + STATE_ENTRY(SPR_CYBR, 15, -1, a_boss_death, S_NULL), /* S_CYBER_DIE10 */ + STATE_ENTRY(SPR_PAIN, 0, 10, a_look, S_PAIN_STND), /* S_PAIN_STND */ + STATE_ENTRY(SPR_PAIN, 0, 3, a_chase, S_PAIN_RUN2), /* S_PAIN_RUN1 */ + STATE_ENTRY(SPR_PAIN, 0, 3, a_chase, S_PAIN_RUN3), /* S_PAIN_RUN2 */ + STATE_ENTRY(SPR_PAIN, 1, 3, a_chase, S_PAIN_RUN4), /* S_PAIN_RUN3 */ + STATE_ENTRY(SPR_PAIN, 1, 3, a_chase, S_PAIN_RUN5), /* S_PAIN_RUN4 */ + STATE_ENTRY(SPR_PAIN, 2, 3, a_chase, S_PAIN_RUN6), /* S_PAIN_RUN5 */ + STATE_ENTRY(SPR_PAIN, 2, 3, a_chase, S_PAIN_RUN1), /* S_PAIN_RUN6 */ + STATE_ENTRY(SPR_PAIN, 3, 5, a_face_target, S_PAIN_ATK2), /* S_PAIN_ATK1 */ + STATE_ENTRY(SPR_PAIN, 4, 5, a_face_target, S_PAIN_ATK3), /* S_PAIN_ATK2 */ + STATE_ENTRY(SPR_PAIN, 32773, 5, a_face_target, + S_PAIN_ATK4), /* S_PAIN_ATK3 */ + STATE_ENTRY(SPR_PAIN, 32773, 0, a_pain_attack, + S_PAIN_RUN1), /* S_PAIN_ATK4 */ + STATE_ENTRY(SPR_PAIN, 6, 6, NULL, S_PAIN_PAIN2), /* S_PAIN_PAIN */ + STATE_ENTRY(SPR_PAIN, 6, 6, a_pain, S_PAIN_RUN1), /* S_PAIN_PAIN2 */ + STATE_ENTRY(SPR_PAIN, 32775, 8, NULL, S_PAIN_DIE2), /* S_PAIN_DIE1 */ + STATE_ENTRY(SPR_PAIN, 32776, 8, a_scream, S_PAIN_DIE3), /* S_PAIN_DIE2 */ + STATE_ENTRY(SPR_PAIN, 32777, 8, NULL, S_PAIN_DIE4), /* S_PAIN_DIE3 */ + STATE_ENTRY(SPR_PAIN, 32778, 8, NULL, S_PAIN_DIE5), /* S_PAIN_DIE4 */ + STATE_ENTRY(SPR_PAIN, 32779, 8, a_pain_die, + S_PAIN_DIE6), /* S_PAIN_DIE5 */ + STATE_ENTRY(SPR_PAIN, 32780, 8, NULL, S_NULL), /* S_PAIN_DIE6 */ + STATE_ENTRY(SPR_PAIN, 12, 8, NULL, S_PAIN_RAISE2), /* S_PAIN_RAISE1 */ + STATE_ENTRY(SPR_PAIN, 11, 8, NULL, S_PAIN_RAISE3), /* S_PAIN_RAISE2 */ + STATE_ENTRY(SPR_PAIN, 10, 8, NULL, S_PAIN_RAISE4), /* S_PAIN_RAISE3 */ + STATE_ENTRY(SPR_PAIN, 9, 8, NULL, S_PAIN_RAISE5), /* S_PAIN_RAISE4 */ + STATE_ENTRY(SPR_PAIN, 8, 8, NULL, S_PAIN_RAISE6), /* S_PAIN_RAISE5 */ + STATE_ENTRY(SPR_PAIN, 7, 8, NULL, S_PAIN_RUN1), /* S_PAIN_RAISE6 */ + STATE_ENTRY(SPR_SSWV, 0, 10, a_look, S_SSWV_STND2), /* S_SSWV_STND */ + STATE_ENTRY(SPR_SSWV, 1, 10, a_look, S_SSWV_STND), /* S_SSWV_STND2 */ + STATE_ENTRY(SPR_SSWV, 0, 3, a_chase, S_SSWV_RUN2), /* S_SSWV_RUN1 */ + STATE_ENTRY(SPR_SSWV, 0, 3, a_chase, S_SSWV_RUN3), /* S_SSWV_RUN2 */ + STATE_ENTRY(SPR_SSWV, 1, 3, a_chase, S_SSWV_RUN4), /* S_SSWV_RUN3 */ + STATE_ENTRY(SPR_SSWV, 1, 3, a_chase, S_SSWV_RUN5), /* S_SSWV_RUN4 */ + STATE_ENTRY(SPR_SSWV, 2, 3, a_chase, S_SSWV_RUN6), /* S_SSWV_RUN5 */ + STATE_ENTRY(SPR_SSWV, 2, 3, a_chase, S_SSWV_RUN7), /* S_SSWV_RUN6 */ + STATE_ENTRY(SPR_SSWV, 3, 3, a_chase, S_SSWV_RUN8), /* S_SSWV_RUN7 */ + STATE_ENTRY(SPR_SSWV, 3, 3, a_chase, S_SSWV_RUN1), /* S_SSWV_RUN8 */ + STATE_ENTRY(SPR_SSWV, 4, 10, a_face_target, + S_SSWV_ATK2), /* S_SSWV_ATK1 */ + STATE_ENTRY(SPR_SSWV, 5, 10, a_face_target, + S_SSWV_ATK3), /* S_SSWV_ATK2 */ + STATE_ENTRY(SPR_SSWV, 32774, 4, a_c_pos_attack, + S_SSWV_ATK4), /* S_SSWV_ATK3 */ + STATE_ENTRY(SPR_SSWV, 5, 6, a_face_target, S_SSWV_ATK5) , /* S_SSWV_ATK4 */ + STATE_ENTRY(SPR_SSWV, 32774, 4, a_c_pos_attack, + S_SSWV_ATK6), /* S_SSWV_ATK5 */ + STATE_ENTRY(SPR_SSWV, 5, 1, a_c_pos_refire, + S_SSWV_ATK2), /* S_SSWV_ATK6 */ + STATE_ENTRY(SPR_SSWV, 7, 3, NULL, S_SSWV_PAIN2), /* S_SSWV_PAIN */ + STATE_ENTRY(SPR_SSWV, 7, 3, a_pain, S_SSWV_RUN1), /* S_SSWV_PAIN2 */ + STATE_ENTRY(SPR_SSWV, 8, 5, NULL, S_SSWV_DIE2), /* S_SSWV_DIE1 */ + STATE_ENTRY(SPR_SSWV, 9, 5, a_scream, S_SSWV_DIE3), /* S_SSWV_DIE2 */ + STATE_ENTRY(SPR_SSWV, 10, 5, a_fall, S_SSWV_DIE4), /* S_SSWV_DIE3 */ + STATE_ENTRY(SPR_SSWV, 11, 5, NULL, S_SSWV_DIE5), /* S_SSWV_DIE4 */ + STATE_ENTRY(SPR_SSWV, 12, -1, NULL, S_NULL), /* S_SSWV_DIE5 */ + STATE_ENTRY(SPR_SSWV, 13, 5, NULL, S_SSWV_XDIE2), /* S_SSWV_XDIE1 */ + STATE_ENTRY(SPR_SSWV, 14, 5, a_xscream, S_SSWV_XDIE3), /* S_SSWV_XDIE2 */ + STATE_ENTRY(SPR_SSWV, 15, 5, a_fall, S_SSWV_XDIE4), /* S_SSWV_XDIE3 */ + STATE_ENTRY(SPR_SSWV, 16, 5, NULL, S_SSWV_XDIE5), /* S_SSWV_XDIE4 */ + STATE_ENTRY(SPR_SSWV, 17, 5, NULL, S_SSWV_XDIE6), /* S_SSWV_XDIE5 */ + STATE_ENTRY(SPR_SSWV, 18, 5, NULL, S_SSWV_XDIE7), /* S_SSWV_XDIE6 */ + STATE_ENTRY(SPR_SSWV, 19, 5, NULL, S_SSWV_XDIE8), /* S_SSWV_XDIE7 */ + STATE_ENTRY(SPR_SSWV, 20, 5, NULL, S_SSWV_XDIE9), /* S_SSWV_XDIE8 */ + STATE_ENTRY(SPR_SSWV, 21, -1, NULL, S_NULL), /* S_SSWV_XDIE9 */ + STATE_ENTRY(SPR_SSWV, 12, 5, NULL, S_SSWV_RAISE2), /* S_SSWV_RAISE1 */ + STATE_ENTRY(SPR_SSWV, 11, 5, NULL, S_SSWV_RAISE3), /* S_SSWV_RAISE2 */ + STATE_ENTRY(SPR_SSWV, 10, 5, NULL, S_SSWV_RAISE4), /* S_SSWV_RAISE3 */ + STATE_ENTRY(SPR_SSWV, 9, 5, NULL, S_SSWV_RAISE5), /* S_SSWV_RAISE4 */ + STATE_ENTRY(SPR_SSWV, 8, 5, NULL, S_SSWV_RUN1), /* S_SSWV_RAISE5 */ + STATE_ENTRY(SPR_KEEN, 0, -1, NULL, S_KEENSTND), /* S_KEENSTND */ + STATE_ENTRY(SPR_KEEN, 0, 6, NULL, S_COMMKEEN2), /* S_COMMKEEN */ + STATE_ENTRY(SPR_KEEN, 1, 6, NULL, S_COMMKEEN3), /* S_COMMKEEN2 */ + STATE_ENTRY(SPR_KEEN, 2, 6, a_scream, S_COMMKEEN4), /* S_COMMKEEN3 */ + STATE_ENTRY(SPR_KEEN, 3, 6, NULL, S_COMMKEEN5), /* S_COMMKEEN4 */ + STATE_ENTRY(SPR_KEEN, 4, 6, NULL, S_COMMKEEN6), /* S_COMMKEEN5 */ + STATE_ENTRY(SPR_KEEN, 5, 6, NULL, S_COMMKEEN7), /* S_COMMKEEN6 */ + STATE_ENTRY(SPR_KEEN, 6, 6, NULL, S_COMMKEEN8), /* S_COMMKEEN7 */ + STATE_ENTRY(SPR_KEEN, 7, 6, NULL, S_COMMKEEN9), /* S_COMMKEEN8 */ + STATE_ENTRY(SPR_KEEN, 8, 6, NULL, S_COMMKEEN10), /* S_COMMKEEN9 */ + STATE_ENTRY(SPR_KEEN, 9, 6, NULL, S_COMMKEEN11), /* S_COMMKEEN10 */ + STATE_ENTRY(SPR_KEEN, 10, 6, a_keen_die, S_COMMKEEN12), /* S_COMMKEEN11 */ + STATE_ENTRY(SPR_KEEN, 11, -1, NULL, S_NULL), /* S_COMMKEEN12 */ + STATE_ENTRY(SPR_KEEN, 12, 4, NULL, S_KEENPAIN2), /* S_KEENPAIN */ + STATE_ENTRY(SPR_KEEN, 12, 8, a_pain, S_KEENSTND), /* S_KEENPAIN2 */ + STATE_ENTRY(SPR_BBRN, 0, -1, NULL, S_NULL), /* S_BRAIN */ + STATE_ENTRY(SPR_BBRN, 1, 36, a_brain_pain, S_BRAIN), /* S_BRAIN_PAIN */ + STATE_ENTRY(SPR_BBRN, 0, 100, a_brain_scream, + S_BRAIN_DIE2), /* S_BRAIN_DIE1 */ + STATE_ENTRY(SPR_BBRN, 0, 10, NULL, S_BRAIN_DIE3), /* S_BRAIN_DIE2 */ + STATE_ENTRY(SPR_BBRN, 0, 10, NULL, S_BRAIN_DIE4), /* S_BRAIN_DIE3 */ + STATE_ENTRY(SPR_BBRN, 0, -1, a_brain_die, S_NULL), /* S_BRAIN_DIE4 */ + STATE_ENTRY(SPR_SSWV, 0, 10, a_look, S_BRAINEYE), /* S_BRAINEYE */ + STATE_ENTRY(SPR_SSWV, 0, 181, + a_brain_awake, S_BRAINEYE1), /* S_BRAINEYESEE */ + STATE_ENTRY(SPR_SSWV, 0, 150, + a_brain_split, S_BRAINEYE1), /* S_BRAINEYE1 */ + STATE_ENTRY(SPR_BOSF, 32768, 3, a_spawn_sound, S_SPAWN2), /* S_SPAWN1 */ + STATE_ENTRY(SPR_BOSF, 32769, 3, a_spawn_fly, S_SPAWN3), /* S_SPAWN2 */ + STATE_ENTRY(SPR_BOSF, 32770, 3, a_spawn_fly, S_SPAWN4), /* S_SPAWN3 */ + STATE_ENTRY(SPR_BOSF, 32771, 3, a_spawn_fly, S_SPAWN1), /* S_SPAWN4 */ + STATE_ENTRY(SPR_FIRE, 32768, 4, a_fire, S_SPAWNFIRE2), /* S_SPAWNFIRE1 */ + STATE_ENTRY(SPR_FIRE, 32769, 4, a_fire, S_SPAWNFIRE3), /* S_SPAWNFIRE2 */ + STATE_ENTRY(SPR_FIRE, 32770, 4, a_fire, S_SPAWNFIRE4), /* S_SPAWNFIRE3 */ + STATE_ENTRY(SPR_FIRE, 32771, 4, a_fire, S_SPAWNFIRE5), /* S_SPAWNFIRE4 */ + STATE_ENTRY(SPR_FIRE, 32772, 4, a_fire, S_SPAWNFIRE6), /* S_SPAWNFIRE5 */ + STATE_ENTRY(SPR_FIRE, 32773, 4, a_fire, S_SPAWNFIRE7), /* S_SPAWNFIRE6 */ + STATE_ENTRY(SPR_FIRE, 32774, 4, a_fire, S_SPAWNFIRE8), /* S_SPAWNFIRE7 */ + STATE_ENTRY(SPR_FIRE, 32775, 4, a_fire, S_NULL), /* S_SPAWNFIRE8 */ + STATE_ENTRY(SPR_MISL, 32769, 10, NULL, + S_BRAINEXPLODE2), /* S_BRAINEXPLODE1 */ + STATE_ENTRY(SPR_MISL, 32770, 10, NULL, + S_BRAINEXPLODE3), /* S_BRAINEXPLODE2 */ + STATE_ENTRY(SPR_MISL, 32771, 10, a_brain_explode, + S_NULL), /* S_BRAINEXPLODE3 */ + STATE_ENTRY(SPR_ARM1, 0, 6, NULL, S_ARM1A), /* S_ARM1 */ + STATE_ENTRY(SPR_ARM1, 32769, 7, NULL, S_ARM1), /* S_ARM1A */ + STATE_ENTRY(SPR_ARM2, 0, 6, NULL, S_ARM2A), /* S_ARM2 */ + STATE_ENTRY(SPR_ARM2, 32769, 6, NULL, S_ARM2), /* S_ARM2A */ + STATE_ENTRY(SPR_BAR1, 0, 6, NULL, S_BAR2), /* S_BAR1 */ + STATE_ENTRY(SPR_BAR1, 1, 6, NULL, S_BAR1), /* S_BAR2 */ + STATE_ENTRY(SPR_BEXP, 32768, 5, NULL, S_BEXP2), /* S_BEXP */ + STATE_ENTRY(SPR_BEXP, 32769, 5, a_scream, S_BEXP3), /* S_BEXP2 */ + STATE_ENTRY(SPR_BEXP, 32770, 5, NULL, S_BEXP4), /* S_BEXP3 */ + STATE_ENTRY(SPR_BEXP, 32771, 10, a_explode, S_BEXP5), /* S_BEXP4 */ + STATE_ENTRY(SPR_BEXP, 32772, 10, NULL, S_NULL), /* S_BEXP5 */ + STATE_ENTRY(SPR_FCAN, 32768, 4, NULL, S_BBAR2), /* S_BBAR1 */ + STATE_ENTRY(SPR_FCAN, 32769, 4, NULL, S_BBAR3), /* S_BBAR2 */ + STATE_ENTRY(SPR_FCAN, 32770, 4, NULL, S_BBAR1), /* S_BBAR3 */ + STATE_ENTRY(SPR_BON1, 0, 6, NULL, S_BON1A), /* S_BON1 */ + STATE_ENTRY(SPR_BON1, 1, 6, NULL, S_BON1B), /* S_BON1A */ + STATE_ENTRY(SPR_BON1, 2, 6, NULL, S_BON1C), /* S_BON1B */ + STATE_ENTRY(SPR_BON1, 3, 6, NULL, S_BON1D), /* S_BON1C */ + STATE_ENTRY(SPR_BON1, 2, 6, NULL, S_BON1E), /* S_BON1D */ + STATE_ENTRY(SPR_BON1, 1, 6, NULL, S_BON1), /* S_BON1E */ + STATE_ENTRY(SPR_BON2, 0, 6, NULL, S_BON2A), /* S_BON2 */ + STATE_ENTRY(SPR_BON2, 1, 6, NULL, S_BON2B), /* S_BON2A */ + STATE_ENTRY(SPR_BON2, 2, 6, NULL, S_BON2C), /* S_BON2B */ + STATE_ENTRY(SPR_BON2, 3, 6, NULL, S_BON2D), /* S_BON2C */ + STATE_ENTRY(SPR_BON2, 2, 6, NULL, S_BON2E), /* S_BON2D */ + STATE_ENTRY(SPR_BON2, 1, 6, NULL, S_BON2), /* S_BON2E */ + STATE_ENTRY(SPR_BKEY, 0, 10, NULL, S_BKEY2), /* S_BKEY */ + STATE_ENTRY(SPR_BKEY, 32769, 10, NULL, S_BKEY), /* S_BKEY2 */ + STATE_ENTRY(SPR_RKEY, 0, 10, NULL, S_RKEY2), /* S_RKEY */ + STATE_ENTRY(SPR_RKEY, 32769, 10, NULL, S_RKEY), /* S_RKEY2 */ + STATE_ENTRY(SPR_YKEY, 0, 10, NULL, S_YKEY2), /* S_YKEY */ + STATE_ENTRY(SPR_YKEY, 32769, 10, NULL, S_YKEY), /* S_YKEY2 */ + STATE_ENTRY(SPR_BSKU, 0, 10, NULL, S_BSKULL2), /* S_BSKULL */ + STATE_ENTRY(SPR_BSKU, 32769, 10, NULL, S_BSKULL), /* S_BSKULL2 */ + STATE_ENTRY(SPR_RSKU, 0, 10, NULL, S_RSKULL2), /* S_RSKULL */ + STATE_ENTRY(SPR_RSKU, 32769, 10, NULL, S_RSKULL), /* S_RSKULL2 */ + STATE_ENTRY(SPR_YSKU, 0, 10, NULL, S_YSKULL2), /* S_YSKULL */ + STATE_ENTRY(SPR_YSKU, 32769, 10, NULL, S_YSKULL), /* S_YSKULL2 */ + STATE_ENTRY(SPR_STIM, 0, -1, NULL, S_NULL), /* S_STIM */ + STATE_ENTRY(SPR_MEDI, 0, -1, NULL, S_NULL), /* S_MEDI */ + STATE_ENTRY(SPR_SOUL, 32768, 6, NULL, S_SOUL2), /* S_SOUL */ + STATE_ENTRY(SPR_SOUL, 32769, 6, NULL, S_SOUL3), /* S_SOUL2 */ + STATE_ENTRY(SPR_SOUL, 32770, 6, NULL, S_SOUL4), /* S_SOUL3 */ + STATE_ENTRY(SPR_SOUL, 32771, 6, NULL, S_SOUL5), /* S_SOUL4 */ + STATE_ENTRY(SPR_SOUL, 32770, 6, NULL, S_SOUL6), /* S_SOUL5 */ + STATE_ENTRY(SPR_SOUL, 32769, 6, NULL, S_SOUL), /* S_SOUL6 */ + STATE_ENTRY(SPR_PINV, 32768, 6, NULL, S_PINV2), /* S_PINV */ + STATE_ENTRY(SPR_PINV, 32769, 6, NULL, S_PINV3), /* S_PINV2 */ + STATE_ENTRY(SPR_PINV, 32770, 6, NULL, S_PINV4), /* S_PINV3 */ + STATE_ENTRY(SPR_PINV, 32771, 6, NULL, S_PINV), /* S_PINV4 */ + STATE_ENTRY(SPR_PSTR, 32768, -1, NULL, S_NULL), /* S_PSTR */ + STATE_ENTRY(SPR_PINS, 32768, 6, NULL, S_PINS2), /* S_PINS */ + STATE_ENTRY(SPR_PINS, 32769, 6, NULL, S_PINS3), /* S_PINS2 */ + STATE_ENTRY(SPR_PINS, 32770, 6, NULL, S_PINS4), /* S_PINS3 */ + STATE_ENTRY(SPR_PINS, 32771, 6, NULL, S_PINS), /* S_PINS4 */ + STATE_ENTRY(SPR_MEGA, 32768, 6, NULL, S_MEGA2), /* S_MEGA */ + STATE_ENTRY(SPR_MEGA, 32769, 6, NULL, S_MEGA3), /* S_MEGA2 */ + STATE_ENTRY(SPR_MEGA, 32770, 6, NULL, S_MEGA4), /* S_MEGA3 */ + STATE_ENTRY(SPR_MEGA, 32771, 6, NULL, S_MEGA), /* S_MEGA4 */ + STATE_ENTRY(SPR_SUIT, 32768, -1, NULL, S_NULL), /* S_SUIT */ + STATE_ENTRY(SPR_PMAP, 32768, 6, NULL, S_PMAP2), /* S_PMAP */ + STATE_ENTRY(SPR_PMAP, 32769, 6, NULL, S_PMAP3), /* S_PMAP2 */ + STATE_ENTRY(SPR_PMAP, 32770, 6, NULL, S_PMAP4), /* S_PMAP3 */ + STATE_ENTRY(SPR_PMAP, 32771, 6, NULL, S_PMAP5), /* S_PMAP4 */ + STATE_ENTRY(SPR_PMAP, 32770, 6, NULL, S_PMAP6), /* S_PMAP5 */ + STATE_ENTRY(SPR_PMAP, 32769, 6, NULL, S_PMAP), /* S_PMAP6 */ + STATE_ENTRY(SPR_PVIS, 32768, 6, NULL, S_PVIS2), /* S_PVIS */ + STATE_ENTRY(SPR_PVIS, 1, 6, NULL, S_PVIS), /* S_PVIS2 */ + STATE_ENTRY(SPR_CLIP, 0, -1, NULL, S_NULL), /* S_CLIP */ + STATE_ENTRY(SPR_AMMO, 0, -1, NULL, S_NULL), /* S_AMMO */ + STATE_ENTRY(SPR_ROCK, 0, -1, NULL, S_NULL), /* S_ROCK */ + STATE_ENTRY(SPR_BROK, 0, -1, NULL, S_NULL), /* S_BROK */ + STATE_ENTRY(SPR_CELL, 0, -1, NULL, S_NULL), /* S_CELL */ + STATE_ENTRY(SPR_CELP, 0, -1, NULL, S_NULL), /* S_CELP */ + STATE_ENTRY(SPR_SHEL, 0, -1, NULL, S_NULL), /* S_SHEL */ + STATE_ENTRY(SPR_SBOX, 0, -1, NULL, S_NULL), /* S_SBOX */ + STATE_ENTRY(SPR_BPAK, 0, -1, NULL, S_NULL), /* S_BPAK */ + STATE_ENTRY(SPR_BFUG, 0, -1, NULL, S_NULL), /* S_BFUG */ + STATE_ENTRY(SPR_MGUN, 0, -1, NULL, S_NULL), /* S_MGUN */ + STATE_ENTRY(SPR_CSAW, 0, -1, NULL, S_NULL), /* S_CSAW */ + STATE_ENTRY(SPR_LAUN, 0, -1, NULL, S_NULL), /* S_LAUN */ + STATE_ENTRY(SPR_PLAS, 0, -1, NULL, S_NULL), /* S_PLAS */ + STATE_ENTRY(SPR_SHOT, 0, -1, NULL, S_NULL), /* S_SHOT */ + STATE_ENTRY(SPR_SGN2, 0, -1, NULL, S_NULL), /* S_SHOT2 */ + STATE_ENTRY(SPR_COLU, 32768, -1, NULL, S_NULL), /* S_COLU */ + STATE_ENTRY(SPR_SMT2, 0, -1, NULL, S_NULL), /* S_STALAG */ + STATE_ENTRY(SPR_GOR1, 0, 10, NULL, S_BLOODYTWITCH2), /* S_BLOODYTWITCH */ + STATE_ENTRY(SPR_GOR1, 1, 15, NULL, S_BLOODYTWITCH3), /* S_BLOODYTWITCH2 */ + STATE_ENTRY(SPR_GOR1, 2, 8, NULL, S_BLOODYTWITCH4), /* S_BLOODYTWITCH3 */ + STATE_ENTRY(SPR_GOR1, 1, 6, NULL, S_BLOODYTWITCH), /* S_BLOODYTWITCH4 */ + STATE_ENTRY(SPR_PLAY, 13, -1, NULL, S_NULL), /* S_DEADTORSO */ + STATE_ENTRY(SPR_PLAY, 18, -1, NULL, S_NULL), /* S_DEADBOTTOM */ + STATE_ENTRY(SPR_POL2, 0, -1, NULL, S_NULL), /* S_HEADSONSTICK */ + STATE_ENTRY(SPR_POL5, 0, -1, NULL, S_NULL), /* S_GIBS */ + STATE_ENTRY(SPR_POL4, 0, -1, NULL, S_NULL), /* S_HEADONASTICK */ + STATE_ENTRY(SPR_POL3, 32768, 6, NULL, S_HEADCANDLES2), /* S_HEADCANDLES */ + STATE_ENTRY(SPR_POL3, 32769, 6, NULL, S_HEADCANDLES), /* S_HEADCANDLES2 */ + STATE_ENTRY(SPR_POL1, 0, -1, NULL, S_NULL), /* S_DEADSTICK */ + STATE_ENTRY(SPR_POL6, 0, 6, NULL, S_LIVESTICK2), /* S_LIVESTICK */ + STATE_ENTRY(SPR_POL6, 1, 8, NULL, S_LIVESTICK), /* S_LIVESTICK2 */ + STATE_ENTRY(SPR_GOR2, 0, -1, NULL, S_NULL), /* S_MEAT2 */ + STATE_ENTRY(SPR_GOR3, 0, -1, NULL, S_NULL), /* S_MEAT3 */ + STATE_ENTRY(SPR_GOR4, 0, -1, NULL, S_NULL), /* S_MEAT4 */ + STATE_ENTRY(SPR_GOR5, 0, -1, NULL, S_NULL), /* S_MEAT5 */ + STATE_ENTRY(SPR_SMIT, 0, -1, NULL, S_NULL), /* S_STALAGTITE */ + STATE_ENTRY(SPR_COL1, 0, -1, NULL, S_NULL), /* S_TALLGRNCOL */ + STATE_ENTRY(SPR_COL2, 0, -1, NULL, S_NULL), /* S_SHRTGRNCOL */ + STATE_ENTRY(SPR_COL3, 0, -1, NULL, S_NULL), /* S_TALLREDCOL */ + STATE_ENTRY(SPR_COL4, 0, -1, NULL, S_NULL), /* S_SHRTREDCOL */ + STATE_ENTRY(SPR_CAND, 32768, -1, NULL, S_NULL), /* S_CANDLESTIK */ + STATE_ENTRY(SPR_CBRA, 32768, -1, NULL, S_NULL), /* S_CANDELABRA */ + STATE_ENTRY(SPR_COL6, 0, -1, NULL, S_NULL), /* S_SKULLCOL */ + STATE_ENTRY(SPR_TRE1, 0, -1, NULL, S_NULL), /* S_TORCHTREE */ + STATE_ENTRY(SPR_TRE2, 0, -1, NULL, S_NULL), /* S_BIGTREE */ + STATE_ENTRY(SPR_ELEC, 0, -1, NULL, S_NULL), /* S_TECHPILLAR */ + STATE_ENTRY(SPR_CEYE, 32768, 6, NULL, S_EVILEYE2), /* S_EVILEYE */ + STATE_ENTRY(SPR_CEYE, 32769, 6, NULL, S_EVILEYE3), /* S_EVILEYE2 */ + STATE_ENTRY(SPR_CEYE, 32770, 6, NULL, S_EVILEYE4), /* S_EVILEYE3 */ + STATE_ENTRY(SPR_CEYE, 32769, 6, NULL, S_EVILEYE), /* S_EVILEYE4 */ + STATE_ENTRY(SPR_FSKU, 32768, 6, NULL, S_FLOATSKULL2), /* S_FLOATSKULL */ + STATE_ENTRY(SPR_FSKU, 32769, 6, NULL, S_FLOATSKULL3), /* S_FLOATSKULL2 */ + STATE_ENTRY(SPR_FSKU, 32770, 6, NULL, S_FLOATSKULL), /* S_FLOATSKULL3 */ + STATE_ENTRY(SPR_COL5, 0, 14, NULL, S_HEARTCOL2), /* S_HEARTCOL */ + STATE_ENTRY(SPR_COL5, 1, 14, NULL, S_HEARTCOL), /* S_HEARTCOL2 */ + STATE_ENTRY(SPR_TBLU, 32768, 4, NULL, S_BLUETORCH2), /* S_BLUETORCH */ + STATE_ENTRY(SPR_TBLU, 32769, 4, NULL, S_BLUETORCH3), /* S_BLUETORCH2 */ + STATE_ENTRY(SPR_TBLU, 32770, 4, NULL, S_BLUETORCH4), /* S_BLUETORCH3 */ + STATE_ENTRY(SPR_TBLU, 32771, 4, NULL, S_BLUETORCH), /* S_BLUETORCH4 */ + STATE_ENTRY(SPR_TGRN, 32768, 4, NULL, S_GREENTORCH2), /* S_GREENTORCH */ + STATE_ENTRY(SPR_TGRN, 32769, 4, NULL, S_GREENTORCH3), /* S_GREENTORCH2 */ + STATE_ENTRY(SPR_TGRN, 32770, 4, NULL, S_GREENTORCH4), /* S_GREENTORCH3 */ + STATE_ENTRY(SPR_TGRN, 32771, 4, NULL, S_GREENTORCH), /* S_GREENTORCH4 */ + STATE_ENTRY(SPR_TRED, 32768, 4, NULL, S_REDTORCH2), /* S_REDTORCH */ + STATE_ENTRY(SPR_TRED, 32769, 4, NULL, S_REDTORCH3), /* S_REDTORCH2 */ + STATE_ENTRY(SPR_TRED, 32770, 4, NULL, S_REDTORCH4), /* S_REDTORCH3 */ + STATE_ENTRY(SPR_TRED, 32771, 4, NULL, S_REDTORCH), /* S_REDTORCH4 */ + STATE_ENTRY(SPR_SMBT, 32768, 4, NULL, S_BTORCHSHRT2), /* S_BTORCHSHRT */ + STATE_ENTRY(SPR_SMBT, 32769, 4, NULL, S_BTORCHSHRT3), /* S_BTORCHSHRT2 */ + STATE_ENTRY(SPR_SMBT, 32770, 4, NULL, S_BTORCHSHRT4), /* S_BTORCHSHRT3 */ + STATE_ENTRY(SPR_SMBT, 32771, 4, NULL, S_BTORCHSHRT), /* S_BTORCHSHRT4 */ + STATE_ENTRY(SPR_SMGT, 32768, 4, NULL, S_GTORCHSHRT2), /* S_GTORCHSHRT */ + STATE_ENTRY(SPR_SMGT, 32769, 4, NULL, S_GTORCHSHRT3), /* S_GTORCHSHRT2 */ + STATE_ENTRY(SPR_SMGT, 32770, 4, NULL, S_GTORCHSHRT4), /* S_GTORCHSHRT3 */ + STATE_ENTRY(SPR_SMGT, 32771, 4, NULL, S_GTORCHSHRT), /* S_GTORCHSHRT4 */ + STATE_ENTRY(SPR_SMRT, 32768, 4, NULL, S_RTORCHSHRT2), /* S_RTORCHSHRT */ + STATE_ENTRY(SPR_SMRT, 32769, 4, NULL, S_RTORCHSHRT3), /* S_RTORCHSHRT2 */ + STATE_ENTRY(SPR_SMRT, 32770, 4, NULL, S_RTORCHSHRT4), /* S_RTORCHSHRT3 */ + STATE_ENTRY(SPR_SMRT, 32771, 4, NULL, S_RTORCHSHRT), /* S_RTORCHSHRT4 */ + STATE_ENTRY(SPR_HDB1, 0, -1, NULL, S_NULL), /* S_HANGNOGUTS */ + STATE_ENTRY(SPR_HDB2, 0, -1, NULL, S_NULL), /* S_HANGBNOBRAIN */ + STATE_ENTRY(SPR_HDB3, 0, -1, NULL, S_NULL), /* S_HANGTLOOKDN */ + STATE_ENTRY(SPR_HDB4, 0, -1, NULL, S_NULL), /* S_HANGTSKULL */ + STATE_ENTRY(SPR_HDB5, 0, -1, NULL, S_NULL), /* S_HANGTLOOKUP */ + STATE_ENTRY(SPR_HDB6, 0, -1, NULL, S_NULL), /* S_HANGTNOBRAIN */ + STATE_ENTRY(SPR_POB1, 0, -1, NULL, S_NULL), /* S_COLONGIBS */ + STATE_ENTRY(SPR_POB2, 0, -1, NULL, S_NULL), /* S_SMALLPOOL */ + STATE_ENTRY(SPR_BRS1, 0, -1, NULL, S_NULL), /* S_BRAINSTEM */ + STATE_ENTRY(SPR_TLMP, 32768, 4, NULL, S_TECHLAMP2), /* S_TECHLAMP */ + STATE_ENTRY(SPR_TLMP, 32769, 4, NULL, S_TECHLAMP3), /* S_TECHLAMP2 */ + STATE_ENTRY(SPR_TLMP, 32770, 4, NULL, S_TECHLAMP4), /* S_TECHLAMP3 */ + STATE_ENTRY(SPR_TLMP, 32771, 4, NULL, S_TECHLAMP), /* S_TECHLAMP4 */ + STATE_ENTRY(SPR_TLP2, 32768, 4, NULL, S_TECH2LAMP2), /* S_TECH2LAMP */ + STATE_ENTRY(SPR_TLP2, 32769, 4, NULL, S_TECH2LAMP3), /* S_TECH2LAMP2 */ + STATE_ENTRY(SPR_TLP2, 32770, 4, NULL, S_TECH2LAMP4), /* S_TECH2LAMP3 */ + STATE_ENTRY(SPR_TLP2, 32771, 4, NULL, S_TECH2LAMP) /* S_TECH2LAMP4 */ +}; + +mobjinfo_t mobjinfo[NUMMOBJTYPES] = +{ + { + /* MT_PLAYER */ + + .doomednum = -1, + .spawnstate = S_PLAY, + .spawnhealth = 100, + .seestate = S_PLAY_RUN1, + .reactiontime = 0, + .painstate = S_PLAY_PAIN, + .painchance = 255, + .meleestate = S_NULL, + .missilestate = S_PLAY_ATK1, + .deathstate = S_PLAY_DIE1, + .xdeathstate = S_PLAY_XDIE1, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_DROPOFF | + MF_PICKUP | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_PLPAIN, + .deathsound = SFX_PLDETH, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_POSSESSED */ + + .doomednum = 3004, + .spawnstate = S_POSS_STND, + .spawnhealth = 20, + .seestate = S_POSS_RUN1, + .reactiontime = 8, + .painstate = S_POSS_PAIN, + .painchance = 200, + .meleestate = 0, + .missilestate = S_POSS_ATK1, + .deathstate = S_POSS_DIE1, + .xdeathstate = S_POSS_XDIE1, + .speed = 8, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_POSS_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_PISTOL, + .painsound = SFX_POPAIN, + .deathsound = SFX_PODTH1, + .activesound = SFX_POSACT, + .seesound = SFX_POSIT1, +#endif + }, + + { + /* MT_SHOTGUY */ + + .doomednum = 9, + .spawnstate = S_SPOS_STND, + .spawnhealth = 30, + .seestate = S_SPOS_RUN1, + .reactiontime = 8, + .painstate = S_SPOS_PAIN, + .painchance = 170, + .meleestate = 0, + .missilestate = S_SPOS_ATK1, + .deathstate = S_SPOS_DIE1, + .xdeathstate = S_SPOS_XDIE1, + .speed = 8, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_SPOS_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_POPAIN, + .deathsound = SFX_PODTH2, + .activesound = SFX_POSACT, + .seesound = SFX_POSIT2, +#endif + }, + + { + /* MT_VILE */ + + .doomednum = 64, + .spawnstate = S_VILE_STND, + .spawnhealth = 700, + .seestate = S_VILE_RUN1, + .reactiontime = 8, + .painstate = S_VILE_PAIN, + .painchance = 10, + .meleestate = 0, + .missilestate = S_VILE_ATK1, + .deathstate = S_VILE_DIE1, + .xdeathstate = S_NULL, + .speed = 15, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 500, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_VIPAIN, + .deathsound = SFX_VILDTH, + .activesound = SFX_VILACT, + .seesound = SFX_VILSIT, +#endif + }, + + { + /* MT_FIRE */ + + .doomednum = -1, + .spawnstate = S_FIRE1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_UNDEAD */ + + .doomednum = 66, + .spawnstate = S_SKEL_STND, + .spawnhealth = 300, + .seestate = S_SKEL_RUN1, + .reactiontime = 8, + .painstate = S_SKEL_PAIN, + .painchance = 100, + .meleestate = S_SKEL_FIST1, + .missilestate = S_SKEL_MISS1, + .deathstate = S_SKEL_DIE1, + .xdeathstate = S_NULL, + .speed = 10, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 500, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_SKEL_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_POPAIN, + .deathsound = SFX_SKEDTH, + .activesound = SFX_SKEACT, + .seesound = SFX_SKESIT, +#endif + }, + + { + /* MT_TRACER */ + + .doomednum = -1, + .spawnstate = S_TRACER, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_TRACEEXP1, + .xdeathstate = S_NULL, + .speed = 10 * FRACUNIT, + .radius = 11 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 10, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_BAREXP, + .activesound = SFX_NONE, + .seesound = SFX_SKEATK, +#endif + }, + + { + /* MT_SMOKE */ + + .doomednum = -1, + .spawnstate = S_SMOKE1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_FATSO */ + + .doomednum = 67, + .spawnstate = S_FATT_STND, + .spawnhealth = 600, + .seestate = S_FATT_RUN1, + .reactiontime = 8, + .painstate = S_FATT_PAIN, + .painchance = 80, + .meleestate = 0, + .missilestate = S_FATT_ATK1, + .deathstate = S_FATT_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 48 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 1000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_FATT_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_MNPAIN, + .deathsound = SFX_MANDTH, + .activesound = SFX_POSACT, + .seesound = SFX_MANSIT, +#endif + }, + + { + /* MT_FATSHOT */ + + .doomednum = -1, + .spawnstate = S_FATSHOT1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_FATSHOTX1, + .xdeathstate = S_NULL, + .speed = 20 * FRACUNIT, + .radius = 6 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 8, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_FIRSHT, +#endif + }, + + { + /* MT_CHAINGUY */ + + .doomednum = 65, + .spawnstate = S_CPOS_STND, + .spawnhealth = 70, + .seestate = S_CPOS_RUN1, + .reactiontime = 8, + .painstate = S_CPOS_PAIN, + .painchance = 170, + .meleestate = 0, + .missilestate = S_CPOS_ATK1, + .deathstate = S_CPOS_DIE1, + .xdeathstate = S_CPOS_XDIE1, + .speed = 8, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_CPOS_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_POPAIN, + .deathsound = SFX_PODTH2, + .activesound = SFX_POSACT, + .seesound = SFX_POSIT2, +#endif + }, + + { + /* MT_TROOP */ + + .doomednum = 3001, + .spawnstate = S_TROO_STND, + .spawnhealth = 60, + .seestate = S_TROO_RUN1, + .reactiontime = 8, + .painstate = S_TROO_PAIN, + .painchance = 200, + .meleestate = S_TROO_ATK1, + .missilestate = S_TROO_ATK1, + .deathstate = S_TROO_DIE1, + .xdeathstate = S_TROO_XDIE1, + .speed = 8, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_TROO_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_POPAIN, + .deathsound = SFX_BGDTH1, + .activesound = SFX_BGACT, + .seesound = SFX_BGSIT1, +#endif + }, + + { + /* MT_SERGEANT */ + + .doomednum = 3002, + .spawnstate = S_SARG_STND, + .spawnhealth = 150, + .seestate = S_SARG_RUN1, + .reactiontime = 8, + .painstate = S_SARG_PAIN, + .painchance = 180, + .meleestate = S_SARG_ATK1, + .missilestate = 0, + .deathstate = S_SARG_DIE1, + .xdeathstate = S_NULL, + .speed = 10, + .radius = 30 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 400, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_SARG_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_SGTATK, + .painsound = SFX_DMPAIN, + .deathsound = SFX_SGTDTH, + .activesound = SFX_DMACT, + .seesound = SFX_SGTSIT, +#endif + }, + + { + /* MT_SHADOWS */ + + .doomednum = 58, + .spawnstate = S_SARG_STND, + .spawnhealth = 150, + .seestate = S_SARG_RUN1, + .reactiontime = 8, + .painstate = S_SARG_PAIN, + .painchance = 180, + .meleestate = S_SARG_ATK1, + .missilestate = 0, + .deathstate = S_SARG_DIE1, + .xdeathstate = S_NULL, + .speed = 10, + .radius = 30 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 400, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_SHADOW | MF_COUNTKILL, + .raisestate = S_SARG_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_SGTATK, + .painsound = SFX_DMPAIN, + .deathsound = SFX_SGTDTH, + .activesound = SFX_DMACT, + .seesound = SFX_SGTSIT, +#endif + }, + + { + /* MT_HEAD */ + + .doomednum = 3005, + .spawnstate = S_HEAD_STND, + .spawnhealth = 400, + .seestate = S_HEAD_RUN1, + .reactiontime = 8, + .painstate = S_HEAD_PAIN, + .painchance = 128, + .meleestate = 0, + .missilestate = S_HEAD_ATK1, + .deathstate = S_HEAD_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 31 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 400, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_FLOAT | + MF_NOGRAVITY | MF_COUNTKILL, + .raisestate = S_HEAD_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_DMPAIN, + .deathsound = SFX_CACDTH, + .activesound = SFX_DMACT, + .seesound = SFX_CACSIT, +#endif + }, + + { + /* MT_BRUISER */ + + .doomednum = 3003, + .spawnstate = S_BOSS_STND, + .spawnhealth = 1000, + .seestate = S_BOSS_RUN1, + .reactiontime = 8, + .painstate = S_BOSS_PAIN, + .painchance = 50, + .meleestate = S_BOSS_ATK1, + .missilestate = S_BOSS_ATK1, + .deathstate = S_BOSS_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 24 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 1000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_BOSS_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_DMPAIN, + .deathsound = SFX_BRSDTH, + .activesound = SFX_DMACT, + .seesound = SFX_BRSSIT, +#endif + }, + + { + /* MT_BRUISERSHOT */ + + .doomednum = -1, + .spawnstate = S_BRBALL1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_BRBALLX1, + .xdeathstate = S_NULL, + .speed = 15 * FRACUNIT, + .radius = 6 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 8, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_FIRSHT, +#endif + }, + + { + /* MT_KNIGHT */ + + .doomednum = 69, + .spawnstate = S_BOS2_STND, + .spawnhealth = 500, + .seestate = S_BOS2_RUN1, + .reactiontime = 8, + .painstate = S_BOS2_PAIN, + .painchance = 50, + .meleestate = S_BOS2_ATK1, + .missilestate = S_BOS2_ATK1, + .deathstate = S_BOS2_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 24 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 1000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_BOS2_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_DMPAIN, + .deathsound = SFX_KNTDTH, + .activesound = SFX_DMACT, + .seesound = SFX_KNTSIT, +#endif + }, + + { + /* MT_SKULL */ + + .doomednum = 3006, + .spawnstate = S_SKULL_STND, + .spawnhealth = 100, + .seestate = S_SKULL_RUN1, + .reactiontime = 8, + .painstate = S_SKULL_PAIN, + .painchance = 256, + .meleestate = 0, + .missilestate = S_SKULL_ATK1, + .deathstate = S_SKULL_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 16 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 50, + .damage = 3, + .flags = MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_SKLATK, + .painsound = SFX_DMPAIN, + .deathsound = SFX_FIRXPL, + .activesound = SFX_DMACT, + .seesound = 0, +#endif + }, + + { + /* MT_SPIDER */ + + .doomednum = 7, + .spawnstate = S_SPID_STND, + .spawnhealth = 3000, + .seestate = S_SPID_RUN1, + .reactiontime = 8, + .painstate = S_SPID_PAIN, + .painchance = 40, + .meleestate = 0, + .missilestate = S_SPID_ATK1, + .deathstate = S_SPID_DIE1, + .xdeathstate = S_NULL, + .speed = 12, + .radius = 128 * FRACUNIT, + .height = 100 * FRACUNIT, + .mass = 1000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_SHOTGN, + .painsound = SFX_DMPAIN, + .deathsound = SFX_SPIDTH, + .activesound = SFX_DMACT, + .seesound = SFX_SPISIT, +#endif + }, + + { + /* MT_BABY */ + + .doomednum = 68, + .spawnstate = S_BSPI_STND, + .spawnhealth = 500, + .seestate = S_BSPI_SIGHT, + .reactiontime = 8, + .painstate = S_BSPI_PAIN, + .painchance = 128, + .meleestate = 0, + .missilestate = S_BSPI_ATK1, + .deathstate = S_BSPI_DIE1, + .xdeathstate = S_NULL, + .speed = 12, + .radius = 64 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 600, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_BSPI_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_DMPAIN, + .deathsound = SFX_BSPDTH, + .activesound = SFX_BSPACT, + .seesound = SFX_BSPSIT, +#endif + }, + + { + /* MT_CYBORG */ + + .doomednum = 16, + .spawnstate = S_CYBER_STND, + .spawnhealth = 4000, + .seestate = S_CYBER_RUN1, + .reactiontime = 8, + .painstate = S_CYBER_PAIN, + .painchance = 20, + .meleestate = 0, + .missilestate = S_CYBER_ATK1, + .deathstate = S_CYBER_DIE1, + .xdeathstate = S_NULL, + .speed = 16, + .radius = 40 * FRACUNIT, + .height = 110 * FRACUNIT, + .mass = 1000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_DMPAIN, + .deathsound = SFX_CYBDTH, + .activesound = SFX_DMACT, + .seesound = SFX_CYBSIT, +#endif + }, + + { + /* MT_PAIN */ + + .doomednum = 71, + .spawnstate = S_PAIN_STND, + .spawnhealth = 400, + .seestate = S_PAIN_RUN1, + .reactiontime = 8, + .painstate = S_PAIN_PAIN, + .painchance = 128, + .meleestate = 0, + .missilestate = S_PAIN_ATK1, + .deathstate = S_PAIN_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 31 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 400, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_FLOAT | + MF_NOGRAVITY | MF_COUNTKILL, + .raisestate = S_PAIN_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_PEPAIN, + .deathsound = SFX_PEDTH, + .activesound = SFX_DMACT, + .seesound = SFX_PESIT, +#endif + }, + + { + /* MT_WOLFSS */ + + .doomednum = 84, + .spawnstate = S_SSWV_STND, + .spawnhealth = 50, + .seestate = S_SSWV_RUN1, + .reactiontime = 8, + .painstate = S_SSWV_PAIN, + .painchance = 170, + .meleestate = 0, + .missilestate = S_SSWV_ATK1, + .deathstate = S_SSWV_DIE1, + .xdeathstate = S_SSWV_XDIE1, + .speed = 8, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_SSWV_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_POPAIN, + .deathsound = SFX_SSDTH, + .activesound = SFX_POSACT, + .seesound = SFX_SSSIT, +#endif + }, + + { + /* MT_KEEN */ + + .doomednum = 72, + .spawnstate = S_KEENSTND, + .spawnhealth = 100, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_KEENPAIN, + .painchance = 256, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_COMMKEEN, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 72 * FRACUNIT, + .mass = 10000000, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY | + MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_KEENPN, + .deathsound = SFX_KEENDT, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_BOSSBRAIN */ + + .doomednum = 88, + .spawnstate = S_BRAIN, + .spawnhealth = 250, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_BRAIN_PAIN, + .painchance = 255, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_BRAIN_DIE1, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 10000000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_BOSPN, + .deathsound = SFX_BOSDTH, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_BOSSSPIT */ + + .doomednum = 89, + .spawnstate = S_BRAINEYE, + .spawnhealth = 1000, + .seestate = S_BRAINEYESEE, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 32 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOSECTOR, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_BOSSTARGET */ + + .doomednum = 87, + .spawnstate = S_NULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 32 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOSECTOR, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_SPAWNSHOT */ + + .doomednum = -1, + .spawnstate = S_SPAWN1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 10 * FRACUNIT, + .radius = 6 * FRACUNIT, + .height = 32 * FRACUNIT, + .mass = 100, + .damage = 3, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | + MF_NOGRAVITY | MF_NOCLIP, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_BOSPIT, +#endif + }, + + { + /* MT_SPAWNFIRE */ + + .doomednum = -1, + .spawnstate = S_SPAWNFIRE1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_BARREL */ + + .doomednum = 2035, + .spawnstate = S_BAR1, + .spawnhealth = 20, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_BEXP, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 10 * FRACUNIT, + .height = 42 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_BAREXP, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_TROOPSHOT */ + + .doomednum = -1, + .spawnstate = S_TBALL1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_TBALLX1, + .xdeathstate = S_NULL, + .speed = 10 * FRACUNIT, + .radius = 6 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 3, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_FIRSHT, +#endif + }, + + { + /* MT_HEADSHOT */ + + .doomednum = -1, + .spawnstate = S_RBALL1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_RBALLX1, + .xdeathstate = S_NULL, + .speed = 10 * FRACUNIT, + .radius = 6 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 5, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_FIRSHT, +#endif + }, + + { + /* MT_ROCKET */ + + .doomednum = -1, + .spawnstate = S_ROCKET, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_EXPLODE1, + .xdeathstate = S_NULL, + .speed = 20 * FRACUNIT, + .radius = 11 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 20, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_BAREXP, + .activesound = SFX_NONE, + .seesound = SFX_RLAUNC, +#endif + }, + + { + /* MT_PLASMA */ + + .doomednum = -1, + .spawnstate = S_PLASBALL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_PLASEXP, + .xdeathstate = S_NULL, + .speed = 25 * FRACUNIT, + .radius = 13 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 5, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_PLASMA, +#endif + }, + + { + /* MT_BFG */ + + .doomednum = -1, + .spawnstate = S_BFGSHOT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_BFGLAND, + .xdeathstate = S_NULL, + .speed = 25 * FRACUNIT, + .radius = 13 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 100, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_RXPLOD, + .activesound = SFX_NONE, + .seesound = 0, +#endif + }, + + { + /* MT_ARACHPLAZ */ + + .doomednum = -1, + .spawnstate = S_ARACH_PLAZ, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_ARACH_PLEX, + .xdeathstate = S_NULL, + .speed = 25 * FRACUNIT, + .radius = 13 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 5, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_PLASMA, +#endif + }, + + { + /* MT_PUFF */ + + .doomednum = -1, + .spawnstate = S_PUFF1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_BLOOD */ + + .doomednum = -1, + .spawnstate = S_BLOOD1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_TFOG */ + + .doomednum = -1, + .spawnstate = S_TFOG, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_IFOG */ + + .doomednum = -1, + .spawnstate = S_IFOG, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_TELEPORTMAN */ + + .doomednum = 14, + .spawnstate = S_NULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOSECTOR, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_EXTRABFG */ + + .doomednum = -1, + .spawnstate = S_BFGEXP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC0 */ + + .doomednum = 2018, + .spawnstate = S_ARM1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC1 */ + + .doomednum = 2019, + .spawnstate = S_ARM2, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC2 */ + + .doomednum = 2014, + .spawnstate = S_BON1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC3 */ + + .doomednum = 2015, + .spawnstate = S_BON2, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC4 */ + + .doomednum = 5, + .spawnstate = S_BKEY, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC5 */ + + .doomednum = 13, + .spawnstate = S_RKEY, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC6 */ + + .doomednum = 6, + .spawnstate = S_YKEY, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC7 */ + + .doomednum = 39, + .spawnstate = S_YSKULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC8 */ + + .doomednum = 38, + .spawnstate = S_RSKULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC9 */ + + .doomednum = 40, + .spawnstate = S_BSKULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC10 */ + + .doomednum = 2011, + .spawnstate = S_STIM, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC11 */ + + .doomednum = 2012, + .spawnstate = S_MEDI, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC12 */ + + .doomednum = 2013, + .spawnstate = S_SOUL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_INV */ + + .doomednum = 2022, + .spawnstate = S_PINV, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC13 */ + + .doomednum = 2023, + .spawnstate = S_PSTR, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_INS */ + + .doomednum = 2024, + .spawnstate = S_PINS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC14 */ + + .doomednum = 2025, + .spawnstate = S_SUIT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC15 */ + + .doomednum = 2026, + .spawnstate = S_PMAP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC16 */ + + .doomednum = 2045, + .spawnstate = S_PVIS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MEGA */ + + .doomednum = 83, + .spawnstate = S_MEGA, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_CLIP */ + + .doomednum = 2007, + .spawnstate = S_CLIP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC17 */ + + .doomednum = 2048, + .spawnstate = S_AMMO, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC18 */ + + .doomednum = 2010, + .spawnstate = S_ROCK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC19 */ + + .doomednum = 2046, + .spawnstate = S_BROK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC20 */ + + .doomednum = 2047, + .spawnstate = S_CELL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC21 */ + + .doomednum = 17, + .spawnstate = S_CELP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC22 */ + + .doomednum = 2008, + .spawnstate = S_SHEL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC23 */ + + .doomednum = 2049, + .spawnstate = S_SBOX, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC24 */ + + .doomednum = 8, + .spawnstate = S_BPAK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC25 */ + + .doomednum = 2006, + .spawnstate = S_BFUG, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_CHAINGUN */ + + .doomednum = 2002, + .spawnstate = S_MGUN, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC26 */ + + .doomednum = 2005, + .spawnstate = S_CSAW, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC27 */ + + .doomednum = 2003, + .spawnstate = S_LAUN, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC28 */ + + .doomednum = 2004, + .spawnstate = S_PLAS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_SHOTGUN */ + + .doomednum = 2001, + .spawnstate = S_SHOT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_SUPERSHOTGUN */ + + .doomednum = 82, + .spawnstate = S_SHOT2, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC29 */ + + .doomednum = 85, + .spawnstate = S_TECHLAMP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC30 */ + + .doomednum = 86, + .spawnstate = S_TECH2LAMP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC31 */ + + .doomednum = 2028, + .spawnstate = S_COLU, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC32 */ + + .doomednum = 30, + .spawnstate = S_TALLGRNCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC33 */ + + .doomednum = 31, + .spawnstate = S_SHRTGRNCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC34 */ + + .doomednum = 32, + .spawnstate = S_TALLREDCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC35 */ + + .doomednum = 33, + .spawnstate = S_SHRTREDCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC36 */ + + .doomednum = 37, + .spawnstate = S_SKULLCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC37 */ + + .doomednum = 36, + .spawnstate = S_HEARTCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC38 */ + + .doomednum = 41, + .spawnstate = S_EVILEYE, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC39 */ + + .doomednum = 42, + .spawnstate = S_FLOATSKULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC40 */ + + .doomednum = 43, + .spawnstate = S_TORCHTREE, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC41 */ + + .doomednum = 44, + .spawnstate = S_BLUETORCH, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC42 */ + + .doomednum = 45, + .spawnstate = S_GREENTORCH, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC43 */ + + .doomednum = 46, + .spawnstate = S_REDTORCH, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC44 */ + + .doomednum = 55, + .spawnstate = S_BTORCHSHRT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC45 */ + + .doomednum = 56, + .spawnstate = S_GTORCHSHRT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC46 */ + + .doomednum = 57, + .spawnstate = S_RTORCHSHRT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC47 */ + + .doomednum = 47, + .spawnstate = S_STALAGTITE, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC48 */ + + .doomednum = 48, + .spawnstate = S_TECHPILLAR, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC49 */ + + .doomednum = 34, + .spawnstate = S_CANDLESTIK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC50 */ + + .doomednum = 35, + .spawnstate = S_CANDELABRA, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC51 */ + + .doomednum = 49, + .spawnstate = S_BLOODYTWITCH, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 68 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC52 */ + + .doomednum = 50, + .spawnstate = S_MEAT2, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 84 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC53 */ + + .doomednum = 51, + .spawnstate = S_MEAT3, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 84 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC54 */ + + .doomednum = 52, + .spawnstate = S_MEAT4, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 68 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC55 */ + + .doomednum = 53, + .spawnstate = S_MEAT5, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 52 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC56 */ + + .doomednum = 59, + .spawnstate = S_MEAT2, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 84 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC57 */ + + .doomednum = 60, + .spawnstate = S_MEAT4, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 68 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC58 */ + + .doomednum = 61, + .spawnstate = S_MEAT3, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 52 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC59 */ + + .doomednum = 62, + .spawnstate = S_MEAT5, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 52 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC60 */ + + .doomednum = 63, + .spawnstate = S_BLOODYTWITCH, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 68 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC61 */ + + .doomednum = 22, + .spawnstate = S_HEAD_DIE6, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC62 */ + + .doomednum = 15, + .spawnstate = S_PLAY_DIE7, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC63 */ + + .doomednum = 18, + .spawnstate = S_POSS_DIE5, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC64 */ + + .doomednum = 21, + .spawnstate = S_SARG_DIE6, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC65 */ + + .doomednum = 23, + .spawnstate = S_SKULL_DIE6, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC66 */ + + .doomednum = 20, + .spawnstate = S_TROO_DIE5, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC67 */ + + .doomednum = 19, + .spawnstate = S_SPOS_DIE5, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC68 */ + + .doomednum = 10, + .spawnstate = S_PLAY_XDIE9, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC69 */ + + .doomednum = 12, + .spawnstate = S_PLAY_XDIE9, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC70 */ + + .doomednum = 28, + .spawnstate = S_HEADSONSTICK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC71 */ + + .doomednum = 24, + .spawnstate = S_GIBS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC72 */ + + .doomednum = 27, + .spawnstate = S_HEADONASTICK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC73 */ + + .doomednum = 29, + .spawnstate = S_HEADCANDLES, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC74 */ + + .doomednum = 25, + .spawnstate = S_DEADSTICK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC75 */ + + .doomednum = 26, + .spawnstate = S_LIVESTICK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC76 */ + + .doomednum = 54, + .spawnstate = S_BIGTREE, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 32 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC77 */ + + .doomednum = 70, + .spawnstate = S_BBAR1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC78 */ + + .doomednum = 73, + .spawnstate = S_HANGNOGUTS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 88 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC79 */ + + .doomednum = 74, + .spawnstate = S_HANGBNOBRAIN, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 88 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC80 */ + + .doomednum = 75, + .spawnstate = S_HANGTLOOKDN, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC81 */ + + .doomednum = 76, + .spawnstate = S_HANGTSKULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC82 */ + + .doomednum = 77, + .spawnstate = S_HANGTLOOKUP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC83 */ + + .doomednum = 78, + .spawnstate = S_HANGTNOBRAIN, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC84 */ + + .doomednum = 79, + .spawnstate = S_COLONGIBS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC85 */ + + .doomednum = 80, + .spawnstate = S_SMALLPOOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC86 */ + + .doomednum = 81, + .spawnstate = S_BRAINSTEM, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .seesound = SFX_NONE, + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, +#endif + }, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/info.h b/games/NXDoom/src/doom/info.h new file mode 100644 index 00000000000..508475f9d64 --- /dev/null +++ b/games/NXDoom/src/doom/info.h @@ -0,0 +1,1380 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/info.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Thing frame/state LUT, generated by multigen utility. + * This one is the original DOOM version, preserved. + * + ****************************************************************************/ + +#ifndef __INFO__ +#define __INFO__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/* Needed for action function pointer handling. */ + +#include "d_think.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +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; + +/* Tightly packed to save space! */ + +begin_packed_struct struct state_t +{ + actionf_t action; /* void (*action) (); */ + spritenum_t sprite; + statenum_t nextstate; + + int32_t tics; /* -1 to 181 */ + + uint16_t frame; /* 0 to 32796 */ + + /* NOTE: as far as I can tell, these fields are never used. They are set to + * zero in the state lookup table, and the only other place directly + * referencing them is P_SetPsprite, which uses them seemingly as a fixed + * point number to set some other coordinates later. However, in my + * gameplay, the check for `if (state->misc1)` never evaluates to true. + * Therefore, in my quest to save memory in one of the games largest lookup + * tables, I am removing these fields. + */ + +#if 0 + int misc1; + int misc2; +#endif +} end_packed_struct; + +typedef struct state_t state_t; + +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; + +/* Ranges for these members were obtained by analyzing the `mobjinfo` + * array at game startup. + */ + +begin_packed_struct struct mobjinfo_t +{ + uint32_t flags; /* 0 to 33557510 */ + uint32_t radius; /* 393216 to 8388608 */ + uint32_t height; /* 524288 to 7208960 */ + uint32_t mass; /* 50 to 10000000 */ + uint32_t speed; /* 0 to 1638400 */ + int16_t doomednum; /* -1 to 3006 */ + uint16_t spawnhealth; /* 20 to 4000 */ + uint16_t spawnstate; /* 0 to 963 */ + uint16_t seestate; /* 0 to 785 */ + uint16_t painstate; /* 0 to 779 */ + uint16_t painchance; /* 0 to 256 */ + uint16_t meleestate; /* 0 to 566 */ + uint16_t missilestate; /* 0 to 736 */ + uint16_t deathstate; /* 0 to 808 */ + uint16_t xdeathstate; /* 0 to 749 */ + uint16_t raisestate; /* 0 to 758 */ + uint8_t damage; /* 0 to 100 */ + uint8_t reactiontime; /* 0 to 8 */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + uint8_t seesound; /* 0 to 107 */ + uint8_t attacksound; /* 0 to 52 */ + uint8_t painsound; /* 0 to 103 */ + uint8_t deathsound; /* 0 to 104 */ + uint8_t activesound; /* 0 to 105 */ +#endif +} end_packed_struct; + +typedef struct mobjinfo_t mobjinfo_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern state_t states[NUMSTATES]; +extern const char *sprnames[]; + +/* Size before opt: 0000000000012604 + * Size after opt with sound: 0000000000007124 + * Size after opt without sound: 0000000000006028 + * + * Best improvement: 6576 bytes + * Best improvement: 6.42KB + */ + +extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; + +#endif /* __INFO__ */ diff --git a/games/NXDoom/src/doom/m_menu.c b/games/NXDoom/src/doom/m_menu.c new file mode 100644 index 00000000000..55dd9589f19 --- /dev/null +++ b/games/NXDoom/src/doom/m_menu.c @@ -0,0 +1,2154 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/m_menu.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * DOOM selection menu, options, episode etc. + * Sliders and icons. Kinda widget stuff. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomdef.h" +#include "doomkeys.h" +#include "dstrings.h" + +#include "d_main.h" +#include "deh_main.h" + +#include "i_input.h" +#include "i_joystick.h" +#include "i_swap.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_misc.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "r_local.h" + +#include "hu_stuff.h" + +#include "g_game.h" + +#include "m_argv.h" +#include "m_controls.h" +#include "p_saveg.h" +#include "p_setup.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "doomstat.h" + +/* Data. */ + +#include "m_menu.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SKULLXOFF -32 +#define LINEHEIGHT 16 + +#define JOY_BUTTON_MAPPED(x) ((x) >= 0) +#define JOY_BUTTON_PRESSED(x) \ + (JOY_BUTTON_MAPPED(x) && (ev->data1 & (1 << (x))) != 0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +void (*message_routine)(int response); + +/* 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 alpha_key; +} menuitem_t; + +typedef struct menu_s +{ + short numitems; /* # of menu items */ + struct menu_s *prev_menu; /* previous menu */ + menuitem_t *menu_items; /* menu items */ + void (*routine)(void); /* draw routine */ + short x; + short y; /* x,y of menu */ + short last_on; /* last item user was on in menu */ +} menu_t; + +/* DOOM MENU */ + +enum +{ + MAIN_NEWGAME = 0, + MAIN_OPTIONS, + MAIN_LOADGAME, + MAIN_SAVEGAME, + MAIN_READTHIS, + MAIN_QUITDOOM, + MAIN_END +} main_e; + +/* EPISODE SELECT */ + +enum +{ + EPISODE_EP1, + EPISODE_EP2, + EPISODE_EP3, + EPISODE_EP4, + EPISODE_END +} episodes_e; + +/* NEW GAME */ + +enum +{ + NEWGAME_KILLTHINGS, + NEWGAME_TOOROUGH, + NEWGAME_HURTME, + NEWGAME_VIOLANCE, + NEWGAME_NIGHTMARE, + NEWGAME_END +} newgame_e; + +/* OPTIONS MENU */ + +enum +{ + OPT_ENDGAME, + OPT_MESSAGES, + OPT_DETAIL, + OPT_SCRNSIZE, + OPT_EMPTY1, + OPT_MOUSESENS, + OPT_EMPTY2, + OPT_SOUNDVOL, + OPT_END +} options_e; + +enum +{ + RDTHSEMPTY1, + READ1_END +} read_e; + +enum +{ + RDTHSEMPTY2, + READ2_END +} read_e2; + +/* SOUND VOLUME MENU */ + +enum +{ + SOUND_SFXVOL, + SOUND_SFXEMPTY1, + SOUND_MUSICVOL, + SOUND_SFXEMPTY2, + SOUND_END +} sound_e; + +/* LOAD GAME MENU */ + +enum +{ + LOAD_1, + LOAD_2, + LOAD_3, + LOAD_4, + LOAD_5, + LOAD_6, + LOAD_END +} load_e; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void m_new_game(int choice); +static void m_episode(int choice); +static void m_choose_skill(int choice); +static void m_load_game(int choice); +static void m_save_game(int choice); +static void m_options(int choice); +static void m_end_game(int choice); +static void m_read_this(int choice); +static void m_read_this2(int choice); +static void m_quit_doom(int choice); + +static void m_change_messages(int choice); +static void m_change_sensitivity(int choice); +static void m_sfx_vol(int choice); +static void m_music_vol(int choice); +static void m_change_detail(int choice); +static void m_size_display(int choice); +static void m_sound(int choice); + +static void m_finish_read_this(int choice); +static void m_load_select(int choice); +static void m_save_select(int choice); +static void m_read_save_strings(void); +static void m_quick_save(void); +static void m_quick_load(void); + +static void m_drawmain_menu(void); +static void m_draw_read_this1(void); +static void m_draw_read_this2(void); +static void m_draw_new_game(void); +static void m_draw_episode(void); +static void m_draw_options(void); +static void m_draw_sound(void); +static void m_draw_load(void); +static void m_draw_save(void); + +static void m_draw_save_load_border(int x, int y); +static void m_setup_next_menu(menu_t *menudef); +static void m_draw_thermo(int x, int y, int therm_width, int therm_dot); +static void m_write_text(int x, int y, const char *string); +static int m_string_width(const char *string); +static int m_string_height(const char *string); +static void m_start_message(const char *string, + void *routine, boolean input); +static void m_clear_menus(void); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* defaulted values */ + +int g_mouse_sensitivity = 5; + +/* Show messages has default, 0 = off, 1 = on */ + +int g_show_messages = 1; + +/* Blocky mode, has default, 0 = high, 1 = normal */ + +int g_detail_level = 0; +int screenblocks = 9; + +boolean g_menuactive; +boolean inhelpscreens; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* message x & y */ + +static int g_messx; +static int g_messy; +static int g_message_last_menu_active; + +/* timed message = no input from user */ + +static boolean g_message_needs_input; + +static char g_gammamsg[5][26] = +{ + GAMMALVL0, GAMMALVL1, GAMMALVL2, GAMMALVL3, GAMMALVL4, +}; + +/* we are going to be entering a savegame string */ + +static int g_save_string_enter; +static int g_save_slot; /* which slot to save in */ +static int g_save_char_index; /* which char we're editing */ + +/* old save description before edit */ + +static char g_save_old_string[SAVESTRINGSIZE]; + +static char g_save_game_strings[10][SAVESTRINGSIZE]; + +static char g_endstring[160]; + +static short g_item_on; /* menu item skull is on */ +static short g_skull_anim_counter; /* skull animation counter */ +static short g_which_skull; /* which skull to draw */ + +/* temp for screenblocks (0-9) */ + +static int g_screen_size; + +/* -1 = no quicksave slot picked! */ + +static int g_quick_save_slot; + +/* 1 = message to be printed */ + +static int g_message_to_print; + +/* ...and here is the message string! */ + +static const char *g_message_string; + +static int g_epi; + +/* graphic name of skulls + * warning: initializer-string for array of chars is too long + */ + +static const char *g_skull_name[2] = +{ + "M_SKULL1", + "M_SKULL2", +}; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +static int g_quitsounds[8] = +{ + SFX_PLDETH, SFX_DMPAIN, SFX_POPAIN, SFX_SLOP, + SFX_TELEPT, SFX_POSIT1, SFX_POSIT3, SFX_SGTATK, +}; + +static int g_quitsounds2[8] = +{ + SFX_VILACT, SFX_GETPOW, SFX_BOSCUB, SFX_SLOP, + SFX_SKESWG, SFX_KNTDTH, SFX_BSPACT, SFX_SGTATK, +}; +#endif + +/* was the save action initiated by joypad? */ + +static boolean g_joypad_save = false; + +static char g_tempstring[90]; + +static const char *g_detail_names[2] = +{ + "M_GDHIGH", + "M_GDLOW", +}; + +static const char *g_msg_names[2] = +{ + "M_MSGOFF", + "M_MSGON", +}; + +/* current menudef */ + +static menu_t *g_current_menu; + +static menuitem_t g_main_menu[] = +{ + {1, "M_NGAME", m_new_game, 'n'}, + {1, "M_OPTION", m_options, 'o'}, + {1, "M_LOADG", m_load_game, 'l'}, + {1, "M_SAVEG", m_save_game, 's'}, + {1, "M_RDTHIS", m_read_this, 'r'}, /* Another hiccup with Special edition. */ + {1, "M_QUITG", m_quit_doom, 'q'}, +}; + +static menu_t g_main_def = +{ + MAIN_END, NULL, g_main_menu, m_drawmain_menu, 97, 64, 0, +}; + +static menuitem_t g_episode_menu[] = +{ + {1, "M_EPI1", m_episode, 'k'}, + {1, "M_EPI2", m_episode, 't'}, + {1, "M_EPI3", m_episode, 'i'}, + {1, "M_EPI4", m_episode, 't'}, +}; + +static menu_t g_epi_def = +{ + EPISODE_END, /* # of menu items */ + &g_main_def, /* previous menu */ + g_episode_menu, /* menuitem_t -> */ + m_draw_episode, /* drawing routine -> */ + 48, + 63, /* x,y */ + EPISODE_EP1 /* lastOn */ +}; + +static menuitem_t new_game_menu[] = +{ + {1, "M_JKILL", m_choose_skill, 'i'}, + {1, "M_ROUGH", m_choose_skill, 'h'}, + {1, "M_HURT", m_choose_skill, 'h'}, + {1, "M_ULTRA", m_choose_skill, 'u'}, + {1, "M_NMARE", m_choose_skill, 'n'}, +}; + +static menu_t g_new_def = +{ + NEWGAME_END, /* # of menu items */ + &g_epi_def, /* previous menu */ + new_game_menu, /* menuitem_t -> */ + m_draw_new_game, /* drawing routine -> */ + 48, + 63, /* x,y */ + NEWGAME_HURTME /* lastOn */ +}; + +static menuitem_t g_options_menu[] = +{ + { + 1, + "M_ENDGAM", + m_end_game, + 'e', + }, + { + 1, + "M_MESSG", + m_change_messages, + 'm', + }, + { + 1, + "M_DETAIL", + m_change_detail, + 'g', + }, + { + 2, + "M_SCRNSZ", + m_size_display, + 's', + }, + { + -1, + "", + 0, + '\0', + }, + { + 2, + "M_MSENS", + m_change_sensitivity, + 'm', + }, + { + -1, + "", + 0, + '\0', + }, + { + 1, + "M_SVOL", + m_sound, + 's', + }, +}; + +static menu_t g_options_def = +{ + OPT_END, &g_main_def, g_options_menu, m_draw_options, 60, 37, 0, +}; + +/* Read This! MENU 1 & 2 */ + +static menuitem_t g_read_menu1[] = +{ + {1, "", m_read_this2, 0}, +}; + +static menu_t g_read_def1 = +{ + READ1_END, &g_main_def, g_read_menu1, m_draw_read_this1, 280, 185, 0, +}; + +static menuitem_t g_read_menu2[] = +{ + {1, "", m_finish_read_this, 0}, +}; + +static menu_t g_read_def2 = +{ + READ2_END, &g_read_def1, g_read_menu2, m_draw_read_this2, 330, 175, 0, +}; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +static menuitem_t g_sound_menu[] = +{ + { + 2, + "M_SFXVOL", + m_sfx_vol, + 's', + }, + { + -1, + "", + 0, + '\0', + }, + { + 2, + "M_MUSVOL", + m_music_vol, + 'm', + }, + { + -1, + "", + 0, + '\0', + }, +}; + +static menu_t g_sound_def = +{ + SOUND_END, &g_options_def, g_sound_menu, m_draw_sound, 80, 64, 0, +}; +#endif + +static menuitem_t g_load_menu[] = +{ + {1, "", m_load_select, '1'}, + {1, "", m_load_select, '2'}, + {1, "", m_load_select, '3'}, + {1, "", m_load_select, '4'}, + {1, "", m_load_select, '5'}, + {1, "", m_load_select, '6'}, +}; + +static menu_t g_load_def = +{ + LOAD_END, &g_main_def, g_load_menu, m_draw_load, 80, 54, 0, +}; + +/* SAVE GAME MENU */ + +static menuitem_t g_save_menu[] = +{ + {1, "", m_save_select, '1'}, + {1, "", m_save_select, '2'}, + {1, "", m_save_select, '3'}, + {1, "", m_save_select, '4'}, + {1, "", m_save_select, '5'}, + {1, "", m_save_select, '6'}, +}; + +static menu_t g_save_def = +{ + LOAD_END, &g_main_def, g_save_menu, m_draw_save, 80, 54, 0, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Generate a default save slot name when the user saves to + * an empty slot via the joypad. + */ + +static void set_default_save_name(int slot) +{ + /* map from IWAD or PWAD? */ + + if (w_is_iwad_lump(maplumpinfo) && strcmp(savegamedir, "")) + { + snprintf(g_save_game_strings[g_item_on], SAVESTRINGSIZE, "%s", + maplumpinfo->name); + } + else + { + char *wadname = m_string_duplicate(w_wad_name_for_lump(maplumpinfo)); + char *ext = strrchr(wadname, '.'); + + if (ext != NULL) + { + *ext = '\0'; + } + + snprintf(g_save_game_strings[g_item_on], SAVESTRINGSIZE, "%s (%s)", + maplumpinfo->name, wadname); + free(wadname); + } + + m_force_uppercase(g_save_game_strings[g_item_on]); + g_joypad_save = false; +} + +/* m_responder calls this when user is finished */ + +static void m_do_save(int slot) +{ + g_save_game(slot, g_save_game_strings[slot]); + m_clear_menus(); + + /* PICK QUICKSAVE SLOT YET? */ + + if (g_quick_save_slot == -2) g_quick_save_slot = slot; +} + +static void m_quick_save_response(int key) +{ + if (key == key_menu_confirm) + { + m_do_save(g_quick_save_slot); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHX); +#endif + } +} + +static void m_quick_load_response(int key) +{ + if (key == key_menu_confirm) + { + m_load_select(g_quick_save_slot); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHX); +#endif + } +} + +/* These keys evaluate to a "null" key in Vanilla Doom that allows weird + * jumping in the menus. Preserve this behavior for accuracy. + */ + +static boolean is_null_key(int key) +{ + return key == KEY_PAUSE || key == KEY_CAPSLOCK || key == KEY_SCRLCK || + key == KEY_NUMLOCK; +} + +static void m_start_message(const char *string, void *routine, boolean input) +{ + g_message_last_menu_active = g_menuactive; + g_message_to_print = 1; + g_message_string = string; + message_routine = routine; + g_message_needs_input = input; + g_menuactive = true; + return; +} + +static const char *m_select_end_message(void) +{ + const char **endmsg; + + if (logical_gamemission == doom) + { + /* Doom 1 */ + + endmsg = doom1_endmsg; + } + else + { + /* Doom 2 */ + + endmsg = doom2_endmsg; + } + + return endmsg[gametic % NUM_QUITMESSAGES]; +} + +static void m_quit_response(int key) +{ + if (key != key_menu_confirm) return; + if (!netgame) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gamemode == commercial) + s_start_sound(NULL, g_quitsounds2[(gametic >> 2) & 7]); + else + s_start_sound(NULL, g_quitsounds[(gametic >> 2) & 7]); +#endif + i_wait_vbl(105); + } + + i_quit(); +} + +static void m_end_game_response(int key) +{ + if (key != key_menu_confirm) return; + + g_current_menu->last_on = g_item_on; + m_clear_menus(); + d_start_title(); +} + +static void m_draw_read_this_commercial(void) +{ + inhelpscreens = true; + + v_draw_patch_direct(0, 0, w_cache_lump_name(("HELP"), PU_CACHE)); +} + +static void m_verify_nightmare(int key) +{ + if (key != key_menu_confirm) return; + + g_deferred_init_new(NEWGAME_NIGHTMARE, g_epi + 1, 1); + m_clear_menus(); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* read the strings from the savegame files */ + +void m_read_save_strings(void) +{ + FILE *handle; + int i; + char name[256]; + + for (i = 0; i < LOAD_END; i++) + { + int retval; + m_str_copy(name, p_save_game_file(i), sizeof(name)); + + handle = fopen(name, "rb"); + if (handle == NULL) + { + m_str_copy(g_save_game_strings[i], EMPTYSTRING, SAVESTRINGSIZE); + g_load_menu[i].status = 0; + continue; + } + + retval = fread(&g_save_game_strings[i], 1, SAVESTRINGSIZE, handle); + fclose(handle); + g_load_menu[i].status = retval == SAVESTRINGSIZE; + } +} + +void m_draw_load(void) +{ + int i; + + v_draw_patch_direct(72, 28, w_cache_lump_name(("M_LOADG"), PU_CACHE)); + + for (i = 0; i < LOAD_END; i++) + { + m_draw_save_load_border(g_load_def.x, g_load_def.y + LINEHEIGHT * i); + m_write_text(g_load_def.x, g_load_def.y + LINEHEIGHT * i, + g_save_game_strings[i]); + } +} + +/* Draw border for the savegame description */ + +void m_draw_save_load_border(int x, int y) +{ + int i; + + v_draw_patch_direct(x - 8, y + 7, + w_cache_lump_name(("M_LSLEFT"), PU_CACHE)); + + for (i = 0; i < 24; i++) + { + v_draw_patch_direct(x, y + 7, + w_cache_lump_name(("M_LSCNTR"), PU_CACHE)); + x += 8; + } + + v_draw_patch_direct(x, y + 7, w_cache_lump_name(("M_LSRGHT"), PU_CACHE)); +} + +/* User wants to load this game */ + +void m_load_select(int choice) +{ + char name[256]; + + m_str_copy(name, p_save_game_file(choice), sizeof(name)); + + g_load_game(name); + m_clear_menus(); +} + +/* Selected from DOOM menu */ + +void m_load_game(int choice) +{ + if (netgame) + { + m_start_message((LOADNET), NULL, false); + return; + } + + m_setup_next_menu(&g_load_def); + m_read_save_strings(); +} + +/* M_SaveGame & Cie. */ + +void m_draw_save(void) +{ + int i; + + v_draw_patch_direct(72, 28, w_cache_lump_name(("M_SAVEG"), PU_CACHE)); + for (i = 0; i < LOAD_END; i++) + { + m_draw_save_load_border(g_load_def.x, g_load_def.y + LINEHEIGHT * i); + m_write_text(g_load_def.x, g_load_def.y + LINEHEIGHT * i, + g_save_game_strings[i]); + } + + if (g_save_string_enter) + { + i = m_string_width(g_save_game_strings[g_save_slot]); + m_write_text(g_load_def.x + i, + g_load_def.y + LINEHEIGHT * g_save_slot, "_"); + } +} + +/* User wants to save. Start string input for m_responder */ + +void m_save_select(int choice) +{ + int x; + int y; + + /* we are going to be intercepting all chars */ + + g_save_string_enter = 1; + + /* We need to turn on text input: */ + + x = g_load_def.x - 11; + y = g_load_def.y + choice * LINEHEIGHT - 4; + i_start_text_input(x, y, x + 8 + 24 * 8 + 8, y + LINEHEIGHT - 2); + + g_save_slot = choice; + m_str_copy(g_save_old_string, g_save_game_strings[choice], SAVESTRINGSIZE); + if (!strcmp(g_save_game_strings[choice], EMPTYSTRING)) + { + g_save_game_strings[choice][0] = 0; + + if (g_joypad_save) + { + set_default_save_name(choice); + } + } + + g_save_char_index = strlen(g_save_game_strings[choice]); +} + +/* Selected from DOOM menu */ + +void m_save_game(int choice) +{ + if (!usergame) + { + m_start_message((SAVEDEAD), NULL, false); + return; + } + + if (gamestate != GS_LEVEL) return; + + m_setup_next_menu(&g_save_def); + m_read_save_strings(); +} + +void m_quick_save(void) +{ + if (!usergame) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return; + } + + if (gamestate != GS_LEVEL) return; + + if (g_quick_save_slot < 0) + { + m_start_control_panel(); + m_read_save_strings(); + m_setup_next_menu(&g_save_def); + g_quick_save_slot = -2; /* means to pick a slot now */ + return; + } + + snprintf(g_tempstring, sizeof(g_tempstring), QSPROMPT, + g_save_game_strings[g_quick_save_slot]); + m_start_message(g_tempstring, m_quick_save_response, true); +} + +void m_quick_load(void) +{ + if (netgame) + { + m_start_message((QLOADNET), NULL, false); + return; + } + + if (g_quick_save_slot < 0) + { + m_start_message((QSAVESPOT), NULL, false); + return; + } + + snprintf(g_tempstring, sizeof(g_tempstring), QLPROMPT, + g_save_game_strings[g_quick_save_slot]); + m_start_message(g_tempstring, m_quick_load_response, true); +} + +/* Read This Menus + * Had a "quick hack to fix romero bug" + */ + +void m_draw_read_this1(void) +{ + inhelpscreens = true; + + v_draw_patch_direct(0, 0, w_cache_lump_name(("HELP2"), PU_CACHE)); +} + +/* Read This Menus - optional second page. */ + +void m_draw_read_this2(void) +{ + inhelpscreens = true; + + /* We only ever draw the second page if this is + * gameversion == exe_doom_1_9 and gamemode == registered + */ + + v_draw_patch_direct(0, 0, w_cache_lump_name(("HELP1"), PU_CACHE)); +} + +/* Change Sfx & Music volumes */ + +void m_draw_sound(void) +{ + v_draw_patch_direct(60, 38, w_cache_lump_name(("M_SVOL"), PU_CACHE)); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + m_draw_thermo(g_sound_def.x, + g_sound_def.y + LINEHEIGHT * (SOUND_SFXVOL + 1), 16, + g_sfx_volume); + + m_draw_thermo(g_sound_def.x, + g_sound_def.y + LINEHEIGHT * (SOUND_MUSICVOL + 1), 16, + g_music_volume); +#endif +} + +void m_sound(int choice) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + m_setup_next_menu(&g_sound_def); +#endif +} + +void m_sfx_vol(int choice) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + switch (choice) + { + case 0: + if (g_sfx_volume) g_sfx_volume--; + break; + case 1: + if (g_sfx_volume < 15) g_sfx_volume++; + break; + } + + s_set_sfx_volume(g_sfx_volume * 8); +#endif +} + +void m_music_vol(int choice) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + switch (choice) + { + case 0: + if (g_music_volume) g_music_volume--; + break; + case 1: + if (g_music_volume < 15) g_music_volume++; + break; + } + + s_set_music_volume(g_music_volume * 8); +#endif +} + +void m_drawmain_menu(void) +{ + v_draw_patch_direct(94, 2, w_cache_lump_name(("M_DOOM"), PU_CACHE)); +} + +void m_draw_new_game(void) +{ + v_draw_patch_direct(96, 14, w_cache_lump_name(("M_NEWG"), PU_CACHE)); + v_draw_patch_direct(54, 38, w_cache_lump_name(("M_SKILL"), PU_CACHE)); +} + +void m_new_game(int choice) +{ + if (netgame && !demoplayback) + { + m_start_message((NEWGAME), NULL, false); + return; + } + + /* Chex Quest disabled the episode select screen, as did Doom II. */ + + if (gamemode == commercial || gameversion == exe_chex) + m_setup_next_menu(&g_new_def); + else + m_setup_next_menu(&g_epi_def); +} + +void m_draw_episode(void) +{ + v_draw_patch_direct(54, 38, w_cache_lump_name(("M_EPISOD"), PU_CACHE)); +} + +void m_choose_skill(int choice) +{ + if (choice == NEWGAME_NIGHTMARE) + { + m_start_message((NIGHTMARE), m_verify_nightmare, true); + return; + } + + g_deferred_init_new(choice, g_epi + 1, 1); + m_clear_menus(); +} + +void m_episode(int choice) +{ + if ((gamemode == shareware) && choice) + { + m_start_message((SWSTRING), NULL, false); + m_setup_next_menu(&g_read_def1); + return; + } + + g_epi = choice; + m_setup_next_menu(&g_new_def); +} + +void m_draw_options(void) +{ + v_draw_patch_direct(108, 15, w_cache_lump_name(("M_OPTTTL"), PU_CACHE)); + + v_draw_patch_direct( + g_options_def.x + 175, g_options_def.y + LINEHEIGHT * OPT_DETAIL, + w_cache_lump_name((g_detail_names[g_detail_level]), PU_CACHE)); + + v_draw_patch_direct( + g_options_def.x + 120, g_options_def.y + LINEHEIGHT * OPT_MESSAGES, + w_cache_lump_name((g_msg_names[g_show_messages]), PU_CACHE)); + + m_draw_thermo(g_options_def.x, + g_options_def.y + LINEHEIGHT * (OPT_MOUSESENS + 1), 10, + g_mouse_sensitivity); + + m_draw_thermo(g_options_def.x, + g_options_def.y + LINEHEIGHT * (OPT_SCRNSIZE + 1), 9, + g_screen_size); +} + +void m_options(int choice) +{ + m_setup_next_menu(&g_options_def); +} + +/* Toggle messages on/off */ + +void m_change_messages(int choice) +{ + /* warning: unused parameter `int choice' */ + + choice = 0; + g_show_messages = 1 - g_show_messages; + + if (!g_show_messages) + players[consoleplayer].message = (MSGOFF); + else + players[consoleplayer].message = (MSGON); + + message_dontfuckwithme = true; +} + +void m_end_game(int choice) +{ + choice = 0; + if (!usergame) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return; + } + + if (netgame) + { + m_start_message((NETEND), NULL, false); + return; + } + + m_start_message((ENDGAME), m_end_game_response, true); +} + +void m_read_this(int choice) +{ + choice = 0; + m_setup_next_menu(&g_read_def1); +} + +void m_read_this2(int choice) +{ + choice = 0; + m_setup_next_menu(&g_read_def2); +} + +void m_finish_read_this(int choice) +{ + choice = 0; + m_setup_next_menu(&g_main_def); +} + +void m_quit_doom(int choice) +{ + snprintf(g_endstring, sizeof(g_endstring), "%s\n\n" DOSY, + (m_select_end_message())); + + m_start_message(g_endstring, m_quit_response, true); +} + +void m_change_sensitivity(int choice) +{ + switch (choice) + { + case 0: + if (g_mouse_sensitivity) g_mouse_sensitivity--; + break; + case 1: + if (g_mouse_sensitivity < 9) g_mouse_sensitivity++; + break; + } +} + +void m_change_detail(int choice) +{ + choice = 0; + g_detail_level = 1 - g_detail_level; + + r_set_view_size(screenblocks, g_detail_level); + + if (!g_detail_level) + players[consoleplayer].message = (DETAILHI); + else + players[consoleplayer].message = (DETAILLO); +} + +void m_size_display(int choice) +{ + switch (choice) + { + case 0: + if (g_screen_size > 0) + { + screenblocks--; + g_screen_size--; + } + break; + case 1: + if (g_screen_size < 8) + { + screenblocks++; + g_screen_size++; + } + break; + } + + r_set_view_size(screenblocks, g_detail_level); +} + +/* Menu Functions */ + +void m_draw_thermo(int x, int y, int therm_width, int therm_dot) +{ + int xx; + int i; + + xx = x; + v_draw_patch_direct(xx, y, w_cache_lump_name(("M_THERML"), PU_CACHE)); + xx += 8; + for (i = 0; i < therm_width; i++) + { + v_draw_patch_direct(xx, y, w_cache_lump_name(("M_THERMM"), PU_CACHE)); + xx += 8; + } + + v_draw_patch_direct(xx, y, w_cache_lump_name(("M_THERMR"), PU_CACHE)); + + v_draw_patch_direct((x + 8) + therm_dot * 8, y, + w_cache_lump_name(("M_THERMO"), PU_CACHE)); +} + +/* Find string width from hu_font chars */ + +int m_string_width(const char *string) +{ + size_t i; + int w = 0; + int c; + + for (i = 0; i < strlen(string); i++) + { + c = 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_string_height(const char *string) +{ + size_t i; + int h; + int height = SHORT(hu_font[0]->height); + + h = height; + for (i = 0; i < strlen(string); i++) + { + if (string[i] == '\n') h += height; + } + + return h; +} + +/* Write a string using the hu_font */ + +void m_write_text(int x, int y, const char *string) +{ + int w; + const 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 = 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_draw_patch_direct(cx, cy, hu_font[c]); + cx += w; + } +} + +/* CONTROL PANEL */ + +boolean m_responder(event_t *ev) +{ + int ch; + int key; + int i; + static int mousewait = 0; + static int mousey = 0; + static int lasty = 0; + static int mousex = 0; + static int lastx = 0; + int dir; + + /* In testcontrols mode, none of the function keys should do anything + * - the only key is escape to quit. + */ + + if (testcontrols) + { + if (ev->type == ev_quit || + (ev->type == ev_keydown && + (ev->data1 == key_menu_activate || ev->data1 == key_menu_quit))) + { + i_quit(); + return true; + } + + return false; + } + + /* "close" button pressed on window? */ + + if (ev->type == ev_quit) + { + /* First click on close button = bring up quit confirm message. + * Second click on close button = confirm quit + */ + + if (g_menuactive && g_message_to_print && + message_routine == m_quit_response) + { + m_quit_response(key_menu_confirm); + } + else + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_quit_doom(0); + } + + return true; + } + + /* key is the key pressed, ch is the actual character typed */ + + ch = 0; + key = -1; + + if (ev->type == ev_joystick) + { + /* Simulate key presses from joystick events to interact with the menu. + */ + + if (g_menuactive) + { + if (JOY_GET_DPAD(ev->data6) != JOY_DIR_NONE) + { + dir = JOY_GET_DPAD(ev->data6); + } + else if (JOY_GET_LSTICK(ev->data6) != JOY_DIR_NONE) + { + dir = JOY_GET_LSTICK(ev->data6); + } + else + { + dir = JOY_GET_RSTICK(ev->data6); + } + + if (dir & JOY_DIR_UP) + { + key = key_menu_up; + joywait = i_get_time() + 5; + } + else if (dir & JOY_DIR_DOWN) + { + key = key_menu_down; + joywait = i_get_time() + 5; + } + + if (dir & JOY_DIR_LEFT) + { + key = key_menu_left; + joywait = i_get_time() + 5; + } + else if (dir & JOY_DIR_RIGHT) + { + key = key_menu_right; + joywait = i_get_time() + 5; + } + + if (JOY_BUTTON_PRESSED(joybfire)) + { + /* Simulate a 'Y' keypress when Doom show a Y/N dialog with + * Fire button. + */ + + if (g_message_to_print && g_message_needs_input) + { + key = key_menu_confirm; + } + + /* Simulate pressing "Enter" when we are supplying a save slot + * name + */ + + else if (g_save_string_enter) + { + key = KEY_ENTER; + } + else + { + /* if selecting a save slot via joypad, set a flag */ + + if (g_current_menu == &g_save_def) + { + g_joypad_save = true; + } + + key = key_menu_forward; + } + + joywait = i_get_time() + 5; + } + + if (JOY_BUTTON_PRESSED(joybuse)) + { + /* Simulate a 'N' keypress when Doom show a Y/N dialog with Use + * button. + */ + + if (g_message_to_print && g_message_needs_input) + { + key = key_menu_abort; + } + + /* If user was entering a save name, back out */ + + else if (g_save_string_enter) + { + key = KEY_ESCAPE; + } + else + { + key = key_menu_back; + } + + joywait = i_get_time() + 5; + } + } + + if (JOY_BUTTON_PRESSED(joybmenu)) + { + key = key_menu_activate; + joywait = i_get_time() + 5; + } + } + else + { + if (ev->type == ev_mouse && mousewait < i_get_time() && g_menuactive) + { + mousey += ev->data3; + if (mousey < lasty - 30) + { + key = key_menu_down; + mousewait = i_get_time() + 5; + mousey = lasty -= 30; + } + else if (mousey > lasty + 30) + { + key = key_menu_up; + mousewait = i_get_time() + 5; + mousey = lasty += 30; + } + + mousex += ev->data2; + if (mousex < lastx - 30) + { + key = key_menu_left; + mousewait = i_get_time() + 5; + mousex = lastx -= 30; + } + else if (mousex > lastx + 30) + { + key = key_menu_right; + mousewait = i_get_time() + 5; + mousex = lastx += 30; + } + + if (ev->data1 & 1) + { + key = key_menu_forward; + mousewait = i_get_time() + 15; + } + + if (ev->data1 & 2) + { + key = key_menu_back; + mousewait = i_get_time() + 15; + } + } + else + { + if (ev->type == ev_keydown) + { + key = ev->data1; + ch = ev->data2; + } + } + } + + if (key == -1) return false; + + /* Save Game string input */ + + if (g_save_string_enter) + { + switch (key) + { + case KEY_BACKSPACE: + if (g_save_char_index > 0) + { + g_save_char_index--; + g_save_game_strings[g_save_slot][g_save_char_index] = 0; + } + break; + + case KEY_ESCAPE: + g_save_string_enter = 0; + i_stop_text_input(); + m_str_copy(g_save_game_strings[g_save_slot], g_save_old_string, + SAVESTRINGSIZE); + break; + + case KEY_ENTER: + g_save_string_enter = 0; + i_stop_text_input(); + if (g_save_game_strings[g_save_slot][0]) m_do_save(g_save_slot); + break; + + default: + /* Savegame name entry. This is complicated. + * Vanilla has a bug where the shift key is ignored when entering + * a savegame name. If vanilla_keyboard_mapping is on, we want + * to emulate this bug by using ev->data1. But if it's turned off, + * it implies the user doesn't care about Vanilla emulation: + * instead, use ev->data3 which gives the fully-translated and + * modified key input. + */ + + if (ev->type != ev_keydown) + { + break; + } + + if (vanilla_keyboard_mapping) + { + ch = ev->data1; + } + else + { + ch = ev->data3; + } + + ch = toupper(ch); + + if (ch != ' ' && + (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE)) + { + break; + } + + if (ch >= 32 && ch <= 127 && + g_save_char_index < SAVESTRINGSIZE - 1 && + m_string_width(g_save_game_strings[g_save_slot]) < + (SAVESTRINGSIZE - 2) * 8) + { + g_save_game_strings[g_save_slot][g_save_char_index++] = ch; + g_save_game_strings[g_save_slot][g_save_char_index] = 0; + } + + break; + } + + return true; + } + + /* Take care of any messages that need input */ + + if (g_message_to_print) + { + if (g_message_needs_input) + { + if (key != ' ' && key != KEY_ESCAPE && key != key_menu_confirm && + key != key_menu_abort) + { + return false; + } + } + + g_menuactive = g_message_last_menu_active; + g_message_to_print = 0; + if (message_routine) message_routine(key); + + g_menuactive = false; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHX); +#endif + return true; + } + + if ((devparm && key == key_menu_help) || + (key != 0 && key == key_menu_screenshot)) + { + g_screenshot(); + return true; + } + + /* F-Keys */ + + if (!g_menuactive) + { + if (key == key_menu_decscreen) /* Screen size down */ + { + if (automapactive || chat_on) return false; + m_size_display(0); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_STNMOV); +#endif + return true; + } + else if (key == key_menu_incscreen) /* Screen size up */ + { + if (automapactive || chat_on) return false; + m_size_display(1); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_STNMOV); +#endif + return true; + } + else if (key == key_menu_help) /* Help key */ + { + m_start_control_panel(); + + if (gameversion >= exe_ultimate) + g_current_menu = &g_read_def2; + else + g_current_menu = &g_read_def1; + + g_item_on = 0; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + return true; + } + else if (key == key_menu_save) /* Save */ + { + m_start_control_panel(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_save_game(0); + return true; + } + else if (key == key_menu_load) /* Load */ + { + m_start_control_panel(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_load_game(0); + return true; + } + else if (key == key_menu_volume) /* Sound Volume */ + { + m_start_control_panel(); + g_item_on = SOUND_SFXVOL; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + g_current_menu = &g_sound_def; + s_start_sound(NULL, SFX_SWTCHN); +#endif + return true; + } + else if (key == key_menu_detail) /* Detail toggle */ + { + m_change_detail(0); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + return true; + } + else if (key == key_menu_qsave) /* Quicksave */ + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_quick_save(); + return true; + } + else if (key == key_menu_endgame) /* End game */ + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_end_game(0); + return true; + } + else if (key == key_menu_messages) /* Toggle messages */ + { + m_change_messages(0); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + return true; + } + else if (key == key_menu_qload) /* Quickload */ + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_quick_load(); + return true; + } + else if (key == key_menu_quit) /* Quit DOOM */ + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_quit_doom(0); + return true; + } + else if (key == key_menu_gamma) /* gamma toggle */ + { + usegamma++; + if (usegamma > 4) usegamma = 0; + players[consoleplayer].message = (g_gammamsg[usegamma]); + i_set_palette(w_cache_lump_name(("PLAYPAL"), PU_CACHE)); + return true; + } + } + + /* Pop-up menu? */ + + if (!g_menuactive) + { + if (key == key_menu_activate) + { + m_start_control_panel(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + return true; + } + + return false; + } + + /* Keys usable within menu */ + + if (key == key_menu_down) + { + /* Move down to next item */ + + do + { + if (g_item_on + 1 > g_current_menu->numitems - 1) + g_item_on = 0; + else + g_item_on++; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PSTOP); +#endif + } + while (g_current_menu->menu_items[g_item_on].status == -1); + + return true; + } + else if (key == key_menu_up) + { + /* Move back up to previous item */ + + do + { + if (!g_item_on) + g_item_on = g_current_menu->numitems - 1; + else + g_item_on--; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PSTOP); +#endif + } + while (g_current_menu->menu_items[g_item_on].status == -1); + + return true; + } + else if (key == key_menu_left) + { + /* Slide slider left */ + + if (g_current_menu->menu_items[g_item_on].routine && + g_current_menu->menu_items[g_item_on].status == 2) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_STNMOV); +#endif + g_current_menu->menu_items[g_item_on].routine(0); + } + + return true; + } + else if (key == key_menu_right) + { + /* Slide slider right */ + + if (g_current_menu->menu_items[g_item_on].routine && + g_current_menu->menu_items[g_item_on].status == 2) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_STNMOV); +#endif + g_current_menu->menu_items[g_item_on].routine(1); + } + + return true; + } + else if (key == key_menu_forward) + { + /* Activate menu item */ + + if (g_current_menu->menu_items[g_item_on].routine && + g_current_menu->menu_items[g_item_on].status) + { + g_current_menu->last_on = g_item_on; + if (g_current_menu->menu_items[g_item_on].status == 2) + { + g_current_menu->menu_items[g_item_on].routine( + 1); /* right arrow */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_STNMOV); +#endif + } + else + { + g_current_menu->menu_items[g_item_on].routine(g_item_on); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PISTOL); +#endif + } + } + + return true; + } + else if (key == key_menu_activate) + { + /* Deactivate menu */ + + g_current_menu->last_on = g_item_on; + m_clear_menus(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHX); +#endif + return true; + } + else if (key == key_menu_back) + { + /* Go back to previous menu */ + + g_current_menu->last_on = g_item_on; + if (g_current_menu->prev_menu) + { + g_current_menu = g_current_menu->prev_menu; + g_item_on = g_current_menu->last_on; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + } + + return true; + } + + /* Keyboard shortcut? + * Vanilla Doom has a weird behavior where it jumps to the scroll bars + * when the certain keys are pressed, so emulate this. + */ + + else if (ch != 0 || is_null_key(key)) + { + for (i = g_item_on + 1; i < g_current_menu->numitems; i++) + { + if (g_current_menu->menu_items[i].alpha_key == ch) + { + g_item_on = i; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PSTOP); +#endif + return true; + } + } + + for (i = 0; i <= g_item_on; i++) + { + if (g_current_menu->menu_items[i].alpha_key == ch) + { + g_item_on = i; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PSTOP); +#endif + return true; + } + } + } + + return false; +} + +void m_start_control_panel(void) +{ + /* intro might call this repeatedly */ + + if (g_menuactive) return; + + g_menuactive = 1; + g_current_menu = &g_main_def; /* JDC */ + g_item_on = g_current_menu->last_on; /* JDC */ +} + +/* Display OPL debug messages - hack for GENMIDI development. */ + +#if 0 +static void m_draw_opl_dev(void) +{ + char debug[1024]; + char *curr; + char *p; + int line; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + i_opl_dev_messages(debug, sizeof(debug)); +#endif + curr = debug; + line = 0; + + for (; ; ) + { + p = strchr(curr, '\n'); + + if (p != NULL) + { + *p = '\0'; + } + + m_write_text(0, line * 8, curr); + ++line; + + if (p == NULL) + { + break; + } + + curr = p + 1; + } +} +#endif + +/* 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; + unsigned int i; + unsigned int max; + char string[80]; + const char *name; + int start; + + inhelpscreens = false; + + /* Horiz. & Vertically center string and print it. */ + + if (g_message_to_print) + { + start = 0; + y = SCREENHEIGHT / 2 - m_string_height(g_message_string) / 2; + while (g_message_string[start] != '\0') + { + boolean foundnewline = false; + + for (i = 0; g_message_string[start + i] != '\0'; i++) + { + if (g_message_string[start + i] == '\n') + { + m_str_copy(string, g_message_string + start, + sizeof(string)); + if (i < sizeof(string)) + { + string[i] = '\0'; + } + + foundnewline = true; + start += i + 1; + break; + } + } + + if (!foundnewline) + { + m_str_copy(string, g_message_string + start, sizeof(string)); + start += strlen(string); + } + + x = SCREENWIDTH / 2 - m_string_width(string) / 2; + m_write_text(x, y, string); + y += SHORT(hu_font[0]->height); + } + + return; + } + + if (!g_menuactive) return; + + if (g_current_menu->routine) + g_current_menu->routine(); /* call Draw routine */ + + /* DRAW MENU */ + + x = g_current_menu->x; + y = g_current_menu->y; + max = g_current_menu->numitems; + + for (i = 0; i < max; i++) + { + name = (g_current_menu->menu_items[i].name); + + if (name[0] && w_check_num_for_name(name) > 0) + { + v_draw_patch_direct(x, y, w_cache_lump_name(name, PU_CACHE)); + } + + y += LINEHEIGHT; + } + + /* DRAW SKULL */ + + v_draw_patch_direct( + x + SKULLXOFF, g_current_menu->y - 5 + g_item_on * LINEHEIGHT, + w_cache_lump_name((g_skull_name[g_which_skull]), PU_CACHE)); +} + +void m_clear_menus(void) +{ + g_menuactive = 0; +} + +void m_setup_next_menu(menu_t *menudef) +{ + g_current_menu = menudef; + g_item_on = g_current_menu->last_on; +} + +void m_ticker(void) +{ + if (--g_skull_anim_counter <= 0) + { + g_which_skull ^= 1; + g_skull_anim_counter = 8; + } +} + +void m_init(void) +{ + g_current_menu = &g_main_def; + g_menuactive = 0; + g_item_on = g_current_menu->last_on; + g_which_skull = 0; + g_skull_anim_counter = 10; + g_screen_size = screenblocks - 3; + g_message_to_print = 0; + g_message_string = NULL; + g_message_last_menu_active = g_menuactive; + g_quick_save_slot = -1; + + /* Here we could catch other version dependencies, like HELP1/2, and four + * episodes. + */ + + /* The same hacks were used in the original Doom EXEs. */ + + if (gameversion >= exe_ultimate) + { + g_main_menu[MAIN_READTHIS].routine = m_read_this2; + g_read_def2.prev_menu = NULL; + } + + if (gameversion >= exe_final && gameversion <= exe_final2) + { + g_read_def2.routine = m_draw_read_this_commercial; + } + + if (gamemode == commercial) + { + g_main_menu[MAIN_READTHIS] = g_main_menu[MAIN_QUITDOOM]; + g_main_def.numitems--; + g_main_def.y += 8; + g_new_def.prev_menu = &g_main_def; + g_read_def1.routine = m_draw_read_this_commercial; + g_read_def1.x = 330; + g_read_def1.y = 165; + g_read_menu1[RDTHSEMPTY1].routine = m_finish_read_this; + } + + /* Versions of doom.exe before the Ultimate Doom release only had + * three episodes; if we're emulating one of those then don't try + * to show episode four. If we are, then do show episode four + * (should crash if missing). + */ + + if (gameversion < exe_ultimate) + { + g_epi_def.numitems--; + } + + /* chex.exe shows only one episode. */ + + else if (gameversion == exe_chex) + { + g_epi_def.numitems = 1; + } +} diff --git a/games/NXDoom/src/doom/m_menu.h b/games/NXDoom/src/doom/m_menu.h new file mode 100644 index 00000000000..545f3eb8a65 --- /dev/null +++ b/games/NXDoom/src/doom/m_menu.h @@ -0,0 +1,76 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/m_menu.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Menu widget stuff, episode selection and such. + * + ****************************************************************************/ + +#ifndef __M_MENU__ +#define __M_MENU__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int g_detail_level; +extern int screenblocks; + +extern boolean inhelpscreens; +extern int g_show_messages; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* 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. + */ + +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_doom_main, 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_start_control_panel(void); + +#endif /* __M_MENU__ */ diff --git a/games/NXDoom/src/doom/m_random.c b/games/NXDoom/src/doom/m_random.c new file mode 100644 index 00000000000..1c846cc0c46 --- /dev/null +++ b/games/NXDoom/src/doom/m_random.c @@ -0,0 +1,90 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/m_random.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Random number LUT. + * + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* m_random + * Returns a 0-255 number + */ + +static const 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, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int rndindex = 0; +int prndindex = 0; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* 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_clear_random(void) +{ + rndindex = prndindex = 0; +} + +/* inspired by the same routine in Eternity, thanks haleyjd */ + +int p_sub_random(void) +{ + int r = p_random(); + return r - p_random(); +} diff --git a/games/NXDoom/src/doom/m_random.h b/games/NXDoom/src/doom/m_random.h new file mode 100644 index 00000000000..f5f5cf74a37 --- /dev/null +++ b/games/NXDoom/src/doom/m_random.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/m_random.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef __M_RANDOM__ +#define __M_RANDOM__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* 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_clear_random(void); + +/* Defined version of p_random() - p_random() */ + +int p_sub_random(void); + +#endif /* __M_RANDOM__ */ diff --git a/games/NXDoom/src/doom/p_ceilng.c b/games/NXDoom/src/doom/p_ceilng.c new file mode 100644 index 00000000000..582ad604e5b --- /dev/null +++ b/games/NXDoom/src/doom/p_ceilng.c @@ -0,0 +1,327 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_ceilng.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: Ceiling aninmation (lowering, crushing, raising) + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" +#include "p_local.h" +#include "z_zone.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* CEILINGS */ + +ceiling_t *activeceilings[MAXCEILINGS]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* t_move_ceiling */ + +void t_move_ceiling(ceiling_t *ceiling) +{ + result_e res; + + switch (ceiling->direction) + { + case 0: /* IN STASIS */ + break; + case 1: /* UP */ + res = t_move_plane(ceiling->sector, ceiling->speed, ceiling->topheight, + false, 1, ceiling->direction); + + if (!(leveltime & 7)) + { + switch (ceiling->type) + { + case CEIL_SILENTCRUSHANDRAISE: + break; + default: +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&ceiling->sector->soundorg, SFX_STNMOV); +#endif + /* ? */ + + break; + } + } + + if (res == pastdest) + { + switch (ceiling->type) + { + case CEIL_RAISETOHIGHEST: + p_remove_active_ceiling(ceiling); + break; + + case CEIL_SILENTCRUSHANDRAISE: +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&ceiling->sector->soundorg, SFX_PSTOP); +#endif + case CEIL_FASTCRUSHANDRAISE: + case CEIL_CRUSHANDRAISE: + ceiling->direction = -1; + break; + + default: + break; + } + } + break; + + case -1: /* DOWN */ + res = t_move_plane(ceiling->sector, ceiling->speed, + ceiling->bottomheight, ceiling->crush, 1, ceiling->direction); + + if (!(leveltime & 7)) + { + switch (ceiling->type) + { + case CEIL_SILENTCRUSHANDRAISE: + break; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + default: + s_start_sound(&ceiling->sector->soundorg, SFX_STNMOV); +#else + default: + break; +#endif + } + } + + if (res == pastdest) + { + switch (ceiling->type) + { + case CEIL_SILENTCRUSHANDRAISE: +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&ceiling->sector->soundorg, SFX_PSTOP); +#endif + case CEIL_CRUSHANDRAISE: + ceiling->speed = CEILSPEED; + case CEIL_FASTCRUSHANDRAISE: + ceiling->direction = 1; + break; + + case CEIL_LOWERANDCRUSH: + case CEIL_LOWERTOFLOOR: + p_remove_active_ceiling(ceiling); + break; + + default: + break; + } + } + else /* ( res != pastdest ) */ + { + if (res == crushed) + { + switch (ceiling->type) + { + case CEIL_SILENTCRUSHANDRAISE: + case CEIL_CRUSHANDRAISE: + case CEIL_LOWERANDCRUSH: + ceiling->speed = CEILSPEED / 8; + break; + + default: + break; + } + } + } + break; + } +} + +/* ev_do_ceiling + * Move a ceiling up/down and all around! + */ + +int ev_do_ceiling(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 CEIL_FASTCRUSHANDRAISE: + case CEIL_SILENTCRUSHANDRAISE: + case CEIL_CRUSHANDRAISE: + p_activate_in_stasis_ceiling(line); + default: + break; + } + + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) continue; + + /* new door thinker */ + + rtn = 1; + ceiling = z_malloc(sizeof(*ceiling), PU_LEVSPEC, 0); + p_add_thinker(&ceiling->thinker); + sec->specialdata = ceiling; + ceiling->thinker.function.acp1 = (actionf_p1)t_move_ceiling; + ceiling->sector = sec; + ceiling->crush = false; + + switch (type) + { + case CEIL_FASTCRUSHANDRAISE: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8 * FRACUNIT); + ceiling->direction = -1; + ceiling->speed = CEILSPEED * 2; + break; + + case CEIL_SILENTCRUSHANDRAISE: + case CEIL_CRUSHANDRAISE: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + case CEIL_LOWERANDCRUSH: + case CEIL_LOWERTOFLOOR: + ceiling->bottomheight = sec->floorheight; + if (type != CEIL_LOWERTOFLOOR) + { + ceiling->bottomheight += 8 * FRACUNIT; + } + + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + case CEIL_RAISETOHIGHEST: + ceiling->topheight = p_find_heighest_ceiling_surrounding(sec); + ceiling->direction = 1; + ceiling->speed = CEILSPEED; + break; + } + + ceiling->tag = sec->tag; + ceiling->type = type; + p_add_active_ceiling(ceiling); + } + + return rtn; +} + +/* Add an active ceiling */ + +void p_add_active_ceiling(ceiling_t *c) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] == NULL) + { + activeceilings[i] = c; + return; + } + } +} + +/* Remove a ceiling's thinker */ + +void p_remove_active_ceiling(ceiling_t *c) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] == c) + { + activeceilings[i]->sector->specialdata = NULL; + p_remove_thinker(&activeceilings[i]->thinker); + activeceilings[i] = NULL; + break; + } + } +} + +/* Restart a ceiling that's in-stasis */ + +void p_activate_in_stasis_ceiling(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_move_ceiling; + } + } +} + +/* ev_ceiling_crush_stop + * Stop a ceiling from crushing! + */ + +int ev_ceiling_crush_stop(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)NULL; + activeceilings[i]->direction = 0; /* in-stasis */ + rtn = 1; + } + } + + return rtn; +} diff --git a/games/NXDoom/src/doom/p_doors.c b/games/NXDoom/src/doom/p_doors.c new file mode 100644 index 00000000000..324fefa4bec --- /dev/null +++ b/games/NXDoom/src/doom/p_doors.c @@ -0,0 +1,802 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_doors.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: Door animation code (opening/closing) + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_main.h" +#include "doomdef.h" +#include "i_system.h" +#include "p_local.h" +#include "z_zone.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/* Data. */ + +#include "dstrings.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Sliding door frame information */ + +#if 0 +slidename_t g_slide_frame_names[MAXSLIDEDOORS] = +{ +{ + "GDOORF1", "GDOORF2", "GDOORF3", "GDOORF4", /* front */ + "GDOORB1", "GDOORB2", "GDOORB3", "GDOORB4" /* back */ +}, +{ + "\0", "\0", "\0", "\0" +} +}; + +slideframe_t g_slide_frames[MAXSLIDEDOORS]; +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* VERTICAL DOORS */ + +void t_vertical_door(vldoor_t *door) +{ + result_e res; + + switch (door->direction) + { + case 0: /* WAITING */ + if (!--door->topcountdown) + { + switch (door->type) + { + case VLD_BLAZERAISE: + door->direction = -1; /* time to go back down */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_BDCLS); +#endif + break; + + case VLD_NORMAL: + door->direction = -1; /* time to go back down */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DORCLS); +#endif + break; + + case VLD_CLOSE30THENOPEN: + door->direction = 1; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DOROPN); +#endif + break; + + default: + break; + } + } + break; + + case 2: /* INITIAL WAIT */ + if (!--door->topcountdown) + { + switch (door->type) + { + case VLD_RAISEIN5MINS: + door->direction = 1; + door->type = VLD_NORMAL; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DOROPN); +#endif + break; + + default: + break; + } + } + + break; + + case -1: /* DOWN */ + res = t_move_plane(door->sector, door->speed, + door->sector->floorheight, false, 1, door->direction); + + if (res == pastdest) + { + switch (door->type) + { + case VLD_BLAZERAISE: + case VLD_BLAZECLOSE: + door->sector->specialdata = NULL; + p_remove_thinker(&door->thinker); /* unlink and free */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_BDCLS); +#endif + break; + + case VLD_NORMAL: + case VLD_CLOSE: + door->sector->specialdata = NULL; + p_remove_thinker(&door->thinker); /* unlink and free */ + break; + + case VLD_CLOSE30THENOPEN: + door->direction = 0; + door->topcountdown = TICRATE * 30; + break; + + default: + break; + } + } + else if (res == crushed) + { + switch (door->type) + { + case VLD_BLAZECLOSE: + case VLD_CLOSE: /* DO NOT GO BACK UP! */ + break; + + default: + door->direction = 1; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DOROPN); +#endif + break; + } + } + break; + + case 1: /* UP */ + res = t_move_plane(door->sector, door->speed, door->topheight, + false, 1, door->direction); + + if (res == pastdest) + { + switch (door->type) + { + case VLD_BLAZERAISE: + case VLD_NORMAL: + door->direction = 0; /* wait at top */ + door->topcountdown = door->topwait; + break; + + case VLD_CLOSE30THENOPEN: + case VLD_BLAZEOPEN: + case VLD_OPEN: + door->sector->specialdata = NULL; + p_remove_thinker(&door->thinker); /* unlink and free */ + break; + + default: + break; + } + } + + break; + } +} + +/* ev_do_locked_door + * Move a locked door up/down + */ + +int ev_do_locked_door(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->cards[it_bluecard] && !p->cards[it_blueskull]) + { + p->message = (PD_BLUEO); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return 0; + } + break; + + case 134: /* Red Lock */ + case 135: + if (!p->cards[it_redcard] && !p->cards[it_redskull]) + { + p->message = (PD_REDO); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return 0; + } + break; + + case 136: /* Yellow Lock */ + case 137: + if (!p->cards[it_yellowcard] && !p->cards[it_yellowskull]) + { + p->message = (PD_YELLOWO); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return 0; + } + + break; + } + + return ev_do_door(line, type); +} + +int ev_do_door(line_t *line, vldoor_e type) +{ + int secnum; + int rtn; + sector_t *sec; + vldoor_t *door; + + secnum = -1; + rtn = 0; + + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) continue; + + /* new door thinker */ + + rtn = 1; + door = z_malloc(sizeof(*door), PU_LEVSPEC, 0); + p_add_thinker(&door->thinker); + sec->specialdata = door; + + door->thinker.function.acp1 = (actionf_p1)t_vertical_door; + door->sector = sec; + door->type = type; + door->topwait = VDOORWAIT; + door->speed = VDOORSPEED; + + switch (type) + { + case VLD_BLAZECLOSE: + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->direction = -1; + door->speed = VDOORSPEED * 4; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_BDCLS); +#endif + break; + + case VLD_CLOSE: + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->direction = -1; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DORCLS); +#endif + break; + + case VLD_CLOSE30THENOPEN: + door->topheight = sec->ceilingheight; + door->direction = -1; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DORCLS); +#endif + break; + + case VLD_BLAZERAISE: + case VLD_BLAZEOPEN: + door->direction = 1; + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->speed = VDOORSPEED * 4; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (door->topheight != sec->ceilingheight) + s_start_sound(&door->sector->soundorg, SFX_BDOPN); +#endif + break; + + case VLD_NORMAL: + case VLD_OPEN: + door->direction = 1; + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (door->topheight != sec->ceilingheight) + s_start_sound(&door->sector->soundorg, SFX_DOROPN); +#endif + break; + + default: + break; + } + } + + return rtn; +} + +/* ev_vertical_door : open a door manually, no tag value */ + +void ev_vertical_door(line_t *line, mobj_t *thing) +{ + player_t *player; + 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); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return; + } + break; + + case 27: /* Yellow Lock */ + case 34: + if (!player) return; + + if (!player->cards[it_yellowcard] && !player->cards[it_yellowskull]) + { + player->message = (PD_YELLOWK); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return; + } + break; + + case 28: /* Red Lock */ + case 33: + if (!player) return; + + if (!player->cards[it_redcard] && !player->cards[it_redskull]) + { + player->message = (PD_REDK); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return; + } + break; + } + + /* if the sector has an active thinker, use it */ + + if (line->sidenum[side ^ 1] == -1) + { + i_error("ev_vertical_door: DR special type on 1-sided linedef"); + } + + sec = sides[line->sidenum[side ^ 1]].sector; + + 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 */ + + /* When is a door not a door? + * In Vanilla, door->direction is set, even though + * "specialdata" might not actually point at a door. + */ + + if (door->thinker.function.acp1 == + (actionf_p1)t_vertical_door) + { + door->direction = -1; /* start going down immediately */ + } + else if (door->thinker.function.acp1 == + (actionf_p1)t_plat_raise) + { + /* Erm, this is a plat, not a door. + * This notably causes a problem in ep1-0500.lmp where + * a plat and a door are cross-referenced; the door + * doesn't open on 64-bit. + * The direction field in vldoor_t corresponds to the wait + * field in plat_t. Let's set that to -1 instead. + */ + + plat_t *plat; + + plat = (plat_t *)door; + plat->wait = -1; + } + else + { + /* This isn't a door OR a plat. Now we're in trouble. */ + + fprintf(stderr, "ev_vertical_door: Tried to close " + "something that wasn't a door.\n"); + + /* Try closing it anyway. At least it will work on 32-bit + * machines. + */ + + door->direction = -1; + } + } + + return; + } + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + /* for proper sound */ + + switch (line->special) + { + case 117: /* BLAZING DOOR RAISE */ + case 118: /* BLAZING DOOR OPEN */ + s_start_sound(&sec->soundorg, SFX_BDOPN); + break; + + case 1: /* NORMAL DOOR SOUND */ + case 31: + s_start_sound(&sec->soundorg, SFX_DOROPN); + break; + + default: /* LOCKED DOOR SOUND */ + s_start_sound(&sec->soundorg, SFX_DOROPN); + break; + } +#endif + + /* new door thinker */ + + door = z_malloc(sizeof(*door), PU_LEVSPEC, 0); + p_add_thinker(&door->thinker); + sec->specialdata = door; + door->thinker.function.acp1 = (actionf_p1)t_vertical_door; + 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 = VLD_NORMAL; + break; + + case 31: + case 32: + case 33: + case 34: + door->type = VLD_OPEN; + line->special = 0; + break; + + case 117: /* blazing door raise */ + door->type = VLD_BLAZERAISE; + door->speed = VDOORSPEED * 4; + break; + case 118: /* blazing door open */ + door->type = VLD_BLAZEOPEN; + line->special = 0; + door->speed = VDOORSPEED * 4; + break; + } + + /* find the top and bottom of the movement range */ + + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; +} + +/* Spawn a door that closes after 30 seconds */ + +void p_spawn_door_close_in30(sector_t *sec) +{ + vldoor_t *door; + + door = z_malloc(sizeof(*door), PU_LEVSPEC, 0); + + p_add_thinker(&door->thinker); + + sec->specialdata = door; + sec->special = 0; + + door->thinker.function.acp1 = (actionf_p1)t_vertical_door; + door->sector = sec; + door->direction = 0; + door->type = VLD_NORMAL; + door->speed = VDOORSPEED; + door->topcountdown = 30 * TICRATE; +} + +/* Spawn a door that opens after 5 minutes */ + +void p_spawn_door_raise_in_5min(sector_t *sec, int secnum) +{ + vldoor_t *door; + + door = z_malloc(sizeof(*door), PU_LEVSPEC, 0); + + p_add_thinker(&door->thinker); + + sec->specialdata = door; + sec->special = 0; + + door->thinker.function.acp1 = (actionf_p1)t_vertical_door; + door->sector = sec; + door->direction = 2; + door->type = VLD_RAISEIN5MINS; + door->speed = VDOORSPEED; + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->topwait = VDOORWAIT; + door->topcountdown = 5 * 60 * TICRATE; +} + +/* UNUSED + * Separate into p_slidoor.c? + */ + +#if 0 /* ABANDONED TO THE MISTS OF TIME!!! */ + +/* ev_sliding_door : slide a door horizontally + * (animate midtexture, then set noblocking line) + */ + +void p_init_sliding_door_frames(void) +{ + int i; + int f1; + int f2; + int f3; + int f4; + + /* DOOM II ONLY... */ + + if (gamemode != commercial) return; + + for (i = 0; i < MAXSLIDEDOORS; i++) + { + if (!g_slide_frame_names[i].front_frame1[0]) break; + + f1 = r_texture_num_for_name(g_slide_frame_names[i].front_frame1); + f2 = r_texture_num_for_name(g_slide_frame_names[i].front_frame2); + f3 = r_texture_num_for_name(g_slide_frame_names[i].front_frame3); + f4 = r_texture_num_for_name(g_slide_frame_names[i].front_frame4); + + g_slide_frames[i].front_frames[0] = f1; + g_slide_frames[i].front_frames[1] = f2; + g_slide_frames[i].front_frames[2] = f3; + g_slide_frames[i].front_frames[3] = f4; + + f1 = r_texture_num_for_name(g_slide_frame_names[i].back_frame1); + f2 = r_texture_num_for_name(g_slide_frame_names[i].back_frame2); + f3 = r_texture_num_for_name(g_slide_frame_names[i].back_frame3); + f4 = r_texture_num_for_name(g_slide_frame_names[i].back_frame4); + + g_slide_frames[i].back_frames[0] = f1; + g_slide_frames[i].back_frames[1] = f2; + g_slide_frames[i].back_frames[2] = f3; + g_slide_frames[i].back_frames[3] = f4; + } +} + +/* Return index into "g_slide_frames" array for which door type to use */ + +static int p_find_sliding_door(line_t *line) +{ + int i; + int val; + + for (i = 0; i < MAXSLIDEDOORS; i++) + { + val = sides[line->sidenum[0]].midtexture; + if (val == g_slide_frames[i].front_frames[0]) return i; + } + + return -1; +} + +static void t_sliding_door(slidedoor_t *door) +{ + switch (door->status) + { + case sd_opening: + if (!door->timer--) + { + if (++door->frame == SNUMFRAMES) + { + /* IF DOOR IS DONE OPENING... */ + + sides[door->line->sidenum[0]].midtexture = 0; + sides[door->line->sidenum[1]].midtexture = 0; + door->line->flags &= ML_BLOCKING ^ 0xff; + + if (door->type == SDT_OPENONLY) + { + door->frontsector->specialdata = NULL; + p_remove_thinker(&door->thinker); + break; + } + + door->timer = SDOORWAIT; + door->status = sd_waiting; + } + else + { + /* IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... */ + + door->timer = SWAITTICS; + + sides[door->line->sidenum[0]].midtexture = + g_slide_frames[door->which_door_index] + .front_frames[door->frame]; + sides[door->line->sidenum[1]].midtexture = + g_slide_frames[door->which_door_index] + .back_frames[door->frame]; + } + } + break; + + case sd_waiting: + + /* IF DOOR IS DONE WAITING... */ + + if (!door->timer--) + { + /* CAN DOOR CLOSE? */ + + if (door->frontsector->thinglist != NULL || + door->backsector->thinglist != NULL) + { + door->timer = SDOORWAIT; + break; + } + + /* door->frame = SNUMFRAMES-1; */ + + door->status = sd_closing; + door->timer = SWAITTICS; + } + break; + + case sd_closing: + if (!door->timer--) + { + if (--door->frame < 0) + { + /* IF DOOR IS DONE CLOSING... */ + + door->line->flags |= ML_BLOCKING; + door->frontsector->specialdata = NULL; + p_remove_thinker(&door->thinker); + break; + } + else + { + /* IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... */ + + door->timer = SWAITTICS; + + sides[door->line->sidenum[0]].midtexture = + g_slide_frames[door->which_door_index] + .front_frames[door->frame]; + sides[door->line->sidenum[1]].midtexture = + g_slide_frames[door->which_door_index] + .back_frames[door->frame]; + } + } + break; + } +} + +void ev_sliding_door(line_t *line, mobj_t *thing) +{ + sector_t *sec; + slidedoor_t *door; + + /* DOOM II ONLY... */ + + if (gamemode != commercial) return; + + /* Make sure door isn't already being animated */ + + sec = line->frontsector; + door = NULL; + if (sec->specialdata) + { + if (!thing->player) return; + + door = sec->specialdata; + if (door->type == SDT_OPENANDCLOSE) + { + if (door->status == sd_waiting) door->status = sd_closing; + } + else + return; + } + + /* Init sliding door vars */ + + if (!door) + { + door = z_malloc(sizeof(*door), PU_LEVSPEC, 0); + p_add_thinker(&door->thinker); + sec->specialdata = door; + + door->type = SDT_OPENANDCLOSE; + door->status = sd_opening; + door->which_door_index = p_find_sliding_door(line); + + if (door->which_door_index < 0) + i_error("ev_sliding_door: Can't use texture for sliding door!"); + + door->frontsector = sec; + door->backsector = line->backsector; + door->thinker.function = t_sliding_door; + door->timer = SWAITTICS; + door->frame = 0; + door->line = line; + } +} +#endif /* UNUSED */ diff --git a/games/NXDoom/src/doom/p_enemy.c b/games/NXDoom/src/doom/p_enemy.c new file mode 100644 index 00000000000..cf10325afda --- /dev/null +++ b/games/NXDoom/src/doom/p_enemy.c @@ -0,0 +1,1932 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_enemy.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Enemy thinking, AI. + * Action Pointer Functions + * that are associated with states/frames. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "i_system.h" +#include "m_random.h" + +#include "doomdef.h" +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "g_game.h" + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TRACEANGLE 0xc000000 + +#define FATSPREAD (ANG90 / 8) + +#define SKULLSPEED (20 * FRACUNIT) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Data. */ + +typedef enum +{ + DI_EAST, + DI_NORTHEAST, + DI_NORTH, + DI_NORTHWEST, + DI_WEST, + DI_SOUTHWEST, + DI_SOUTH, + DI_SOUTHEAST, + DI_NODIR, + NUMDIRS +} dirtype_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void a_fall(mobj_t *actor); +void a_refire(player_t *player, pspdef_t *psp); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* p_new_chase_dir 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, +}; + +/* ENEMY THINKING + * Enemies are always spawned with targetplayer = -1, threshold = 0 + * Most monsters are spawned unaware of all players, but some can be made + * preaware + */ + +/* Called by p_noise_alert. + * Recursively traverse adjacent sectors, sound blocking lines cut off + * traversal. + */ + +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 +}; + +mobj_t *corpsehit; +mobj_t *vileobj; +fixed_t viletryx; +fixed_t viletryy; + +mobj_t *braintargets[32]; +int numbraintargets; +int braintargeton = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void p_recursive_sound(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_line_opening(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_recursive_sound(other, 1); + } + else + p_recursive_sound(other, soundblocks); + } +} + +static boolean p_check_missile_range(mobj_t *actor) +{ + fixed_t dist; + + if (!p_check_sight(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_approx_distance(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 >>= FRACBITS; + + 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. + */ + +static 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 */ + + boolean try_ok; + boolean good; + + if (actor->movedir == DI_NODIR) return false; + + if ((unsigned)actor->movedir >= 8) i_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_try_move(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_use_special_line(actor, ld, 0)) good = true; + } + + return good; + } + else + { + actor->flags &= ~MF_INFLOAT; + } + + if (!(actor->flags & MF_FLOAT)) actor->z = actor->floorz; + return true; +} + +/* p_try_walk + * + * 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. + */ + +static boolean p_try_walk(mobj_t *actor) +{ + if (!p_move(actor)) + { + return false; + } + + actor->movecount = p_random() & 15; + return true; +} + +static void p_new_chase_dir(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("p_new_chase_dir: 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 != (int)turnaround && p_try_walk(actor)) return; + } + + /* try other directions */ + + if (p_random() > 200 || abs(deltay) > 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_try_walk(actor)) + { + return; /* either moved forward or attacked */ + } + } + + if (d[2] != DI_NODIR) + { + actor->movedir = d[2]; + + if (p_try_walk(actor)) return; + } + + /* there is no direct path to the player, so pick another direction. */ + + if (olddir != DI_NODIR) + { + actor->movedir = olddir; + + if (p_try_walk(actor)) return; + } + + /* randomly determine direction of search */ + + if (p_random() & 1) + { + for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) + { + if (tdir != (int)turnaround) + { + actor->movedir = tdir; + + if (p_try_walk(actor)) return; + } + } + } + else + { + for (tdir = DI_SOUTHEAST; tdir != (DI_EAST - 1); tdir--) + { + if (tdir != (int)turnaround) + { + actor->movedir = tdir; + + if (p_try_walk(actor)) return; + } + } + } + + if (turnaround != DI_NODIR) + { + actor->movedir = turnaround; + if (p_try_walk(actor)) return; + } + + actor->movedir = DI_NODIR; /* can not move */ +} + +/* p_look_for_players + * If allaround is false, only look 180 degrees in front. Returns true if a + * player is targeted. + */ + +static boolean p_look_for_players(mobj_t *actor, boolean allaround) +{ + int c; + int stop; + player_t *player; + angle_t an; + fixed_t dist; + + 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) + { + return false; /* done looking */ + } + + player = &players[actor->lastlook]; + + if (player->health <= 0) continue; /* dead */ + + if (!p_check_sight(actor, player->mo)) continue; /* out of sight */ + + if (!allaround) + { + an = r_point_to_angle2(actor->x, actor->y, player->mo->x, + player->mo->y) - + actor->angle; + + if (an > ANG90 && an < ANG270) + { + dist = p_approx_distance(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; +} + +/* pit_vile_check + * Detect a corpse that could be raised. + */ + +static boolean pit_vile_check(mobj_t *thing) +{ + int maxdist; + 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 (abs(thing->x - viletryx) > maxdist || + abs(thing->y - viletryy) > maxdist) + { + return true; /* not actually touching */ + } + + corpsehit = thing; + corpsehit->momx = corpsehit->momy = 0; + corpsehit->height <<= 2; + check = p_check_position(corpsehit, corpsehit->x, corpsehit->y); + corpsehit->height >>= 2; + + if (!check) return true; /* doesn't fit here */ + + return false; /* got one, so stop checking */ +} + +/* Check whether the death of the specified monster type is allowed + * to trigger the end of episode special action. + * + * This behavior changed in v1.9, the most notable effect of which + * was to break uac_dead.wad + */ + +static boolean check_boss_end(mobjtype_t motype) +{ + if (gameversion < exe_ultimate) + { + if (gamemap != 8) + { + return false; + } + + /* Baron death on later episodes is nothing special. */ + + if (motype == MT_BRUISER && gameepisode != 1) + { + return false; + } + + return true; + } + else + { + /* New logic that appeared in Ultimate Doom. + * Looks like the logic was overhauled while adding in the + * episode 4 support. Now bosses only trigger on their + * specific episode. + */ + + switch (gameepisode) + { + case 1: + return gamemap == 8 && motype == MT_BRUISER; + + case 2: + return gamemap == 8 && motype == MT_CYBORG; + + case 3: + return gamemap == 8 && motype == MT_SPIDER; + + case 4: + return (gamemap == 6 && motype == MT_CYBORG) || + (gamemap == 8 && motype == MT_SPIDER); + + default: + return gamemap == 8; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_noise_alert + * If a monster yells at a player, + * it will alert other monsters to the player. + */ + +void p_noise_alert(mobj_t *target, mobj_t *emmiter) +{ + soundtarget = target; + validcount++; + p_recursive_sound(emmiter->subsector->sector, 0); +} + +boolean p_check_melee_range(mobj_t *actor) +{ + mobj_t *pl; + fixed_t dist; + fixed_t range; + + if (!actor->target) return false; + + pl = actor->target; + dist = p_approx_distance(pl->x - actor->x, pl->y - actor->y); + + if (gameversion < exe_doom_1_5) + range = MELEERANGE; + else + range = MELEERANGE - 20 * FRACUNIT + pl->info->radius; + + if (dist >= range) return false; + + if (!p_check_sight(actor, actor->target)) return false; + + return true; +} + +/* a_keen_die + * DOOM II special, map 32. Uses special tag 666. + */ + +void a_keen_die(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_mobj_thinker) continue; + + mo2 = (mobj_t *)th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + { + return; /* other Keen not dead */ + } + } + + junk.tag = 666; + ev_do_door(&junk, VLD_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_check_sight(actor, actor->target)) goto seeyou; + } + else + { + goto seeyou; + } + } + + if (!p_look_for_players(actor, false)) return; + + /* go into chase state */ + +seeyou: +#ifdef CONFIG_GAMES_NXDOOM_SOUND + 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_start_sound(NULL, sound); + } + else + s_start_sound(actor, sound); + } +#endif + + p_set_mobj_state(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 (gameversion > exe_doom_1_2 && + (!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 &= (7u << 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_look_for_players(actor, true)) return; /* got a new target */ + + p_set_mobj_state(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_new_chase_dir(actor); + return; + } + + /* check for melee attack */ + + if (actor->info->meleestate && p_check_melee_range(actor)) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (actor->info->attacksound) + s_start_sound(actor, actor->info->attacksound); +#endif + + p_set_mobj_state(actor, actor->info->meleestate); + return; + } + + /* check for missile attack */ + + if (actor->info->missilestate) + { + if (gameskill < sk_nightmare && !fastparm && actor->movecount) + { + goto nomissile; + } + + if (!p_check_missile_range(actor)) goto nomissile; + + p_set_mobj_state(actor, actor->info->missilestate); + actor->flags |= MF_JUSTATTACKED; + return; + } + + /* ? */ + +nomissile: + + /* possibly choose another target */ + + if (netgame && !actor->threshold && !p_check_sight(actor, actor->target)) + { + if (p_look_for_players(actor, true)) return; /* got a new target */ + } + + /* chase towards player */ + + if (--actor->movecount < 0 || !p_move(actor)) + { + p_new_chase_dir(actor); + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + + /* make active sound */ + + if (actor->info->activesound && p_random() < 3) + { + s_start_sound(actor, actor->info->activesound); + } +#endif +} + +void a_face_target(mobj_t *actor) +{ + if (!actor->target) return; + + actor->flags &= ~MF_AMBUSH; + + actor->angle = r_point_to_angle2(actor->x, actor->y, actor->target->x, + actor->target->y); + + if (actor->target->flags & MF_SHADOW) actor->angle += p_sub_random() << 21; +} + +void a_pos_attack(mobj_t *actor) +{ + int angle; + int damage; + int slope; + + if (!actor->target) return; + + a_face_target(actor); + angle = actor->angle; + slope = p_aim_line_attack(actor, angle, MISSILERANGE); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_PISTOL); +#endif + angle += p_sub_random() << 20; + damage = ((p_random() % 5) + 1) * 3; + p_line_attack(actor, angle, MISSILERANGE, slope, damage); +} + +void a_s_pos_attack(mobj_t *actor) +{ + int i; + int angle; + int bangle; + int damage; + int slope; + + if (!actor->target) return; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_SHOTGN); +#endif + a_face_target(actor); + bangle = actor->angle; + slope = p_aim_line_attack(actor, bangle, MISSILERANGE); + + for (i = 0; i < 3; i++) + { + angle = bangle + (p_sub_random() << 20); + damage = ((p_random() % 5) + 1) * 3; + p_line_attack(actor, angle, MISSILERANGE, slope, damage); + } +} + +void a_c_pos_attack(mobj_t *actor) +{ + int angle; + int bangle; + int damage; + int slope; + + if (!actor->target) return; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_SHOTGN); +#endif + a_face_target(actor); + bangle = actor->angle; + slope = p_aim_line_attack(actor, bangle, MISSILERANGE); + + angle = bangle + (p_sub_random() << 20); + damage = ((p_random() % 5) + 1) * 3; + p_line_attack(actor, angle, MISSILERANGE, slope, damage); +} + +void a_c_pos_refire(mobj_t *actor) +{ + /* keep firing unless target got out of sight */ + + a_face_target(actor); + + if (p_random() < 40) return; + + if (!actor->target || actor->target->health <= 0 || + !p_check_sight(actor, actor->target)) + { + p_set_mobj_state(actor, actor->info->seestate); + } +} + +void a_spid_refire(mobj_t *actor) +{ + /* keep firing unless target got out of sight */ + + a_face_target(actor); + + if (p_random() < 10) return; + + if (!actor->target || actor->target->health <= 0 || + !p_check_sight(actor, actor->target)) + { + p_set_mobj_state(actor, actor->info->seestate); + } +} + +void a_bspi_attack(mobj_t *actor) +{ + if (!actor->target) return; + + a_face_target(actor); + + /* launch a missile */ + + p_spawn_missile(actor, actor->target, MT_ARACHPLAZ); +} + +void a_troop_attack(mobj_t *actor) +{ + int damage; + + if (!actor->target) return; + + a_face_target(actor); + if (p_check_melee_range(actor)) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_CLAW); +#endif + damage = (p_random() % 8 + 1) * 3; + p_damage_mobj(actor->target, actor, actor, damage); + return; + } + + /* launch a missile */ + + p_spawn_missile(actor, actor->target, MT_TROOPSHOT); +} + +void a_sarg_attack(mobj_t *actor) +{ + int damage; + + if (!actor->target) return; + + a_face_target(actor); + + if (gameversion >= exe_doom_1_5) + { + if (!p_check_melee_range(actor)) return; + } + + damage = ((p_random() % 10) + 1) * 4; + + if (gameversion <= exe_doom_1_2) + p_line_attack(actor, actor->angle, MELEERANGE, 0, damage); + else + p_damage_mobj(actor->target, actor, actor, damage); +} + +void a_head_attack(mobj_t *actor) +{ + int damage; + + if (!actor->target) return; + + a_face_target(actor); + if (p_check_melee_range(actor)) + { + damage = (p_random() % 6 + 1) * 10; + p_damage_mobj(actor->target, actor, actor, damage); + return; + } + + /* launch a missile */ + + p_spawn_missile(actor, actor->target, MT_HEADSHOT); +} + +void a_cyber_attack(mobj_t *actor) +{ + if (!actor->target) return; + + a_face_target(actor); + p_spawn_missile(actor, actor->target, MT_ROCKET); +} + +void a_bruis_attack(mobj_t *actor) +{ + int damage; + + if (!actor->target) return; + + if (p_check_melee_range(actor)) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_CLAW); +#endif + damage = (p_random() % 8 + 1) * 10; + p_damage_mobj(actor->target, actor, actor, damage); + return; + } + + /* launch a missile */ + + p_spawn_missile(actor, actor->target, MT_BRUISERSHOT); +} + +void a_skel_missile(mobj_t *actor) +{ + mobj_t *mo; + + if (!actor->target) return; + + a_face_target(actor); + actor->z += 16 * FRACUNIT; /* so missile spawns higher */ + mo = p_spawn_missile(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_spawn_puff(actor->x, actor->y, actor->z); + + th = p_spawn_mobj(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_point_to_angle2(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 = fixed_mul(actor->info->speed, finecosine[exact]); + actor->momy = fixed_mul(actor->info->speed, finesine[exact]); + + /* change slope */ + + dist = p_approx_distance(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_skel_woosh(mobj_t *actor) +{ + if (!actor->target) return; + a_face_target(actor); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_SKESWG); +#endif +} + +void a_skel_fist(mobj_t *actor) +{ + int damage; + + if (!actor->target) return; + + a_face_target(actor); + + if (p_check_melee_range(actor)) + { + damage = ((p_random() % 10) + 1) * 6; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_SKEPCH); +#endif + p_damage_mobj(actor->target, actor, actor, damage); + } +} + +/* a_vile_chase + * Check for resurrecting a body + */ + +void a_vile_chase(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_vile_check to check whether object is a corpse that + * can be raised. + */ + + if (!p_block_things_iterator(bx, by, pit_vile_check)) + { + /* got one! */ + + temp = actor->target; + actor->target = corpsehit; + a_face_target(actor); + actor->target = temp; + + p_set_mobj_state(actor, S_VILE_HEAL1); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(corpsehit, SFX_SLOP); +#endif + info = corpsehit->info; + + p_set_mobj_state(corpsehit, info->raisestate); + corpsehit->height <<= 2; + corpsehit->flags = info->flags; + corpsehit->health = info->spawnhealth; + corpsehit->target = NULL; + + return; + } + } + } + } + + /* Return to normal attack. */ + + a_chase(actor); +} + +/* Keep fire in front of player unless out of sight */ + +void a_fire(mobj_t *actor) +{ + mobj_t *dest; + mobj_t *target; + unsigned an; + + dest = actor->tracer; + if (!dest) return; + + target = p_subst_null_mobj(actor->target); + + /* don't move it if the vile lost sight */ + + if (!p_check_sight(target, dest)) return; + + an = dest->angle >> ANGLETOFINESHIFT; + + p_unset_thing_position(actor); + actor->x = dest->x + fixed_mul(24 * FRACUNIT, finecosine[an]); + actor->y = dest->y + fixed_mul(24 * FRACUNIT, finesine[an]); + actor->z = dest->z; + p_set_thing_position(actor); +} + +void a_vile_start(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_VILATK); +#endif +} + +void a_start_fire(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_FLAMST); +#endif + a_fire(actor); +} + +void a_fire_crackle(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_FLAME); +#endif + a_fire(actor); +} + +/* a_vile_target + * Spawn the hellfire + */ + +void a_vile_target(mobj_t *actor) +{ + mobj_t *fog; + + if (!actor->target) return; + + a_face_target(actor); + + fog = p_spawn_mobj(actor->target->x, actor->target->x, actor->target->z, + MT_FIRE); + + actor->tracer = fog; + fog->target = actor; + fog->tracer = actor->target; + a_fire(fog); +} + +void a_vile_attack(mobj_t *actor) +{ + mobj_t *fire; + int an; + + if (!actor->target) return; + + a_face_target(actor); + + if (!p_check_sight(actor, actor->target)) return; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_BAREXP); +#endif + p_damage_mobj(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 - fixed_mul(24 * FRACUNIT, finecosine[an]); + fire->y = actor->target->y - fixed_mul(24 * FRACUNIT, finesine[an]); + p_radius_attack(fire, actor, 70); +} + +/* Mancubus attack, + * firing three missiles (bruisers) in three different directions? + * Doesn't look like it. + */ + +void a_fat_raise(mobj_t *actor) +{ + a_face_target(actor); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_MANATK); +#endif +} + +void a_fat_attack1(mobj_t *actor) +{ + mobj_t *mo; + mobj_t *target; + int an; + + a_face_target(actor); + + /* Change direction to ... */ + + actor->angle += FATSPREAD; + target = p_subst_null_mobj(actor->target); + p_spawn_missile(actor, target, MT_FATSHOT); + + mo = p_spawn_missile(actor, target, MT_FATSHOT); + mo->angle += FATSPREAD; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = fixed_mul(mo->info->speed, finecosine[an]); + mo->momy = fixed_mul(mo->info->speed, finesine[an]); +} + +void a_fat_attack2(mobj_t *actor) +{ + mobj_t *mo; + mobj_t *target; + int an; + + a_face_target(actor); + + /* Now here choose opposite deviation. */ + + actor->angle -= FATSPREAD; + target = p_subst_null_mobj(actor->target); + p_spawn_missile(actor, target, MT_FATSHOT); + + mo = p_spawn_missile(actor, target, MT_FATSHOT); + mo->angle -= FATSPREAD * 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = fixed_mul(mo->info->speed, finecosine[an]); + mo->momy = fixed_mul(mo->info->speed, finesine[an]); +} + +void a_fat_attack3(mobj_t *actor) +{ + mobj_t *mo; + mobj_t *target; + int an; + + a_face_target(actor); + + target = p_subst_null_mobj(actor->target); + + mo = p_spawn_missile(actor, target, MT_FATSHOT); + mo->angle -= FATSPREAD / 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = fixed_mul(mo->info->speed, finecosine[an]); + mo->momy = fixed_mul(mo->info->speed, finesine[an]); + + mo = p_spawn_missile(actor, target, MT_FATSHOT); + mo->angle += FATSPREAD / 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = fixed_mul(mo->info->speed, finecosine[an]); + mo->momy = fixed_mul(mo->info->speed, finesine[an]); +} + +/* SkullAttack + * Fly at the player like a missile. + */ + +void a_skull_attack(mobj_t *actor) +{ + mobj_t *dest; + angle_t an; + int dist; + + if (!actor->target) return; + + dest = actor->target; + actor->flags |= MF_SKULLFLY; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, actor->info->attacksound); +#endif + a_face_target(actor); + an = actor->angle >> ANGLETOFINESHIFT; + actor->momx = fixed_mul(SKULLSPEED, finecosine[an]); + actor->momy = fixed_mul(SKULLSPEED, finesine[an]); + dist = p_approx_distance(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_pain_shoot_skull + * Spawn a lost soul and launch it at the target + */ + +void a_pain_shoot_skull(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_mobj_thinker) && + ((mobj_t *)currentthinker)->type == MT_SKULL) + count++; + currentthinker = currentthinker->next; + } + + /* if there are already 20 skulls on the level, don't spit another one */ + + if (count > 20) return; + + /* okay, there's player for another one */ + + an = angle >> ANGLETOFINESHIFT; + + prestep = 4 * FRACUNIT + + 3 * (actor->info->radius + mobjinfo[MT_SKULL].radius) / 2; + + x = actor->x + fixed_mul(prestep, finecosine[an]); + y = actor->y + fixed_mul(prestep, finesine[an]); + z = actor->z + 8 * FRACUNIT; + + newmobj = p_spawn_mobj(x, y, z, MT_SKULL); + + /* Check for movements. */ + + if (!p_try_move(newmobj, newmobj->x, newmobj->y)) + { + /* kill it immediately */ + + p_damage_mobj(newmobj, actor, actor, 10000); + return; + } + + newmobj->target = actor->target; + a_skull_attack(newmobj); +} + +/* a_pain_attack + * Spawn a lost soul and launch it at the target + */ + +void a_pain_attack(mobj_t *actor) +{ + if (!actor->target) return; + + a_face_target(actor); + a_pain_shoot_skull(actor, actor->angle); +} + +void a_pain_die(mobj_t *actor) +{ + a_fall(actor); + a_pain_shoot_skull(actor, actor->angle + ANG90); + a_pain_shoot_skull(actor, actor->angle + ANG180); + a_pain_shoot_skull(actor, actor->angle + ANG270); +} + +void a_scream(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + 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) + { + s_start_sound(NULL, sound); /* full volume */ + } + else + s_start_sound(actor, sound); +#endif +} + +void a_xscream(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_SLOP); +#endif +} + +void a_pain(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (actor->info->painsound) s_start_sound(actor, actor->info->painsound); +#endif +} + +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. */ +} + +void a_explode(mobj_t *thingy) +{ + p_radius_attack(thingy, thingy->target, 128); +} + +/* a_boss_death + * Possibly trigger special effects if on first boss level + */ + +void a_boss_death(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 + { + if (!check_boss_end(mo->type)) + { + return; + } + } + + /* 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_mobj_thinker) continue; + + mo2 = (mobj_t *)th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + { + return; /* other boss not dead */ + } + } + + /* victory! */ + + if (gamemode == commercial) + { + if (gamemap == 7) + { + if (mo->type == MT_FATSO) + { + junk.tag = 666; + ev_do_floor(&junk, FLOOR_LOWERFLOORTOLOWEST); + return; + } + + if (mo->type == MT_BABY) + { + junk.tag = 667; + ev_do_floor(&junk, FLOOR_RAISETOTEXTURE); + return; + } + } + } + else + { + switch (gameepisode) + { + case 1: + junk.tag = 666; + ev_do_floor(&junk, FLOOR_LOWERFLOORTOLOWEST); + return; + break; + + case 4: + switch (gamemap) + { + case 6: + junk.tag = 666; + ev_do_door(&junk, VLD_BLAZEOPEN); + return; + break; + + case 8: + junk.tag = 666; + ev_do_floor(&junk, FLOOR_LOWERFLOORTOLOWEST); + return; + break; + } + } + } + + g_exit_level(); +} + +void a_hoof(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_HOOF); +#endif + a_chase(mo); +} + +void a_metal(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_METAL); +#endif + a_chase(mo); +} + +void a_baby_metal(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_BSPWLK); +#endif + a_chase(mo); +} + +void a_open_shotgun2(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_DBOPN); +#endif +} + +void a_load_shotgun2(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_DBLOAD); +#endif +} + +void a_close_shotgun2(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_DBCLS); +#endif + a_refire(player, psp); +} + +void a_brain_awake(mobj_t *mo) +{ + thinker_t *thinker; + mobj_t *m; + + /* find all the target spots */ + + numbraintargets = 0; + braintargeton = 0; + + for (thinker = thinkercap.next; thinker != &thinkercap; + thinker = thinker->next) + { + if (thinker->function.acp1 != (actionf_p1)p_mobj_thinker) + { + continue; /* not a mobj */ + } + + m = (mobj_t *)thinker; + + if (m->type == MT_BOSSTARGET) + { + braintargets[numbraintargets] = m; + numbraintargets++; + } + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_BOSSIT); +#endif +} + +void a_brain_pain(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_BOSPN); +#endif +} + +void a_brain_scream(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_spawn_mobj(x, y, z, MT_ROCKET); + th->momz = p_random() * 512; + + p_set_mobj_state(th, S_BRAINEXPLODE1); + + th->tics -= p_random() & 7; + if (th->tics < 1) th->tics = 1; + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_BOSDTH); +#endif +} + +void a_brain_explode(mobj_t *mo) +{ + int x; + int y; + int z; + mobj_t *th; + + x = mo->x + p_sub_random() * 2048; + y = mo->y; + z = 128 + p_random() * 2 * FRACUNIT; + th = p_spawn_mobj(x, y, z, MT_ROCKET); + th->momz = p_random() * 512; + + p_set_mobj_state(th, S_BRAINEXPLODE1); + + th->tics -= p_random() & 7; + if (th->tics < 1) th->tics = 1; +} + +void a_brain_die(mobj_t *mo) +{ + g_exit_level(); +} + +void a_brain_split(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]; + if (numbraintargets == 0) + { + i_error("a_brain_split: numbraintargets was 0 (vanilla crashes here)"); + } + + braintargeton = (braintargeton + 1) % numbraintargets; + + /* spawn brain missile */ + + newmobj = p_spawn_missile(mo, targ, MT_SPAWNSHOT); + newmobj->target = targ; + newmobj->reactiontime = + ((targ->y - mo->y) / newmobj->momy) / newmobj->state->tics; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_BOSPIT); +#endif +} + +void a_spawn_fly(mobj_t *mo) +{ + mobj_t *newmobj; + mobj_t *fog; + mobj_t *targ; + int r; + mobjtype_t type; + + if (--mo->reactiontime) return; /* still flying */ + + targ = p_subst_null_mobj(mo->target); + + /* First spawn teleport fog. */ + + fog = p_spawn_mobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(fog, SFX_TELEPT); +#else + UNUSED(fog); +#endif + + /* 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_spawn_mobj(targ->x, targ->y, targ->z, type); + if (p_look_for_players(newmobj, true)) + p_set_mobj_state(newmobj, newmobj->info->seestate); + + /* telefrag anything in this spot */ + + p_teleport_move(newmobj, newmobj->x, newmobj->y); + + /* remove self (i.e., cube). */ + + p_remove_mobj(mo); +} + +/* travelling cube sound */ + +void a_spawn_sound(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_BOSCUB); +#endif + a_spawn_fly(mo); +} + +void a_player_screm(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + + /* 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_start_sound(mo, sound); +#else + UNUSED(mo); +#endif +} diff --git a/games/NXDoom/src/doom/p_floor.c b/games/NXDoom/src/doom/p_floor.c new file mode 100644 index 00000000000..6562407852c --- /dev/null +++ b/games/NXDoom/src/doom/p_floor.c @@ -0,0 +1,569 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_floor.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Floor animation: raising stairs. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" +#include "p_local.h" +#include "z_zone.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* e6y */ + +#define STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE 10 + +/* FLOORS */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Move a plane (floor or ceiling) and check for crushing */ + +result_e t_move_plane(sector_t *sector, fixed_t speed, fixed_t dest, + boolean crush, int floor_or_ceiling, int direction) +{ + boolean flag; + fixed_t lastpos; + + switch (floor_or_ceiling) + { + case 0: /* FLOOR */ + switch (direction) + { + case -1: /* DOWN */ + if (sector->floorheight - speed < dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = p_change_sector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + p_change_sector(sector, crush); + + /* return crushed; */ + } + + return pastdest; + } + else + { + lastpos = sector->floorheight; + sector->floorheight -= speed; + flag = p_change_sector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + p_change_sector(sector, crush); + return crushed; + } + } + + break; + + case 1: /* UP */ + if (sector->floorheight + speed > dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = p_change_sector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + p_change_sector(sector, crush); + + /* return crushed; */ + } + + return pastdest; + } + else + { + /* COULD GET CRUSHED */ + + lastpos = sector->floorheight; + sector->floorheight += speed; + flag = p_change_sector(sector, crush); + if (flag == true) + { + if (crush == true) return crushed; + sector->floorheight = lastpos; + p_change_sector(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_change_sector(sector, crush); + + if (flag == true) + { + sector->ceilingheight = lastpos; + p_change_sector(sector, crush); + + /* return crushed; */ + } + + return pastdest; + } + else + { + /* COULD GET CRUSHED */ + + lastpos = sector->ceilingheight; + sector->ceilingheight -= speed; + flag = p_change_sector(sector, crush); + + if (flag == true) + { + if (crush == true) return crushed; + sector->ceilingheight = lastpos; + p_change_sector(sector, crush); + return crushed; + } + } + break; + + case 1: /* UP */ + if (sector->ceilingheight + speed > dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = p_change_sector(sector, crush); + if (flag == true) + { + sector->ceilingheight = lastpos; + p_change_sector(sector, crush); + + /* return crushed; */ + } + + return pastdest; + } + else + { + lastpos = sector->ceilingheight; + sector->ceilingheight += speed; + flag = p_change_sector(sector, crush); + +#if 0 /* UNUSED */ + if (flag == true) + { + sector->ceilingheight = lastpos; + p_change_sector(sector, crush); + return crushed; + } +#endif + } + break; + } + break; + } + + return ok; +} + +/* MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) */ + +void t_move_floor(floormove_t *floor) +{ + result_e res; + + res = t_move_plane(floor->sector, floor->speed, floor->floordestheight, + floor->crush, 0, floor->direction); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(leveltime & 7)) s_start_sound(&floor->sector->soundorg, SFX_STNMOV); +#endif + + if (res == pastdest) + { + floor->sector->specialdata = NULL; + + if (floor->direction == 1) + { + switch (floor->type) + { + case FLOOR_DONUTRAISE: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + default: + break; + } + } + else if (floor->direction == -1) + { + switch (floor->type) + { + case FLOOR_LOWERANDCHANGE: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + default: + break; + } + } + + p_remove_thinker(&floor->thinker); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&floor->sector->soundorg, SFX_PSTOP); +#endif + } +} + +/* HANDLE FLOOR TYPES */ + +int ev_do_floor(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_find_sector_from_line_tag(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_add_thinker(&floor->thinker); + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + floor->type = floortype; + floor->crush = false; + + switch (floortype) + { + case FLOOR_LOWERFLOOR: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = p_find_highest_floor_surrounding(sec); + break; + + case FLOOR_LOWERFLOORTOLOWEST: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = p_find_lowest_floor_surrounding(sec); + break; + + case FLOOR_TURBOLOWER: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = p_find_highest_floor_surrounding(sec); + if (gameversion <= exe_doom_1_2 || + floor->floordestheight != sec->floorheight) + floor->floordestheight += 8 * FRACUNIT; + break; + + case FLOOR_RAISEFLOORCRUSH: + floor->crush = true; + case FLOOR_RAISEFLOOR: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = p_find_lowest_ceiling_surrounding(sec); + if (floor->floordestheight > sec->ceilingheight) + floor->floordestheight = sec->ceilingheight; + floor->floordestheight -= + (8 * FRACUNIT) * (floortype == FLOOR_RAISEFLOORCRUSH); + break; + + case FLOOR_RAISEFLOORTURBO: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = + p_find_next_highest_floor(sec, sec->floorheight); + break; + + case FLOOR_RAISEFLOORTONEAREST: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + p_find_next_highest_floor(sec, sec->floorheight); + break; + + case FLOOR_RAISEFLOOR24: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + floor->sector->floorheight + 24 * FRACUNIT; + break; + + case FLOOR_RAISEFLOOR512: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + floor->sector->floorheight + 512 * FRACUNIT; + break; + + case FLOOR_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 FLOOR_RAISETOTEXTURE: + { + int minsize = INT_MAX; + side_t *side; + + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + for (i = 0; i < sec->linecount; i++) + { + if (two_sided(secnum, i)) + { + side = get_side(secnum, i, 0); + if (side->bottomtexture >= 0) + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + side = get_side(secnum, i, 1); + if (side->bottomtexture >= 0) + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + } + } + + floor->floordestheight = floor->sector->floorheight + minsize; + } + break; + + case FLOOR_LOWERANDCHANGE: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = p_find_lowest_floor_surrounding(sec); + floor->texture = sec->floorpic; + + for (i = 0; i < sec->linecount; i++) + { + if (two_sided(secnum, i)) + { + if (get_side(secnum, i, 0)->sector - sectors == secnum) + { + sec = get_sector(secnum, i, 1); + + if (sec->floorheight == floor->floordestheight) + { + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + break; + } + } + else + { + sec = get_sector(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_build_stairs(line_t *line, stair_e type) +{ + int secnum; + int height; + int i; + int newsecnum; + int texture; + int is_ok; + int rtn; + + sector_t *sec; + sector_t *tsec; + + floormove_t *floor; + + fixed_t stairsize = 0; + fixed_t speed = 0; + + secnum = -1; + rtn = 0; + while ((secnum = p_find_sector_from_line_tag(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_add_thinker(&floor->thinker); + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + floor->direction = 1; + floor->sector = sec; + + switch (type) + { + case STAIR_BUILD8: + speed = FLOORSPEED / 4; + stairsize = 8 * FRACUNIT; + break; + case STAIR_TURBO16: + speed = FLOORSPEED * 4; + stairsize = 16 * FRACUNIT; + break; + } + + floor->speed = speed; + height = sec->floorheight + stairsize; + floor->floordestheight = height; + + /* Initialize */ + + floor->type = FLOOR_LOWERFLOOR; + + /* e6y + * Uninitialized crush field will not be equal to 0 or 1 (true) + * with high probability. So, initialize it with any other value + */ + + floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE; + + 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 + { + is_ok = 0; + for (i = 0; i < sec->linecount; i++) + { + if (!((sec->lines[i])->flags & ML_TWOSIDED)) continue; + + tsec = (sec->lines[i])->frontsector; + newsecnum = tsec - sectors; + + if (secnum != newsecnum) continue; + + tsec = (sec->lines[i])->backsector; + newsecnum = 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_add_thinker(&floor->thinker); + + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + floor->direction = 1; + floor->sector = sec; + floor->speed = speed; + floor->floordestheight = height; + + /* Initialize */ + + floor->type = FLOOR_LOWERFLOOR; + + /* e6y + * Uninitialized crush field will not be equal to 0 or 1 + * (true) with high probability. So, initialize it with any + * other value + */ + + floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE; + is_ok = 1; + break; + } + } + while (is_ok); + } + + return rtn; +} diff --git a/games/NXDoom/src/doom/p_inter.c b/games/NXDoom/src/doom/p_inter.c new file mode 100644 index 00000000000..cd77f8ca7c9 --- /dev/null +++ b/games/NXDoom/src/doom/p_inter.c @@ -0,0 +1,890 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_inter.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Handling interactions (i.e., collisions). + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Data. */ + +#include "doomdef.h" +#include "dstrings.h" + +#include "deh_main.h" +#include "deh_misc.h" +#include "doomstat.h" + +#include "i_system.h" +#include "m_random.h" + +#include "am_map.h" + +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "p_inter.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define BONUSADD 6 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* 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 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* GET STUFF */ + +/* p_give_ammo + * + * 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 + */ + +static boolean p_give_ammo(player_t *player, ammotype_t ammo, int num) +{ + int oldammo; + + if (ammo == am_noammo) return false; + + if (ammo >= NUMAMMO) i_error("p_give_ammo: bad type %i", ammo); + + 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_give_weapon + * + * The weapon name may have a MF_DROPPED flag ored in. + */ + +static boolean p_give_weapon(player_t *player, weapontype_t weapon, + boolean dropped) +{ + boolean gaveammo; + 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_give_ammo(player, weaponinfo[weapon].ammo, 5); + else + p_give_ammo(player, weaponinfo[weapon].ammo, 2); + player->pendingweapon = weapon; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (player == &players[consoleplayer]) s_start_sound(NULL, SFX_WPNUP); +#endif + 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_give_ammo(player, weaponinfo[weapon].ammo, 1); + else + gaveammo = p_give_ammo(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_give_body + * Returns false if the body isn't needed at all + */ + +static boolean p_give_body(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_give_armor + * Returns false if the armor is worse than the current armor. + */ + +static boolean p_give_armor(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; +} + +static void p_give_card(player_t *player, card_t card) +{ + if (player->cards[card]) return; + + player->bonuscount = BONUSADD; + player->cards[card] = 1; +} + +static void p_kill_mobj(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_drop_weapon(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_set_mobj_state(target, target->info->xdeathstate); + } + else + p_set_mobj_state(target, target->info->deathstate); + + target->tics -= p_random() & 3; + + if (target->tics < 1) target->tics = 1; + + /* In Chex Quest, monsters don't drop items. */ + + if (gameversion == exe_chex) + { + return; + } + + /* 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_spawn_mobj(target->x, target->y, ONFLOORZ, item); + mo->flags |= MF_DROPPED; /* special versions of items */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +boolean p_give_power(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_give_body(player, 100); + player->powers[power] = 1; + return true; + } + + if (player->powers[power]) return false; /* already got it */ + + player->powers[power] = 1; + return true; +} + +void p_touch_special_thing(mobj_t *special, mobj_t *toucher) +{ + player_t *player; + int i; + fixed_t delta; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + int sound; +#endif + + delta = special->z - toucher->z; + + if (delta > toucher->height || delta < -8 * FRACUNIT) + { + return; /* out of reach */ + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_ITEMUP; +#endif + 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_give_armor(player, deh_green_armor_class)) return; + player->message = (GOTARMOR); + break; + + case SPR_ARM2: + if (!p_give_armor(player, deh_blue_armor_class)) return; + player->message = (GOTMEGA); + break; + + /* bonus items */ + + case SPR_BON1: + player->health++; /* can go over 100% */ + if (player->health > deh_max_health) player->health = deh_max_health; + player->mo->health = player->health; + player->message = (GOTHTHBONUS); + break; + + case SPR_BON2: + player->armorpoints++; /* can go over 100% */ + if (player->armorpoints > deh_max_armor && gameversion > exe_doom_1_2) + { + player->armorpoints = deh_max_armor; + } + + /* deh_green_armor_class only applies to the green armor shirt; + * for the armor helmets, armortype 1 is always used. + */ + + if (!player->armortype) player->armortype = 1; + player->message = (GOTARMBONUS); + break; + + case SPR_SOUL: + player->health += deh_soulsphere_health; + if (player->health > deh_max_soulsphere) + player->health = deh_max_soulsphere; + player->mo->health = player->health; + player->message = (GOTSUPER); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_MEGA: + if (gamemode != commercial) return; + player->health = deh_megasphere_health; + player->mo->health = player->health; + + /* We always give armor type 2 for the megasphere; dehacked only + * affects the MegaArmor. + */ + + p_give_armor(player, 2); + player->message = (GOTMSPHERE); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + /* cards + * leave cards for everyone + */ + + case SPR_BKEY: + if (!player->cards[it_bluecard]) player->message = (GOTBLUECARD); + p_give_card(player, it_bluecard); + if (!netgame) break; + return; + + case SPR_YKEY: + if (!player->cards[it_yellowcard]) player->message = (GOTYELWCARD); + p_give_card(player, it_yellowcard); + if (!netgame) break; + return; + + case SPR_RKEY: + if (!player->cards[it_redcard]) player->message = (GOTREDCARD); + p_give_card(player, it_redcard); + if (!netgame) break; + return; + + case SPR_BSKU: + if (!player->cards[it_blueskull]) player->message = (GOTBLUESKUL); + p_give_card(player, it_blueskull); + if (!netgame) break; + return; + + case SPR_YSKU: + if (!player->cards[it_yellowskull]) player->message = (GOTYELWSKUL); + p_give_card(player, it_yellowskull); + if (!netgame) break; + return; + + case SPR_RSKU: + if (!player->cards[it_redskull]) player->message = (GOTREDSKULL); + p_give_card(player, it_redskull); + if (!netgame) break; + return; + + /* medikits, heals */ + + case SPR_STIM: + if (!p_give_body(player, 10)) return; + player->message = (GOTSTIM); + break; + + case SPR_MEDI: + if (!p_give_body(player, 25)) return; + + if (player->health < 25) + player->message = (GOTMEDINEED); + else + player->message = (GOTMEDIKIT); + break; + + /* power ups */ + + case SPR_PINV: + if (!p_give_power(player, pw_invulnerability)) return; + player->message = (GOTINVUL); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_PSTR: + if (!p_give_power(player, pw_strength)) return; + player->message = (GOTBERSERK); + if (player->readyweapon != wp_fist) player->pendingweapon = wp_fist; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_PINS: + if (!p_give_power(player, pw_invisibility)) return; + player->message = (GOTINVIS); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_SUIT: + if (!p_give_power(player, pw_ironfeet)) return; + player->message = (GOTSUIT); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_PMAP: + if (!p_give_power(player, pw_allmap)) return; + player->message = (GOTMAP); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_PVIS: + if (!p_give_power(player, pw_infrared)) return; + player->message = (GOTVISOR); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + /* ammo */ + + case SPR_CLIP: + if (special->flags & MF_DROPPED) + { + if (!p_give_ammo(player, am_clip, 0)) return; + } + else + { + if (!p_give_ammo(player, am_clip, 1)) return; + } + + player->message = (GOTCLIP); + break; + + case SPR_AMMO: + if (!p_give_ammo(player, am_clip, 5)) return; + player->message = (GOTCLIPBOX); + break; + + case SPR_ROCK: + if (!p_give_ammo(player, am_misl, 1)) return; + player->message = (GOTROCKET); + break; + + case SPR_BROK: + if (!p_give_ammo(player, am_misl, 5)) return; + player->message = (GOTROCKBOX); + break; + + case SPR_CELL: + if (!p_give_ammo(player, am_cell, 1)) return; + player->message = (GOTCELL); + break; + + case SPR_CELP: + if (!p_give_ammo(player, am_cell, 5)) return; + player->message = (GOTCELLBOX); + break; + + case SPR_SHEL: + if (!p_give_ammo(player, am_shell, 1)) return; + player->message = (GOTSHELLS); + break; + + case SPR_SBOX: + if (!p_give_ammo(player, am_shell, 5)) return; + player->message = (GOTSHELLBOX); + break; + + case SPR_BPAK: + if (!player->backpack) + { + for (i = 0; i < NUMAMMO; i++) + player->maxammo[i] *= 2; + player->backpack = true; + } + + for (i = 0; i < NUMAMMO; i++) + p_give_ammo(player, i, 1); + player->message = (GOTBACKPACK); + break; + + /* weapons */ + + case SPR_BFUG: + if (!p_give_weapon(player, wp_bfg, false)) return; + player->message = (GOTBFG9000); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_MGUN: + if (!p_give_weapon(player, wp_chaingun, + (special->flags & MF_DROPPED) != 0)) + return; + player->message = (GOTCHAINGUN); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_CSAW: + if (!p_give_weapon(player, wp_chainsaw, false)) return; + player->message = (GOTCHAINSAW); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_LAUN: + if (!p_give_weapon(player, wp_missile, false)) return; + player->message = (GOTLAUNCHER); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_PLAS: + if (!p_give_weapon(player, wp_plasma, false)) return; + player->message = (GOTPLASMA); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_SHOT: + if (!p_give_weapon(player, wp_shotgun, + (special->flags & MF_DROPPED) != 0)) + return; + player->message = (GOTSHOTGUN); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_SGN2: + if (!p_give_weapon(player, wp_supershotgun, + (special->flags & MF_DROPPED) != 0)) + return; + player->message = (GOTSHOTGUN2); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + default: + i_error("P_SpecialThing: Unknown gettable thing"); + } + + if (special->flags & MF_COUNTITEM) player->itemcount++; + p_remove_mobj(special); + player->bonuscount += BONUSADD; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (player == &players[consoleplayer]) s_start_sound(NULL, sound); +#endif +} + +/* p_damage_mobj + * Damages both enemies and players "inflictor" is the thing that caused the + * damage creature or missile, can be NULL (slime, etc) "source" is the thing + * to target after taking damage creature or NULL + * + * Source and inflictor are the same for melee attacks. + * Source can be NULL for slime, barrel explosions and other environmental + * stuff. + */ + +void p_damage_mobj(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_point_to_angle2(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 += fixed_mul(thrust, finecosine[ang]); + target->momy += fixed_mul(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_kill_mobj(source, target); + return; + } + + if ((p_random() < target->info->painchance) && + !(target->flags & MF_SKULLFLY)) + { + target->flags |= MF_JUSTHIT; /* fight back! */ + + p_set_mobj_state(target, target->info->painstate); + } + + target->reactiontime = 0; /* we're awake now... */ + + if ((!target->threshold || target->type == MT_VILE) && source && + (source != target || gameversion < exe_doom_1_5) && + 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_set_mobj_state(target, target->info->seestate); + } +} diff --git a/games/NXDoom/src/doom/p_inter.h b/games/NXDoom/src/doom/p_inter.h new file mode 100644 index 00000000000..450349e9c7c --- /dev/null +++ b/games/NXDoom/src/doom/p_inter.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_inter.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef __P_INTER__ +#define __P_INTER__ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean p_give_power(player_t *, int); + +#endif /* __P_INTER__ */ diff --git a/games/NXDoom/src/doom/p_lights.c b/games/NXDoom/src/doom/p_lights.c new file mode 100644 index 00000000000..019e1c941db --- /dev/null +++ b/games/NXDoom/src/doom/p_lights.c @@ -0,0 +1,314 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_lights.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Handle Sector base lighting effects. + * Muzzle flash? + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "m_random.h" +#include "z_zone.h" + +#include "doomdef.h" +#include "p_local.h" + +/* State. */ + +#include "r_state.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* FIRELIGHT FLICKER */ + +/* t_fire_flicker */ + +static void t_fire_flicker(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; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void p_spawn_fire_flicker(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_add_thinker(&flick->thinker); + + flick->thinker.function.acp1 = (actionf_p1)t_fire_flicker; + flick->sector = sector; + flick->maxlight = sector->lightlevel; + flick->minlight = p_find_min_surrounding(sector, sector->lightlevel) + 16; + flick->count = 4; +} + +/* BROKEN LIGHT FLASHING */ + +/* t_light_flash + * Do flashing lights. + */ + +void t_light_flash(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_spawn_light_flash + * After the map has been loaded, scan each sector for specials that spawn + * thinkers + */ + +void p_spawn_light_flash(sector_t *sector) +{ + lightflash_t *flash; + + /* nothing special about it during gameplay */ + + sector->special = 0; + + flash = z_malloc(sizeof(*flash), PU_LEVSPEC, 0); + + p_add_thinker(&flash->thinker); + + flash->thinker.function.acp1 = (actionf_p1)t_light_flash; + flash->sector = sector; + flash->maxlight = sector->lightlevel; + + flash->minlight = p_find_min_surrounding(sector, sector->lightlevel); + flash->maxtime = 64; + flash->mintime = 7; + flash->count = (p_random() & flash->maxtime) + 1; +} + +/* STROBE LIGHT FLASHING */ + +void t_strobe_flash(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_spawn_strobe_flash + * After the map has been loaded, scan each sector for specials that spawn + * thinkers + */ + +void p_spawn_strobe_flash(sector_t *sector, int fast_or_slow, int in_sync) +{ + strobe_t *flash; + + flash = z_malloc(sizeof(*flash), PU_LEVSPEC, 0); + + p_add_thinker(&flash->thinker); + + flash->sector = sector; + flash->darktime = fast_or_slow; + flash->brighttime = STROBEBRIGHT; + flash->thinker.function.acp1 = (actionf_p1)t_strobe_flash; + flash->maxlight = sector->lightlevel; + flash->minlight = p_find_min_surrounding(sector, sector->lightlevel); + + if (flash->minlight == flash->maxlight) flash->minlight = 0; + + /* nothing special about it during gameplay */ + + sector->special = 0; + + if (!in_sync) + flash->count = (p_random() & 7) + 1; + else + flash->count = 1; +} + +/* Start strobing lights (usually from a trigger) */ + +void ev_start_light_strobing(line_t *line) +{ + int secnum; + sector_t *sec; + + secnum = -1; + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) continue; + + p_spawn_strobe_flash(sec, SLOWDARK, 0); + } +} + +/* TURN LINE'S TAG LIGHTS OFF */ + +void ev_turn_tag_lights_off(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 = get_next_sector(templine, sector); + if (!tsec) continue; + if (tsec->lightlevel < min) min = tsec->lightlevel; + } + + sector->lightlevel = min; + } + } +} + +/* TURN LINE'S TAG LIGHTS ON */ + +void ev_light_turn_on(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 = get_next_sector(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_spawn_glowing_light(sector_t *sector) +{ + glow_t *g; + + g = z_malloc(sizeof(*g), PU_LEVSPEC, 0); + + p_add_thinker(&g->thinker); + + g->sector = sector; + g->minlight = p_find_min_surrounding(sector, sector->lightlevel); + g->maxlight = sector->lightlevel; + g->thinker.function.acp1 = (actionf_p1)t_glow; + g->direction = -1; + + sector->special = 0; +} diff --git a/games/NXDoom/src/doom/p_local.h b/games/NXDoom/src/doom/p_local.h new file mode 100644 index 00000000000..30bd51d635a --- /dev/null +++ b/games/NXDoom/src/doom/p_local.h @@ -0,0 +1,268 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_local.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Play functions, animation, global header. + * + ****************************************************************************/ + +#ifndef __P_LOCAL__ +#define __P_LOCAL__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#ifndef __R_LOCAL__ +#include "r_local.h" +#endif + +/* P_SPEC */ + +#include "p_spec.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#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 exclusively for 3 seconds */ + +#define BASETHRESHOLD 100 + +#define ONFLOORZ INT_MIN +#define ONCEILINGZ INT_MAX + +/* Time interval for item respawning. */ + +#define ITEMQUESIZE 128 + +/* Extended MAXINTERCEPTS, to allow for intercepts overrun emulation. */ + +#define MAXINTERCEPTS_ORIGINAL 128 +#define MAXINTERCEPTS (MAXINTERCEPTS_ORIGINAL + 61) + +#define PT_ADDLINES 1 +#define PT_ADDTHINGS 2 +#define PT_EARLYOUT 4 + +/* fraggle: I have increased the size of this buffer. In the original Doom, + * overrunning past this limit caused other bits of memory to be overwritten, + * affecting demo playback. However, in doing so, the limit was still + * exceeded. So we have to support more than 8 specials. + * + * We keep the original limit, to detect what variables in memory were + * overwritten (see SpechitOverrun()) + */ + +#define MAXSPECIALCROSS 20 +#define MAXSPECIALCROSS_ORIGINAL 8 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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 */ + boolean isaline; + union + { + mobj_t *thing; + line_t *line; + } d; +} intercept_t; + +typedef boolean (*traverser_t)(intercept_t *in); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* P_TICK */ + +/* both the head and tail of the thinker list */ + +extern thinker_t thinkercap; + +extern mapthing_t itemrespawnque[ITEMQUESIZE]; +extern int itemrespawntime[ITEMQUESIZE]; +extern int iquehead; +extern int iquetail; + +extern intercept_t intercepts[MAXINTERCEPTS]; +extern intercept_t *intercept_p; + +extern fixed_t opentop; +extern fixed_t openbottom; +extern fixed_t openrange; +extern fixed_t lowfloor; + +extern divline_t trace; + +/* If "floatok" true, move would be ok if within "tmfloorz - tmceilingz". */ + +extern boolean floatok; +extern fixed_t tmfloorz; +extern fixed_t tmceilingz; + +extern line_t *ceilingline; + +extern line_t *spechit[MAXSPECIALCROSS]; +extern int numspechit; + +extern mobj_t *linetarget; /* who got hit (or NULL) */ + +extern fixed_t attackrange; + +/* slopes to top and bottom of target */ + +extern fixed_t topslope; +extern fixed_t bottomslope; + +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 */ + +extern int maxammo[NUMAMMO]; +extern int clipammo[NUMAMMO]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void p_init_thinkers(void); +void p_add_thinker(thinker_t *thinker); +void p_remove_thinker(thinker_t *thinker); + +/* P_PSPR */ + +void p_setup_psprites(player_t *curplayer); +void p_move_psprites(player_t *curplayer); +void p_drop_weapon(player_t *player); + +/* P_USER */ + +void p_player_think(player_t *player); + +/* P_MOBJ */ + +void p_respawn_specials(void); + +mobj_t *p_spawn_mobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); + +void p_remove_mobj(mobj_t *th); +mobj_t *p_subst_null_mobj(mobj_t *th); +boolean p_set_mobj_state(mobj_t *mobj, statenum_t state); +void p_mobj_thinker(mobj_t *mobj); + +void p_spawn_puff(fixed_t x, fixed_t y, fixed_t z); +void p_spawn_blood(fixed_t x, fixed_t y, fixed_t z, int damage); +mobj_t *p_spawn_missile(mobj_t *source, mobj_t *dest, mobjtype_t type); +void p_spawn_player_missile(mobj_t *source, mobjtype_t type); + +/* P_ENEMY */ + +void p_noise_alert(mobj_t *target, mobj_t *emmiter); + +fixed_t p_approx_distance(fixed_t dx, fixed_t dy); +int p_point_on_line_side(fixed_t x, fixed_t y, line_t *line); +int p_point_on_divline_side(fixed_t x, fixed_t y, divline_t *line); +int p_box_on_line_side(fixed_t *tmbox, line_t *ld); + +void p_line_opening(line_t *linedef); + +boolean p_block_lines_iterator(int x, int y, boolean (*func)(line_t *)); +boolean p_block_things_iterator(int x, int y, boolean (*func)(mobj_t *)); + +boolean p_path_traverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, boolean (*trav)(intercept_t *)); + +void p_unset_thing_position(mobj_t *thing); +void p_set_thing_position(mobj_t *thing); + +/* P_MAP */ + +boolean p_check_position(mobj_t *thing, fixed_t x, fixed_t y); +boolean p_try_move(mobj_t *thing, fixed_t x, fixed_t y); +boolean p_teleport_move(mobj_t *thing, fixed_t x, fixed_t y); +void p_slide_move(mobj_t *mo); +boolean p_check_sight(mobj_t *t1, mobj_t *t2); +void p_use_lines(player_t *player); + +boolean p_change_sector(sector_t *sector, boolean crunch); + +fixed_t p_aim_line_attack(mobj_t *t1, angle_t angle, fixed_t distance); + +void p_line_attack(mobj_t *t1, angle_t angle, fixed_t distance, + fixed_t slope, int damage); + +void p_radius_attack(mobj_t *spot, mobj_t *source, int damage); + +/* P_INTER */ + +void p_touch_special_thing(mobj_t *special, mobj_t *toucher); + +void p_damage_mobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, + int damage); + +#endif /* __P_LOCAL__ */ diff --git a/games/NXDoom/src/doom/p_map.c b/games/NXDoom/src/doom/p_map.c new file mode 100644 index 00000000000..2b4aa4386b4 --- /dev/null +++ b/games/NXDoom/src/doom/p_map.c @@ -0,0 +1,1470 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_map.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard, Andrey Budko + * + * 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. + * + * DESCRIPTION: + * Movement, collision handling. + * Shooting and aiming. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_misc.h" + +#include "i_system.h" +#include "m_bbox.h" +#include "m_random.h" + +#include "doomdef.h" +#include "m_argv.h" +#include "m_misc.h" +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Spechit overrun magic value. + * + * This is the value used by PrBoom-plus. I think the value below is + * actually better and works with more demos. However, I think + * it's better for the spechits emulation to be compatible with + * PrBoom-plus, at least so that the big spechits emulation list + * on Doomworld can also be used with Chocolate Doom. + */ + +#define DEFAULT_SPECHIT_MAGIC 0x01C09C98 + +/* This is from a post by myk on the Doomworld forums, + * outputted from entryway's spechit_magic generator for + * s205n546.lmp. The _exact_ value of this isn't too + * important; as long as it is in the right general + * range, it will usually work. Otherwise, we can use + * the generator (hacked doom2.exe) and provide it + * with -spechit. + */ + +/* #define DEFAULT_SPECHIT_MAGIC 0x84f968e8 */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +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". + */ + +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; + +/* 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_line_attack */ + +mobj_t *linetarget; /* who got hit (or NULL) */ +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; + +/* USE LINES */ + +mobj_t *usething; + +/* RADIUS ATTACK */ + +mobj_t *bombsource; +mobj_t *bombspot; +int bombdamage; + +/* 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_change_sector again to undo the changes. + */ + +boolean crushchange; +boolean nofit; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* TELEPORT MOVE */ + +static boolean pit_stomp_thing(mobj_t *thing) +{ + fixed_t blockdist; + + if (!(thing->flags & MF_SHOOTABLE)) return true; + + blockdist = thing->radius + tmthing->radius; + + if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) + { + return true; /* didn't hit it */ + } + + /* 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_damage_mobj(thing, tmthing, tmthing, 10000); + + return true; +} + +/* Code to emulate the behavior of Vanilla Doom when encountering an overrun + * of the spechit array. This is by Andrey Budko (e6y) and comes from his + * PrBoom plus port. A big thanks to Andrey for this. + */ + +static void spechit_overrun(line_t *ld) +{ + static unsigned int baseaddr = 0; + unsigned int addr; + + if (baseaddr == 0) + { + int p; + + /* This is the first time we have had an overrun. Work out + * what base address we are going to use. + * Allow a spechit value to be specified on the command line. + */ + + /* @category compat + * @arg + * + * Use the specified magic value when emulating spechit overruns. + */ + + p = m_check_parm_with_args("-spechit", 1); + + if (p > 0) + { + m_str_to_int(myargv[p + 1], (int *)&baseaddr); + } + else + { + baseaddr = DEFAULT_SPECHIT_MAGIC; + } + } + + /* Calculate address used in doom2.exe */ + + addr = baseaddr + (ld - lines) * 0x3e; + + switch (numspechit) + { + case 9: + case 10: + case 11: + case 12: + tmbbox[numspechit - 9] = addr; + break; + case 13: + crushchange = addr; + break; + case 14: + nofit = addr; + break; + default: + fprintf(stderr, + "SpechitOverrun: Warning: unable to emulate" + "an overrun where numspechit=%i\n", + numspechit); + break; + } +} + +/* pit_check_line + * Adjusts tmfloorz and tmceilingz as lines are contacted + */ + +static boolean pit_check_line(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_box_on_line_side(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_line_opening(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++; + + /* fraggle: spechits overrun emulation code from prboom-plus */ + + if (numspechit > MAXSPECIALCROSS_ORIGINAL) + { + spechit_overrun(ld); + } + } + + return true; +} + +/* pit_radius_attack + * "bombsource" is the creature that caused the explosion at "bombspot". + */ + +static boolean pit_radius_attack(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 = abs(thing->x - bombspot->x); + dy = 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_check_sight(thing, bombspot)) + { + /* must be in direct path */ + + p_damage_mobj(thing, bombspot, bombsource, bombdamage - dist); + } + + return true; +} + +/* p_things_height_clip + * 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. + */ + +static boolean p_things_height_clip(mobj_t *thing) +{ + boolean onfloor; + + onfloor = (thing->z == thing->floorz); + + p_check_position(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; +} + +/* p_hit_slide_line + * Adjusts the xmove / ymove so that the next move will slide along the wall. + */ + +static void p_hit_slide_line(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_point_on_line_side(slidemo->x, slidemo->y, ld); + + lineangle = r_point_to_angle2(0, 0, ld->dx, ld->dy); + + if (side == 1) lineangle += ANG180; + + moveangle = r_point_to_angle2(0, 0, tmxmove, tmymove); + deltaangle = moveangle - lineangle; + + if (deltaangle > ANG180) deltaangle += ANG180; + + lineangle >>= ANGLETOFINESHIFT; + deltaangle >>= ANGLETOFINESHIFT; + + movelen = p_approx_distance(tmxmove, tmymove); + newlen = fixed_mul(movelen, finecosine[deltaangle]); + + tmxmove = fixed_mul(newlen, finecosine[lineangle]); + tmymove = fixed_mul(newlen, finesine[lineangle]); +} + +/* ptr_slide_traverse */ + +static boolean ptr_slide_traverse(intercept_t *in) +{ + line_t *li; + + if (!in->isaline) i_error("ptr_slide_traverse: not a line?"); + + li = in->d.line; + + if (!(li->flags & ML_TWOSIDED)) + { + if (p_point_on_line_side(slidemo->x, slidemo->y, li)) + { + return true; /* don't hit the back side */ + } + + goto isblocking; + } + + /* set openrange, opentop, openbottom */ + + p_line_opening(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 */ +} + +static boolean ptr_use_traverse(intercept_t *in) +{ + int side; + + if (!in->d.line->special) + { + p_line_opening(in->d.line); + if (openrange <= 0) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(usething, SFX_NOWAY); +#endif + + /* can't use through a wall */ + + return false; + } + + /* not a special line, but keep checking */ + + return true; + } + + side = 0; + if (p_point_on_line_side(usething->x, usething->y, in->d.line) == 1) + { + side = 1; + } + + /* return false; don't use back side */ + + p_use_special_line(usething, in->d.line, side); + + /* can't use for than one special line in a row */ + + return false; +} + +/* ptr_aim_traverse + * Sets linetaget and aimslope when a target is aimed at. + */ + +static boolean ptr_aim_traverse(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_line_opening(li); + + if (openbottom >= opentop) return false; /* stop */ + + dist = fixed_mul(attackrange, in->frac); + + if (li->backsector == NULL || + li->frontsector->floorheight != li->backsector->floorheight) + { + slope = fixed_div(openbottom - shootz, dist); + if (slope > bottomslope) bottomslope = slope; + } + + if (li->backsector == NULL || + li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = fixed_div(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 = fixed_mul(attackrange, in->frac); + thingtopslope = fixed_div(th->z + th->height - shootz, dist); + + if (thingtopslope < bottomslope) return true; /* shot over the thing */ + + thingbottomslope = fixed_div(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_shoot_traverse */ + +static boolean ptr_shoot_traverse(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_shoot_special_line(shootthing, li); + + if (!(li->flags & ML_TWOSIDED)) goto hitline; + + /* crosses a two sided line */ + + p_line_opening(li); + + dist = fixed_mul(attackrange, in->frac); + + /* e6y: emulation of missed back side on two-sided lines. + * backsector can be NULL when emulating missing back side. + */ + + if (li->backsector == NULL) + { + slope = fixed_div(openbottom - shootz, dist); + if (slope > aimslope) goto hitline; + + slope = fixed_div(opentop - shootz, dist); + if (slope < aimslope) goto hitline; + } + else + { + if (li->frontsector->floorheight + != li->backsector->floorheight) + { + slope = fixed_div(openbottom - shootz, dist); + if (slope > aimslope) goto hitline; + } + + if (li->frontsector->ceilingheight + != li->backsector->ceilingheight) + { + slope = fixed_div(opentop - shootz, dist); + if (slope < aimslope) goto hitline; + } + } + + /* shot continues */ + + return true; + + /* hit line */ + + hitline: + + /* position a bit closer */ + + frac = in->frac - fixed_div(4 * FRACUNIT, attackrange); + x = trace.x + fixed_mul(trace.dx, frac); + y = trace.y + fixed_mul(trace.dy, frac); + z = shootz + fixed_mul(aimslope, fixed_mul(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_spawn_puff(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 = fixed_mul(attackrange, in->frac); + thingtopslope = fixed_div(th->z + th->height - shootz, dist); + + if (thingtopslope < aimslope) return true; /* shot over the thing */ + + thingbottomslope = fixed_div(th->z - shootz, dist); + + if (thingbottomslope > aimslope) return true; /* shot under the thing */ + + /* hit thing position a bit closer */ + + frac = in->frac - fixed_div(10 * FRACUNIT, attackrange); + + x = trace.x + fixed_mul(trace.dx, frac); + y = trace.y + fixed_mul(trace.dy, frac); + z = shootz + fixed_mul(aimslope, fixed_mul(frac, attackrange)); + + /* Spawn bullet puffs or blod spots, + * depending on target type. + */ + + if (in->d.thing->flags & MF_NOBLOOD) + p_spawn_puff(x, y, z); + else + p_spawn_blood(x, y, z, la_damage); + + if (la_damage) p_damage_mobj(th, shootthing, shootthing, la_damage); + + /* don't go any farther */ + + return false; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_teleport_move */ + +boolean p_teleport_move(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_point_in_subsector(x, y); + ceilingline = NULL; + + /* 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_block_things_iterator(bx, by, pit_stomp_thing)) + { + return false; + } + } + } + + /* the move is ok, + * so link the thing into its new position + */ + + p_unset_thing_position(thing); + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->x = x; + thing->y = y; + + p_set_thing_position(thing); + + return true; +} + +boolean pit_check_thing(mobj_t *thing) +{ + fixed_t blockdist; + boolean solid; + int damage; + + if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE))) return true; + + blockdist = thing->radius + tmthing->radius; + + if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) + { + return true; /* didn't hit it */ + } + + /* 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_damage_mobj(thing, tmthing, tmthing, damage); + + tmthing->flags &= ~MF_SKULLFLY; + tmthing->momx = tmthing->momy = tmthing->momz = 0; + + p_set_mobj_state(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; + + /* sdh: Add deh_species_infighting here. We can override the + * "monsters of the same species can't hurt each other" behavior + * through dehacked patches + */ + + if (thing->type != MT_PLAYER && !deh_species_infighting) + { + /* 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_damage_mobj(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) != 0; + if (tmflags & MF_PICKUP) + { + /* can remove thing */ + + p_touch_special_thing(thing, tmthing); + } + + return !solid; + } + + return !(thing->flags & MF_SOLID); +} + +/* MOVEMENT CLIPPING */ + +/* p_check_position + * 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 + */ + +boolean p_check_position(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_point_in_subsector(x, y); + ceilingline = NULL; + + /* 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_block_things_iterator(bx, by, pit_check_thing)) + { + 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_block_lines_iterator(bx, by, pit_check_line)) + { + return false; + } + } + } + + return true; +} + +/* p_try_move + * Attempt to move to a new position, + * crossing special lines unless MF_TELEPORT is set. + */ + +boolean p_try_move(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_check_position(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_unset_thing_position(thing); + + oldx = thing->x; + oldy = thing->y; + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->x = x; + thing->y = y; + + p_set_thing_position(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_point_on_line_side(thing->x, thing->y, ld); + oldside = p_point_on_line_side(oldx, oldy, ld); + if (side != oldside) + { + if (ld->special) + { + p_cross_special_line(ld - lines, oldside, thing); + } + } + } + } + + return true; +} + +/* p_slide_move + * 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_slide_move(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_path_traverse(leadx, leady, leadx + mo->momx, leady + mo->momy, + PT_ADDLINES, ptr_slide_traverse); + p_path_traverse(trailx, leady, trailx + mo->momx, leady + mo->momy, + PT_ADDLINES, ptr_slide_traverse); + p_path_traverse(leadx, traily, leadx + mo->momx, traily + mo->momy, + PT_ADDLINES, ptr_slide_traverse); + + /* move up to the wall */ + + if (bestslidefrac == FRACUNIT + 1) + { + /* the move most have hit the middle, so stairstep */ + + stairstep: + if (!p_try_move(mo, mo->x, mo->y + mo->momy)) + p_try_move(mo, mo->x + mo->momx, mo->y); + return; + } + + /* fudge a bit to make sure it doesn't hit */ + + bestslidefrac -= 0x800; + if (bestslidefrac > 0) + { + newx = fixed_mul(mo->momx, bestslidefrac); + newy = fixed_mul(mo->momy, bestslidefrac); + + if (!p_try_move(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 = fixed_mul(mo->momx, bestslidefrac); + tmymove = fixed_mul(mo->momy, bestslidefrac); + + p_hit_slide_line(bestslideline); /* clip the moves */ + + mo->momx = tmxmove; + mo->momy = tmymove; + + if (!p_try_move(mo, mo->x + tmxmove, mo->y + tmymove)) + { + goto retry; + } +} + +/* p_aim_line_attack */ + +fixed_t p_aim_line_attack(mobj_t *t1, angle_t angle, fixed_t distance) +{ + fixed_t x2; + fixed_t y2; + + t1 = p_subst_null_mobj(t1); + + 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 = (SCREENHEIGHT / 2) * FRACUNIT / (SCREENWIDTH / 2); + bottomslope = -(SCREENHEIGHT / 2) * FRACUNIT / (SCREENWIDTH / 2); + + attackrange = distance; + linetarget = NULL; + + p_path_traverse(t1->x, t1->y, x2, y2, PT_ADDLINES | PT_ADDTHINGS, + ptr_aim_traverse); + + if (linetarget) return aimslope; + + return 0; +} + +/* p_line_attack + * + * If damage == 0, it is just a test trace that will leave linetarget set. + */ + +void p_line_attack(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_path_traverse(t1->x, t1->y, x2, y2, PT_ADDLINES | PT_ADDTHINGS, + ptr_shoot_traverse); +} + +/* p_use_lines + * Looks for special lines in front of the player to activate. + */ + +void p_use_lines(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_path_traverse(x1, y1, x2, y2, PT_ADDLINES, ptr_use_traverse); +} + +/* p_radius_attack + * Source is the creature that caused the explosion at spot. + */ + +void p_radius_attack(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_block_things_iterator(x, y, pit_radius_attack); + } + } +} + +/* pit_change_sector */ + +static boolean pit_change_sector(mobj_t *thing) +{ + mobj_t *mo; + + if (p_things_height_clip(thing)) + { + return true; /* keep checking */ + } + + /* crunch bodies to giblets */ + + if (thing->health <= 0) + { + p_set_mobj_state(thing, S_GIBS); + + if (gameversion > exe_doom_1_2) thing->flags &= ~MF_SOLID; + thing->height = 0; + thing->radius = 0; + + /* keep checking */ + + return true; + } + + /* crunch dropped items */ + + if (thing->flags & MF_DROPPED) + { + p_remove_mobj(thing); + + return true; /* keep checking */ + } + + if (!(thing->flags & MF_SHOOTABLE)) + { + return true; /* assume it is bloody gibs or something */ + } + + nofit = true; + + if (crushchange && !(leveltime & 3)) + { + p_damage_mobj(thing, NULL, NULL, 10); + + /* spray blood in a random direction */ + + mo = p_spawn_mobj(thing->x, thing->y, thing->z + thing->height / 2, + MT_BLOOD); + + mo->momx = p_sub_random() << 12; + mo->momy = p_sub_random() << 12; + } + + /* keep checking (crush other things) */ + + return true; +} + +/* p_change_sector */ + +boolean p_change_sector(sector_t *sector, 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_block_things_iterator(x, y, pit_change_sector); + } + } + + return nofit; +} diff --git a/games/NXDoom/src/doom/p_maputl.c b/games/NXDoom/src/doom/p_maputl.c new file mode 100644 index 00000000000..f5c1932740f --- /dev/null +++ b/games/NXDoom/src/doom/p_maputl.c @@ -0,0 +1,939 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_maputl.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2005, 2006 Andrey Budko + * + * 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. + * + * DESCRIPTION: + * Movement/collision utility functions, + * as used by function in p_map.c. + * BLOCKMAP Iterator functions, + * and some PIT_* functions to use for iteration. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "m_bbox.h" +#include "m_misc.h" + +#include "doomdef.h" +#include "doomstat.h" +#include "p_local.h" + +/* State. */ + +#include "r_state.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Intercepts Overrun emulation, from PrBoom-plus. + * Thanks to Andrey Budko (entryway) for researching this and his + * implementation of Intercepts Overrun emulation in PrBoom-plus + * which this is based on. + */ + +typedef struct +{ + int len; + void *addr; + boolean int16_array; +} intercepts_overrun_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +fixed_t p_intercept_vector(divline_t *v2, divline_t *v1); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Intercepts memory table. This is where various variables are located + * in memory in Vanilla Doom. When the intercepts table overflows, we + * need to write to them. + * + * Almost all of the values to overwrite are 32-bit integers, except for + * playerstarts, which is effectively an array of 16-bit integers and + * must be treated differently. + */ + +static intercepts_overrun_t g_intercepts_overrun[] = +{ + {4, NULL, false}, + {4, NULL, /* &earlyout, */ false}, + {4, NULL, /* &intercept_p, */ false}, + {4, &lowfloor, false}, + {4, &openbottom, false}, + {4, &opentop, false}, + {4, &openrange, false}, + {4, NULL, false}, + {120, NULL, /* &activeplats, */ false}, + {8, NULL, false}, + {4, &bulletslope, false}, + {4, NULL, /* &swingx, */ false}, + {4, NULL, /* &swingy, */ false}, + {4, NULL, false}, + {40, &playerstarts, true}, + {4, NULL, /* &blocklinks, */ false}, + {4, &bmapwidth, false}, + {4, NULL, /* &blockmap, */ false}, + {4, &bmaporgx, false}, + {4, &bmaporgy, false}, + {4, NULL, /* &blockmaplump, */ false}, + {4, &bmapheight, false}, + {0, NULL, false}, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +fixed_t opentop; +fixed_t openbottom; +fixed_t openrange; +fixed_t lowfloor; + +/* INTERCEPT ROUTINES */ + +intercept_t intercepts[MAXINTERCEPTS]; +intercept_t *intercept_p; + +divline_t trace; +boolean earlyout; +int ptflags; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void p_make_divline(line_t *li, divline_t *dl) +{ + dl->x = li->v1->x; + dl->y = li->v1->y; + dl->dx = li->dx; + dl->dy = li->dy; +} + +/* Overwrite a specific memory location with a value. */ + +static void intercepts_mem_overrun(int location, int value) +{ + int i; + int offset; + int index; + void *addr; + + i = 0; + offset = 0; + + /* Search down the array until we find the right entry */ + + while (g_intercepts_overrun[i].len != 0) + { + if (offset + g_intercepts_overrun[i].len > location) + { + addr = g_intercepts_overrun[i].addr; + + /* Write the value to the memory location. + * 16-bit and 32-bit values are written differently. + */ + + if (addr != NULL) + { + if (g_intercepts_overrun[i].int16_array) + { + index = (location - offset) / 2; + ((short *)addr)[index] = value & 0xffff; + ((short *)addr)[index + 1] = (value >> 16) & 0xffff; + } + else + { + index = (location - offset) / 4; + ((int *)addr)[index] = value; + } + } + + break; + } + + offset += g_intercepts_overrun[i].len; + ++i; + } +} + +/* Emulate overruns of the intercepts[] array. */ + +static void intercepts_overrun(int num_intercepts, intercept_t *intercept) +{ + int location; + + if (num_intercepts <= MAXINTERCEPTS_ORIGINAL) + { + return; /* No overrun */ + } + + location = (num_intercepts - MAXINTERCEPTS_ORIGINAL - 1) * 12; + + /* Overwrite memory that is overwritten in Vanilla Doom, using + * the values from the intercept structure. + * + * Note: the ->d.{thing,line} member should really have its + * address translated into the correct address value for + * Vanilla Doom. + */ + + intercepts_mem_overrun(location, intercept->frac); + intercepts_mem_overrun(location + 4, intercept->isaline); + intercepts_mem_overrun(location + 8, (intptr_t)intercept->d.thing); +} + +/* pit_add_line_intercepts. + * 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. + */ + +static boolean pit_add_line_intercepts(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_point_on_divline_side(ld->v1->x, ld->v1->y, &trace); + s2 = p_point_on_divline_side(ld->v2->x, ld->v2->y, &trace); + } + else + { + s1 = p_point_on_line_side(trace.x, trace.y, ld); + s2 = p_point_on_line_side(trace.x + trace.dx, trace.y + trace.dy, ld); + } + + if (s1 == s2) return true; /* line isn't crossed */ + + /* hit the line */ + + p_make_divline(ld, &dl); + frac = p_intercept_vector(&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; + intercepts_overrun(intercept_p - intercepts, intercept_p); + intercept_p++; + + return true; /* continue */ +} + +static boolean pit_add_thing_intercepts(mobj_t *thing) +{ + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + int s1; + int s2; + + boolean tracepositive; + + divline_t dl; + + fixed_t frac; + + tracepositive = (trace.dx ^ trace.dy) > 0; + + /* check a corner to corner cross-section 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_point_on_divline_side(x1, y1, &trace); + s2 = p_point_on_divline_side(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_intercept_vector(&trace, &dl); + + if (frac < 0) return true; /* behind source */ + + intercept_p->frac = frac; + intercept_p->isaline = false; + intercept_p->d.thing = thing; + intercepts_overrun(intercept_p - intercepts, intercept_p); + intercept_p++; + + return true; /* keep going */ +} + +/* p_traverse_intercepts + * Returns true if the traverser function returns true for all lines. + */ + +static boolean p_traverse_intercepts(traverser_t func, fixed_t maxfrac) +{ + int count; + fixed_t dist; + intercept_t *scan; + intercept_t *in; + + count = intercept_p - intercepts; + + in = 0; /* shut up compiler warning */ + + while (count--) + { + dist = INT_MAX; + 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 = INT_MAX; + } + + return true; /* everything was traversed */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_approx_distance + * Gives an estimation of distance (not exact) + */ + +fixed_t p_approx_distance(fixed_t dx, fixed_t dy) +{ + dx = abs(dx); + dy = abs(dy); + if (dx < dy) return dx + dy - (dx >> 1); + return dx + dy - (dy >> 1); +} + +/* p_point_on_line_side + * Returns 0 or 1 + */ + +int p_point_on_line_side(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 = fixed_mul(line->dy >> FRACBITS, dx); + right = fixed_mul(dy, line->dx >> FRACBITS); + + if (right < left) return 0; /* front side */ + + return 1; /* back side */ +} + +/* p_box_on_line_side + * Considers the line to be infinite + * Returns side 0 or 1, -1 if box crosses the line. + */ + +int p_box_on_line_side(fixed_t *tmbox, line_t *ld) +{ + int p1 = 0; + int p2 = 0; + + 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_point_on_line_side(tmbox[BOXLEFT], tmbox[BOXTOP], ld); + p2 = p_point_on_line_side(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); + break; + + case ST_NEGATIVE: + p1 = p_point_on_line_side(tmbox[BOXRIGHT], tmbox[BOXTOP], ld); + p2 = p_point_on_line_side(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); + break; + } + + if (p1 == p2) return p1; + return -1; +} + +/* p_point_on_divline_side + * Returns 0 or 1. + */ + +int p_point_on_divline_side(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 = fixed_mul(line->dy >> 8, dx >> 8); + right = fixed_mul(dy >> 8, line->dx >> 8); + + if (right < left) return 0; /* front side */ + + return 1; /* back side */ +} + +/* p_intercept_vector + * + * Returns the fractional intercept point along the first divline. + * This is only called by the addthings and addlines traversers. + */ + +fixed_t p_intercept_vector(divline_t *v2, divline_t *v1) +{ +#if 1 + fixed_t frac; + fixed_t num; + fixed_t den; + + den = fixed_mul(v1->dy >> 8, v2->dx) - fixed_mul(v1->dx >> 8, v2->dy); + + if (den == 0) return 0; + + num = fixed_mul((v1->x - v2->x) >> 8, v1->dy) + + fixed_mul((v2->y - v1->y) >> 8, v1->dx); + + frac = fixed_div(num, den); + + return frac; +#else /* UNUSED, float debug. */ + float frac; + float num; + float den; + float v1x; + float v1y; + float v1dx; + float v1dy; + float v2x; + float v2y; + float v2dx; + float v2dy; + + v1x = (float)v1->x / FRACUNIT; + v1y = (float)v1->y / FRACUNIT; + v1dx = (float)v1->dx / FRACUNIT; + v1dy = (float)v1->dy / FRACUNIT; + v2x = (float)v2->x / FRACUNIT; + v2y = (float)v2->y / FRACUNIT; + v2dx = (float)v2->dx / FRACUNIT; + v2dy = (float)v2->dy / FRACUNIT; + + den = v1dy * v2dx - v1dx * v2dy; + + if (den == 0) return 0; /* parallel */ + + num = (v1x - v2x) * v1dy + (v2y - v1y) * v1dx; + frac = num / den; + + return frac * FRACUNIT; +#endif +} + +/* p_line_opening + * Sets opentop and openbottom to the window through a two sided line. + * OPTIMIZE: keep this precalculated + */ + +void p_line_opening(line_t *p_linedef) +{ + sector_t *front; + sector_t *back; + + if (p_linedef->sidenum[1] == -1) + { + /* single sided line */ + + openrange = 0; + return; + } + + front = p_linedef->frontsector; + back = p_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_unset_thing_position + * + * Unlinks a thing from block map and sectors. + * + * On each position change, BLOCKMAP and other lookups maintaining lists to + * things inside these structures need to be updated. + */ + +void p_unset_thing_position(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_set_thing_position + * Links a thing into both a block and a subsector based on it's x y. + * Sets thing->subsector properly + */ + +void p_set_thing_position(mobj_t *thing) +{ + subsector_t *ss; + sector_t *sec; + int blockx; + int blocky; + mobj_t **link; + + /* link into subsector */ + + ss = r_point_in_subsector(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 = NULL; + 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 = NULL; + thing->bnext = *link; + if (*link) (*link)->bprev = thing; + + *link = thing; + } + else + { + /* thing is off the map */ + + thing->bnext = thing->bprev = NULL; + } + } +} + +/* 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_block_lines_iterator + * The validcount flags are used to avoid checking lines + * that are marked in multiple mapblocks, + * so increment validcount before the first call + * to p_block_lines_iterator, then make one or more calls + * to it. + */ + +boolean p_block_lines_iterator(int x, int y, 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 */ +} + +boolean p_block_things_iterator(int x, int y, boolean (*func)(mobj_t *)) +{ + mobj_t *mobj; + + if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) + { + return true; + } + + LINKED_LIST_CHECK_NO_CYCLE(mobj_t, blocklinks[y * bmapwidth + x], bnext); + + for (mobj = blocklinks[y * bmapwidth + x]; mobj; mobj = mobj->bnext) + { + if (!func(mobj)) return false; + } + + return true; +} + +/* p_path_traverse + * + * 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. + */ + +boolean p_path_traverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, 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) != 0; + + 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 = fixed_div(y2 - y1, abs(x2 - x1)); + } + else if (xt2 < xt1) + { + mapxstep = -1; + partial = (x1 >> MAPBTOFRAC) & (FRACUNIT - 1); + ystep = fixed_div(y2 - y1, abs(x2 - x1)); + } + else + { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256 * FRACUNIT; + } + + yintercept = (y1 >> MAPBTOFRAC) + fixed_mul(partial, ystep); + + if (yt2 > yt1) + { + mapystep = 1; + partial = FRACUNIT - ((y1 >> MAPBTOFRAC) & (FRACUNIT - 1)); + xstep = fixed_div(x2 - x1, abs(y2 - y1)); + } + else if (yt2 < yt1) + { + mapystep = -1; + partial = (y1 >> MAPBTOFRAC) & (FRACUNIT - 1); + xstep = fixed_div(x2 - x1, abs(y2 - y1)); + } + else + { + mapystep = 0; + partial = FRACUNIT; + xstep = 256 * FRACUNIT; + } + + xintercept = (x1 >> MAPBTOFRAC) + fixed_mul(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_block_lines_iterator(mapx, mapy, pit_add_line_intercepts)) + { + return false; /* early out */ + } + } + + if (flags & PT_ADDTHINGS) + { + if (!p_block_things_iterator(mapx, mapy, pit_add_thing_intercepts)) + { + 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_traverse_intercepts(trav, FRACUNIT); +} diff --git a/games/NXDoom/src/doom/p_mobj.c b/games/NXDoom/src/doom/p_mobj.c new file mode 100644 index 00000000000..4b81fa45afc --- /dev/null +++ b/games/NXDoom/src/doom/p_mobj.c @@ -0,0 +1,1126 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_mobj.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Moving object handling. Spawn functions. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "i_system.h" +#include "m_random.h" +#include "z_zone.h" + +#include "doomdef.h" +#include "p_local.h" + +#include "hu_stuff.h" +#include "st_stuff.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "doomstat.h" + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Use a heuristic approach to detect infinite state cycles: Count the number + * of times the loop in p_set_mobj_state() executes and exit with an error + * once an arbitrary very large limit is reached. + */ + +#define MOBJ_CYCLE_LIMIT 1000000 + +#define STOPSPEED 0x1000 +#define FRICTION 0xe800 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int test; + +mapthing_t itemrespawnque[ITEMQUESIZE]; +int itemrespawntime[ITEMQUESIZE]; +int iquehead; +int iquetail; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void g_player_reborn(int player); +void p_spawn_map_thing(mapthing_t *mthing); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: p_explode_missile + ****************************************************************************/ + +static void p_explode_missile(mobj_t *mo) +{ + mo->momx = mo->momy = mo->momz = 0; + + p_set_mobj_state(mo, mobjinfo[mo->type].deathstate); + + mo->tics -= p_random() & 3; + + if (mo->tics < 1) mo->tics = 1; + + mo->flags &= ~MF_MISSILE; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (mo->info->deathsound) s_start_sound(mo, mo->info->deathsound); +#endif +} + +/**************************************************************************** + * Name: p_xy_movement + ****************************************************************************/ + +static void p_xy_movement(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_set_mobj_state(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_try_move(mo, ptryx, ptryy)) + { + /* blocked move */ + + if (mo->player) + { + /* try to slide along it */ + + p_slide_move(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_remove_mobj(mo); + return; + } + + p_explode_missile(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_set_mobj_state(player->mo, S_PLAY); + + mo->momx = 0; + mo->momy = 0; + } + else + { + mo->momx = fixed_mul(mo->momx, FRICTION); + mo->momy = fixed_mul(mo->momy, FRICTION); + } +} + +/**************************************************************************** + * Name: p_z_movement + ****************************************************************************/ + +static void p_z_movement(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; + } + + mo->z += mo->momz; /* adjust height */ + + 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_approx_distance(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. + * + * cph - This was the a bug in the linuxdoom-1.10 source which + * caused it not to sync Doom 2 v1.9 demos. Someone + * added the above comment and moved up the following code. So + * demos would desync in close lost soul fights. + * Note that this only applies to original Doom 1 or Doom2 demos - not + * Final Doom and Ultimate Doom. So we test demo_compatibility *and* + * gamemission. (Note we assume that Doom1 is always Ult Doom, which + * seems to hold for most published demos.) + * + * fraggle - cph got the logic here slightly wrong. There are three + * versions of Doom 1.9: + * + * * The version used in registered doom 1.9 + doom2 - no bounce + * * The version used in ultimate doom - has bounce + * * The version used in final doom - has bounce + * + * So we need to check that this is either retail or commercial + * (but not doom2) + */ + + int correct_lost_soul_bounce = gameversion >= exe_ultimate; + + if (correct_lost_soul_bounce && 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; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_OOF); +#endif + } + + mo->momz = 0; + } + + mo->z = mo->floorz; + + /* cph 2001/05/26 - + * See lost soul bouncing comment above. We need this here for bug + * compatibility with original Doom2 v1.9 - if a soul is charging and + * hit by a raising floor this incorrectly reverses its Y momentum. + */ + + if (!correct_lost_soul_bounce && mo->flags & MF_SKULLFLY) + mo->momz = -mo->momz; + + if ((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP)) + { + p_explode_missile(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_explode_missile(mo); + return; + } + } +} + +/**************************************************************************** + * Name: p_nightmare_respawn + ****************************************************************************/ + +static void p_nightmare_respawn(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; + + /* something is occupying it's position? */ + + if (!p_check_position(mobj, x, y)) return; /* no respawn */ + + /* spawn a teleport fog at old spot because of removal of the body? */ + + mo = p_spawn_mobj(mobj->x, mobj->y, mobj->subsector->sector->floorheight, + MT_TFOG); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + /* initiate teleport sound */ + + s_start_sound(mo, SFX_TELEPT); +#endif + + /* spawn a teleport fog at the new spot */ + + ss = r_point_in_subsector(x, y); + + mo = p_spawn_mobj(x, y, ss->sector->floorheight, MT_TFOG); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_TELEPT); +#endif + + mthing = &mobj->spawnpoint; /* spawn the new monster */ + + /* spawn it */ + + if (mobj->info->flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + /* inherit attributes from deceased one */ + + mo = p_spawn_mobj(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_remove_mobj(mobj); +} + +/**************************************************************************** + * Name: p_check_missile_spawn + * + * Description: + * Moves the missile forward a bit and possibly explodes it right there. + * + ****************************************************************************/ + +static void p_check_missile_spawn(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_try_move(th, th->x, th->y)) p_explode_missile(th); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: p_set_mobj_state + * + * Description: + * Returns true if the mobj is still present. + * + ****************************************************************************/ + +boolean p_set_mobj_state(mobj_t *mobj, statenum_t state) +{ + state_t *st; + int cycle_counter = 0; + + do + { + if (state == S_NULL) + { + mobj->state = (state_t *)S_NULL; + p_remove_mobj(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; + + if (cycle_counter++ > MOBJ_CYCLE_LIMIT) + { + i_error("p_set_mobj_state: Infinite state cycle detected!"); + } + } + while (!mobj->tics); + + return true; +} + +/**************************************************************************** + * Name: p_mobj_thinker + ****************************************************************************/ + +void p_mobj_thinker(mobj_t *mobj) +{ + /* momentum movement */ + + if (mobj->momx || mobj->momy || (mobj->flags & MF_SKULLFLY)) + { + p_xy_movement(mobj); + + /* FIXME: decent NOP/NULL/Nil function pointer please. */ + + if (mobj->thinker.function.acv == (actionf_v)(-1)) + return; /* mobj was removed */ + } + + if ((mobj->z != mobj->floorz) || mobj->momz) + { + p_z_movement(mobj); + + /* FIXME: decent NOP/NULL/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_set_mobj_state(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 * TICRATE) return; + + if (leveltime & 31) return; + + if (p_random() > 4) return; + + p_nightmare_respawn(mobj); + } +} + +/**************************************************************************** + * Name: p_spawn_mobj + ****************************************************************************/ + +mobj_t *p_spawn_mobj(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, NULL); + 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_set_mobj_state, + * 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_set_thing_position(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_mobj_thinker; + + p_add_thinker(&mobj->thinker); + + return mobj; +} + +/**************************************************************************** + * Name: p_remove_mobj + ****************************************************************************/ + +void p_remove_mobj(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_unset_thing_position(mobj); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + /* stop any playing sound */ + + s_stop_sound(mobj); +#endif + + p_remove_thinker((thinker_t *)mobj); /* free block */ +} + +/**************************************************************************** + * Name: p_respawn_specials + ****************************************************************************/ + +void p_respawn_specials(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 * TICRATE) return; + + mthing = &itemrespawnque[iquetail]; + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + /* spawn a teleport fog at the new spot */ + + ss = r_point_in_subsector(x, y); + mo = p_spawn_mobj(x, y, ss->sector->floorheight, MT_IFOG); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_ITMBK); +#endif + + /* find which type to spawn */ + + for (i = 0; i < NUMMOBJTYPES; i++) + { + if (mthing->type == mobjinfo[i].doomednum) break; + } + + if (i >= NUMMOBJTYPES) + { + i_error("p_respawn_specials: Failed to find mobj type with doomednum " + "%d when respawning thing. This would cause a buffer overrun " + "in vanilla Doom", + mthing->type); + } + + /* spawn it */ + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mo = p_spawn_mobj(x, y, z, i); + mo->spawnpoint = *mthing; + mo->angle = ANG45 * (mthing->angle / 45); + + /* pull it from the que */ + + iquetail = (iquetail + 1) & (ITEMQUESIZE - 1); +} + +/**************************************************************************** + * Name: p_spawn_player + * + * Description: + * Called when a player is spawned on the level. + * Most of the player structure stays unchanged between levels. + * + ****************************************************************************/ + +void p_spawn_player(mapthing_t *mthing) +{ + player_t *p; + fixed_t x; + fixed_t y; + fixed_t z; + + mobj_t *mobj; + + int i; + + if (mthing->type == 0) + { + return; + } + + /* not playing? */ + + if (!playeringame[mthing->type - 1]) return; + + p = &players[mthing->type - 1]; + + if (p->playerstate == PST_REBORN) g_player_reborn(mthing->type - 1); + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + z = ONFLOORZ; + mobj = p_spawn_mobj(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 = NULL; + p->damagecount = 0; + p->bonuscount = 0; + p->extralight = 0; + p->fixedcolormap = 0; + p->viewheight = VIEWHEIGHT; + + p_setup_psprites(p); /* setup gun psprite */ + + /* give all cards in death match mode */ + + if (deathmatch) + { + for (i = 0; i < NUMCARDS; i++) + { + p->cards[i] = true; + } + } + + if (mthing->type - 1 == consoleplayer) + { + st_start(); /* wake up the status bar */ + hu_start(); /* wake up the heads up text */ + } +} + +/**************************************************************************** + * Name: p_spawn_map_thing + * + * Description: + * The fields of the mapthing should already be in host byte order. + * + ****************************************************************************/ + +void p_spawn_map_thing(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]) + { + memcpy(deathmatch_p, mthing, sizeof(*mthing)); + deathmatch_p++; + } + + return; + } + + if (mthing->type <= 0) + { + /* Thing type 0 is actually "player -1 start". + * For some reason, Vanilla Doom accepts/ignores this. + */ + + return; + } + + /* check for players specially */ + + if (mthing->type <= 4) + { + /* save spots for respawning in network games */ + + playerstarts[mthing->type - 1] = *mthing; + playerstartsingame[mthing->type - 1] = true; + if (!deathmatch) p_spawn_player(mthing); + + return; + } + + /* check for appropriate skill level */ + + if (!netgame && (mthing->options & 16)) return; + + if (gameskill == sk_baby) + { + bit = 1; + } + else if (gameskill == sk_nightmare) + { + bit = 4; + } + else + { + /* avoid undefined behavior (left shift by negative value and rhs too + * big) by accurately emulating what doom.exe did: reduce mod 32. For + * more details check: + * https://github.com/chocolate-doom/chocolate-doom/issues/1677 + */ + + bit = (int)(1U << ((gameskill - 1) & 0x1f)); + } + + 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("p_spawn_map_thing: Unknown type %i at (%i, %i)", mthing->type, + mthing->x, mthing->y); + + /* 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_spawn_mobj(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 */ + +/**************************************************************************** + * Name: p_spawn_puff + ****************************************************************************/ + +void p_spawn_puff(fixed_t x, fixed_t y, fixed_t z) +{ + mobj_t *th; + + z += (p_sub_random() << 10); + + th = p_spawn_mobj(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_set_mobj_state(th, S_PUFF3); +} + +/**************************************************************************** + * Name: p_spawn_blood + ****************************************************************************/ + +void p_spawn_blood(fixed_t x, fixed_t y, fixed_t z, int damage) +{ + mobj_t *th; + + z += (p_sub_random() << 10); + th = p_spawn_mobj(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_set_mobj_state(th, S_BLOOD2); + else if (damage < 9) + p_set_mobj_state(th, S_BLOOD3); +} + +/**************************************************************************** + * Name: p_subst_null_mobj + * + * Description: + * Certain functions assume that a mobj_t pointer is non-NULL, + * causing a crash in some situations where it is NULL. Vanilla + * Doom did not crash because of the lack of proper memory + * protection. This function substitutes NULL pointers for + * pointers to a dummy mobj, to avoid a crash. + * + ****************************************************************************/ + +mobj_t *p_subst_null_mobj(mobj_t *mobj) +{ + if (mobj == NULL) + { + static mobj_t dummy_mobj; + + dummy_mobj.x = 0; + dummy_mobj.y = 0; + dummy_mobj.z = 0; + dummy_mobj.flags = 0; + + mobj = &dummy_mobj; + } + + return mobj; +} + +/**************************************************************************** + * Name: p_spawn_missile + ****************************************************************************/ + +mobj_t *p_spawn_missile(mobj_t *source, mobj_t *dest, mobjtype_t type) +{ + mobj_t *th; + angle_t an; + int dist; + + th = p_spawn_mobj(source->x, source->y, + source->z + 4 * 8 * FRACUNIT, type); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (th->info->seesound) s_start_sound(th, th->info->seesound); +#endif + + th->target = source; /* where it came from */ + an = r_point_to_angle2(source->x, source->y, dest->x, dest->y); + + /* fuzzy player */ + + if (dest->flags & MF_SHADOW) an += p_sub_random() << 20; + + th->angle = an; + an >>= ANGLETOFINESHIFT; + th->momx = fixed_mul(th->info->speed, finecosine[an]); + th->momy = fixed_mul(th->info->speed, finesine[an]); + + dist = p_approx_distance(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_check_missile_spawn(th); + + return th; +} + +/**************************************************************************** + * Name: p_spawn_player_missile + * + * Description: + * Tries to aim at a nearby monster + * + ****************************************************************************/ + +void p_spawn_player_missile(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_aim_line_attack(source, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an += 1 << 26; + slope = p_aim_line_attack(source, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an -= 2 << 26; + slope = p_aim_line_attack(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_spawn_mobj(x, y, z, type); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (th->info->seesound) s_start_sound(th, th->info->seesound); +#endif + + th->target = source; + th->angle = an; + th->momx = fixed_mul(th->info->speed, finecosine[an >> ANGLETOFINESHIFT]); + th->momy = fixed_mul(th->info->speed, finesine[an >> ANGLETOFINESHIFT]); + th->momz = fixed_mul(th->info->speed, slope); + + p_check_missile_spawn(th); +} diff --git a/games/NXDoom/src/doom/p_mobj.h b/games/NXDoom/src/doom/p_mobj.h new file mode 100644 index 00000000000..6cd73f3a67b --- /dev/null +++ b/games/NXDoom/src/doom/p_mobj.h @@ -0,0 +1,357 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_mobj.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Map Objects, MObj, definition and handling. + * + ****************************************************************************/ + +#ifndef __P_MOBJ__ +#define __P_MOBJ__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Basics. */ + +#include "m_fixed.h" +#include "tables.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" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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 almost always 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_point_in_subsector(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 +{ + MF_SPECIAL = 1, /* Call P_SpecialThing when touched. */ + MF_SOLID = 2, /* Blocks. */ + MF_SHOOTABLE = 4, /* Can be hit. */ + + /* 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 NULL), + * 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 re-spawn. */ + + mapthing_t spawnpoint; + + /* Thing being chased/attacked for tracers. */ + + struct mobj_s *tracer; +} mobj_t; + +#endif /* __P_MOBJ__ */ diff --git a/games/NXDoom/src/doom/p_plats.c b/games/NXDoom/src/doom/p_plats.c new file mode 100644 index 00000000000..8cbbf70529e --- /dev/null +++ b/games/NXDoom/src/doom/p_plats.c @@ -0,0 +1,336 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_plats.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Plats (i.e. elevator platforms) code, raising/lowering. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "i_system.h" +#include "m_random.h" +#include "z_zone.h" + +#include "doomdef.h" +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +plat_t *activeplats[MAXPLATS]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Move a plat up and down */ + +void t_plat_raise(plat_t *plat) +{ + result_e res; + + switch (plat->status) + { + case up: + res = t_move_plane(plat->sector, plat->speed, + plat->high, plat->crush, 0, 1); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (plat->type == PLAT_RAISEANDCHANGE || + plat->type == PLAT_RAISETONEARESTANDCHANGE) + { + if (!(leveltime & 7)) + s_start_sound(&plat->sector->soundorg, SFX_STNMOV); + } +#endif + + if (res == crushed && (!plat->crush)) + { + plat->count = plat->wait; + plat->status = down; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&plat->sector->soundorg, SFX_PSTART); +#endif + } + else + { + if (res == pastdest) + { + plat->count = plat->wait; + plat->status = waiting; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&plat->sector->soundorg, SFX_PSTOP); +#endif + + switch (plat->type) + { + case PLAT_BLAZEDWUS: + case PLAT_DOWNWAITUPSTAY: + p_remove_active_plat(plat); + break; + + case PLAT_RAISEANDCHANGE: + case PLAT_RAISETONEARESTANDCHANGE: + p_remove_active_plat(plat); + break; + + default: + break; + } + } + } + break; + + case down: + res = t_move_plane(plat->sector, plat->speed, plat->low, false, 0, -1); + + if (res == pastdest) + { + plat->count = plat->wait; + plat->status = waiting; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&plat->sector->soundorg, SFX_PSTOP); +#endif + } + break; + + case waiting: + if (!--plat->count) + { + if (plat->sector->floorheight == plat->low) + plat->status = up; + else + plat->status = down; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&plat->sector->soundorg, SFX_PSTART); +#endif + } + + case in_stasis: + break; + } +} + +/* Do Platforms + * "amount" is only used for SOME platforms. + */ + +int ev_do_plat(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 PLAT_PERPETUALRAISE: + p_activate_in_stasis(line->tag); + break; + + default: + break; + } + + while ((secnum = p_find_sector_from_line_tag(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_add_thinker(&plat->thinker); + + plat->type = type; + plat->sector = sec; + plat->sector->specialdata = plat; + plat->thinker.function.acp1 = (actionf_p1)t_plat_raise; + plat->crush = false; + plat->tag = line->tag; + + switch (type) + { + case PLAT_RAISETONEARESTANDCHANGE: + plat->speed = PLATSPEED / 2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = p_find_next_highest_floor(sec, sec->floorheight); + plat->wait = 0; + plat->status = up; + + /* NO MORE DAMAGE, IF APPLICABLE */ + + sec->special = 0; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&sec->soundorg, SFX_STNMOV); +#endif + break; + + case PLAT_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; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&sec->soundorg, SFX_STNMOV); +#endif + break; + + case PLAT_DOWNWAITUPSTAY: + plat->speed = PLATSPEED * 4; + plat->low = p_find_lowest_floor_surrounding(sec); + + if (plat->low > sec->floorheight) plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = TICRATE * PLATWAIT; + plat->status = down; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&sec->soundorg, SFX_PSTART); +#endif + break; + + case PLAT_BLAZEDWUS: + plat->speed = PLATSPEED * 8; + plat->low = p_find_lowest_floor_surrounding(sec); + + if (plat->low > sec->floorheight) plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = TICRATE * PLATWAIT; + plat->status = down; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&sec->soundorg, SFX_PSTART); +#endif + break; + + case PLAT_PERPETUALRAISE: + plat->speed = PLATSPEED; + plat->low = p_find_lowest_floor_surrounding(sec); + + if (plat->low > sec->floorheight) plat->low = sec->floorheight; + + plat->high = p_find_highest_floor_surrounding(sec); + + if (plat->high < sec->floorheight) plat->high = sec->floorheight; + + plat->wait = TICRATE * PLATWAIT; + plat->status = p_random() & 1; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&sec->soundorg, SFX_PSTART); +#endif + break; + } + + p_add_active_plat(plat); + } + + return rtn; +} + +void p_activate_in_stasis(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_plat_raise; + } + } +} + +void ev_stop_plat(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)NULL; + } + } +} + +void p_add_active_plat(plat_t *plat) +{ + int i; + + for (i = 0; i < MAXPLATS; i++) + { + if (activeplats[i] == NULL) + { + activeplats[i] = plat; + return; + } + } + + i_error("p_add_active_plat: no more plats!"); +} + +void p_remove_active_plat(plat_t *plat) +{ + int i; + for (i = 0; i < MAXPLATS; i++) + { + if (plat == activeplats[i]) + { + (activeplats[i])->sector->specialdata = NULL; + p_remove_thinker(&(activeplats[i])->thinker); + activeplats[i] = NULL; + + return; + } + } + + i_error("p_remove_active_plat: can't find plat!"); +} diff --git a/games/NXDoom/src/doom/p_pspr.c b/games/NXDoom/src/doom/p_pspr.c new file mode 100644 index 00000000000..9800b0e7cad --- /dev/null +++ b/games/NXDoom/src/doom/p_pspr.c @@ -0,0 +1,786 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_pspr.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Weapon sprite animation, weapon objects. + * Action functions for weapons. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "doomdef.h" + +#include "deh_misc.h" + +#include "m_random.h" +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" + +/* Data. */ + +#include "p_pspr.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define LOWERSPEED FRACUNIT * 6 +#define RAISESPEED FRACUNIT * 6 + +#define WEAPONBOTTOM 128 * FRACUNIT +#define WEAPONTOP 32 * FRACUNIT + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +fixed_t swingx; +fixed_t swingy; + +fixed_t bulletslope; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void p_set_psprite(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 = NULL; + break; + } + + state = &states[stnum]; + psp->state = state; + psp->tics = state->tics; /* could be 0 */ + +#if 0 /* Unused as far as I can tell */ + if (state->misc1) + { + /* coordinate set */ + + psp->sx = state->misc1 << FRACBITS; + psp->sy = state->misc2 << FRACBITS; + } +#endif + + /* 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 */ +} + +static void p_calc_swing(player_t *player) +{ + fixed_t swing; + int angle; + + /* OPTIMIZE: tablify this. + * A LUT would allow for different modes, and add flexibility. + */ + + swing = player->bob; + + angle = (FINEANGLES / 70 * leveltime) & FINEMASK; + swingx = fixed_mul(swing, finesine[angle]); + + angle = (FINEANGLES / 70 * leveltime + FINEANGLES / 2) & FINEMASK; + swingy = -fixed_mul(swingx, finesine[angle]); +} + +/* p_bringup_weapon + * Starts bringing the pending weapon up from the bottom of the screen. Uses + * player + */ + +static void p_bringup_weapon(player_t *player) +{ + statenum_t newstate; + + if (player->pendingweapon == wp_nochange) + player->pendingweapon = player->readyweapon; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (player->pendingweapon == wp_chainsaw) + s_start_sound(player->mo, SFX_SAWUP); +#endif + + newstate = weaponinfo[player->pendingweapon].upstate; + + player->pendingweapon = wp_nochange; + player->psprites[ps_weapon].sy = WEAPONBOTTOM; + + p_set_psprite(player, ps_weapon, newstate); +} + +/* Doom does not check the bounds of the ammo array. As a result, + * it is possible to use an ammo type > 4 that overflows into the + * maxammo array and affects that instead. Through dehacked, for + * example, it is possible to make a weapon that decreases the max + * number of ammo for another weapon. Emulate this. + */ + +static void decrease_ammo(player_t *player, int ammonum, int amount) +{ + if (ammonum < NUMAMMO) + { + player->ammo[ammonum] -= amount; + } + else + { + player->maxammo[ammonum - NUMAMMO] -= amount; + } +} + +/* p_bullet_slope + * Sets a slope so a near miss is at approximately + * the height of the intended target + */ + +static void p_bullet_slope(mobj_t *mo) +{ + angle_t an; + + /* see which target is to be aimed at */ + + an = mo->angle; + bulletslope = p_aim_line_attack(mo, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an += 1 << 26; + bulletslope = p_aim_line_attack(mo, an, 16 * 64 * FRACUNIT); + if (!linetarget) + { + an -= 2 << 26; + bulletslope = p_aim_line_attack(mo, an, 16 * 64 * FRACUNIT); + } + } +} + +static void p_gunshot(mobj_t *mo, boolean accurate) +{ + angle_t angle; + int damage; + + damage = 5 * (p_random() % 3 + 1); + angle = mo->angle; + + if (!accurate) angle += p_sub_random() << 18; + + p_line_attack(mo, angle, MISSILERANGE, bulletslope, damage); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_check_ammo + * Returns true if there is enough ammo to shoot. + * If not, selects the next weapon to use. + */ + +boolean p_check_ammo(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 = deh_bfg_cells_per_shot; + 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_set_psprite(player, ps_weapon, + weaponinfo[player->readyweapon].downstate); + return false; +} + +static void p_fire_weapon(player_t *player) +{ + statenum_t newstate; + + if (!p_check_ammo(player)) return; + + p_set_mobj_state(player->mo, S_PLAY_ATK1); + newstate = weaponinfo[player->readyweapon].atkstate; + p_set_psprite(player, ps_weapon, newstate); + p_noise_alert(player->mo, player->mo); +} + +/* p_drop_weapon + * Player died, so put the weapon away. + */ + +void p_drop_weapon(player_t *player) +{ + p_set_psprite(player, ps_weapon, + weaponinfo[player->readyweapon].downstate); +} + +/* a_weapon_ready + * 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_weapon_ready(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_set_mobj_state(player->mo, S_PLAY); + } + + if (player->readyweapon == wp_chainsaw && psp->state == &states[S_SAW]) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_SAWIDL); +#endif + } + + /* check for change if player is dead, put the weapon away */ + + if (player->pendingweapon != wp_nochange || !player->health) + { + /* change weapon (pending weapon should already be validated) */ + + newstate = weaponinfo[player->readyweapon].downstate; + p_set_psprite(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_fire_weapon(player); + return; + } + } + else + player->attackdown = false; + + /* bob the weapon based on movement speed */ + + angle = (128 * leveltime) & FINEMASK; + psp->sx = FRACUNIT + fixed_mul(player->bob, finecosine[angle]); + angle &= FINEANGLES / 2 - 1; + psp->sy = WEAPONTOP + fixed_mul(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_fire_weapon(player); + } + else + { + player->refire = 0; + p_check_ammo(player); + } +} + +void a_check_reload(player_t *player, pspdef_t *psp) +{ + p_check_ammo(player); +#if 0 + if (player->ammo[am_shell] < 2) + { + p_set_psprite(player, ps_weapon, S_DSNR1); + } +#endif +} + +/* 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_set_psprite(player, ps_weapon, S_NULL); + return; + } + + player->readyweapon = player->pendingweapon; + + p_bringup_weapon(player); +} + +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_set_psprite(player, ps_weapon, newstate); +} + +void a_gun_flash(player_t *player, pspdef_t *psp) +{ + p_set_mobj_state(player->mo, S_PLAY_ATK2); + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate); +} + +/* WEAPON ATTACKS */ + +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_sub_random() << 18; + slope = p_aim_line_attack(player->mo, angle, MELEERANGE); + p_line_attack(player->mo, angle, MELEERANGE, slope, damage); + + /* turn to face target */ + + if (linetarget) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_PUNCH); +#endif + player->mo->angle = r_point_to_angle2(player->mo->x, player->mo->y, + linetarget->x, linetarget->y); + } +} + +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_sub_random() << 18; + + /* use meleerange + 1 se the puff doesn't skip the flash */ + + slope = p_aim_line_attack(player->mo, angle, MELEERANGE + 1); + p_line_attack(player->mo, angle, MELEERANGE + 1, slope, damage); + + if (!linetarget) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_SAWFUL); +#endif + return; + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_SAWHIT); +#endif + + /* turn to face target */ + + angle = r_point_to_angle2(player->mo->x, player->mo->y, linetarget->x, + linetarget->y); + if (angle - player->mo->angle > ANG180) + { + if ((signed int)(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; +} + +void a_fire_missile(player_t *player, pspdef_t *psp) +{ + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 1); + p_spawn_player_missile(player->mo, MT_ROCKET); +} + +void a_fire_bfg(player_t *player, pspdef_t *psp) +{ + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, + deh_bfg_cells_per_shot); + p_spawn_player_missile(player->mo, MT_BFG); +} + +void a_fire_plasma(player_t *player, pspdef_t *psp) +{ + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 1); + + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate + + (p_random() & 1)); + + p_spawn_player_missile(player->mo, MT_PLASMA); +} + +void a_fire_pistol(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_PISTOL); +#endif + + p_set_mobj_state(player->mo, S_PLAY_ATK2); + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 1); + + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate); + + p_bullet_slope(player->mo); + p_gunshot(player->mo, !player->refire); +} + +void a_fire_shotgun(player_t *player, pspdef_t *psp) +{ + int i; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_SHOTGN); +#endif + p_set_mobj_state(player->mo, S_PLAY_ATK2); + + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 1); + + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate); + + p_bullet_slope(player->mo); + + for (i = 0; i < 7; i++) + p_gunshot(player->mo, false); +} + +void a_fire_shotgun2(player_t *player, pspdef_t *psp) +{ + int i; + angle_t angle; + int damage; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_DSHTGN); +#endif + p_set_mobj_state(player->mo, S_PLAY_ATK2); + + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 2); + + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate); + + p_bullet_slope(player->mo); + + for (i = 0; i < 20; i++) + { + damage = 5 * (p_random() % 3 + 1); + angle = player->mo->angle; + angle += p_sub_random() << ANGLETOFINESHIFT; + p_line_attack(player->mo, angle, MISSILERANGE, + bulletslope + (p_sub_random() << 5), damage); + } +} + +void a_fire_cgun(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_PISTOL); +#endif + + if (!player->ammo[weaponinfo[player->readyweapon].ammo]) return; + + p_set_mobj_state(player->mo, S_PLAY_ATK2); + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 1); + + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate + psp->state - + &states[S_CHAIN1]); + + p_bullet_slope(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_bfg_spray + * Spawn a BFG explosion on every monster in view + */ + +void a_bfg_spray(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_aim_line_attack(mo->target, an, 16 * 64 * FRACUNIT); + + if (!linetarget) continue; + + p_spawn_mobj(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_damage_mobj(linetarget, mo->target, mo->target, damage); + } +} + +/* a_bfg_sound */ + +void a_bfg_sound(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_BFG); +#endif +} + +/* p_setup_psprites + * Called at start of level for each player. + */ + +void p_setup_psprites(player_t *player) +{ + int i; + + /* remove all psprites */ + + for (i = 0; i < NUMPSPRITES; i++) + player->psprites[i].state = NULL; + + /* spawn the gun */ + + player->pendingweapon = player->readyweapon; + p_bringup_weapon(player); +} + +/* p_move_psprites + * Called every tic by player thinking routine. + */ + +void p_move_psprites(player_t *player) +{ + int i; + pspdef_t *psp; + + psp = &player->psprites[0]; + for (i = 0; i < NUMPSPRITES; i++, psp++) + { + /* a null state means not active */ + + if (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_set_psprite(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/games/NXDoom/src/doom/p_pspr.h b/games/NXDoom/src/doom/p_pspr.h new file mode 100644 index 00000000000..83ab57f6a01 --- /dev/null +++ b/games/NXDoom/src/doom/p_pspr.h @@ -0,0 +1,88 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_pspr.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Sprite animation. + * + ****************************************************************************/ + +#ifndef __P_PSPR__ +#define __P_PSPR__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* 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 Attributes table and the Frame Sequence table. + */ + +#include "info.h" + +/* Frame flags: + * handles maximum brightness (torches, muzzle flare, light sources) + */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define FF_FULLBRIGHT 0x8000 /* flag in thing->frame */ +#define FF_FRAMEMASK 0x7fff + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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 NULL state means not active */ + int tics; + fixed_t sx; + fixed_t sy; +} pspdef_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern fixed_t bulletslope; + +#endif /* __P_PSPR__ */ diff --git a/games/NXDoom/src/doom/p_saveg.c b/games/NXDoom/src/doom/p_saveg.c new file mode 100644 index 00000000000..5c5439cb97b --- /dev/null +++ b/games/NXDoom/src/doom/p_saveg.c @@ -0,0 +1,2129 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_saveg.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Archiving: SaveGame I/O. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_main.h" +#include "dstrings.h" +#include "i_system.h" +#include "p_local.h" +#include "p_saveg.h" +#include "z_zone.h" + +/* State. */ + +#include "doomstat.h" +#include "g_game.h" +#include "m_misc.h" +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Enum values are 32-bit integers. */ + +#define saveg_read_enum saveg_read32 +#define saveg_write_enum saveg_write32 + +/* think_t + * This is just an actionf_t. + */ + +#define saveg_read_think_t saveg_read_actionf_t +#define saveg_write_think_t saveg_write_actionf_t + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum +{ + tc_ceiling, + tc_door, + tc_floor, + tc_plat, + tc_flash, + tc_strobe, + tc_glow, + tc_endspecials +} specials_e; + +/* Thinkers */ + +typedef enum +{ + tc_end, + tc_mobj +} thinkerclass_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +FILE *save_stream; +int savegamelength; +boolean savegame_error; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Endian-safe integer read/write functions */ + +static byte saveg_read8(void) +{ + byte result = -1; + + if (fread(&result, 1, 1, save_stream) < 1) + { + if (!savegame_error) + { + fprintf(stderr, "saveg_read8: Unexpected end of file while " + "reading save game\n"); + + savegame_error = true; + } + } + + return result; +} + +static void saveg_write8(byte value) +{ + if (fwrite(&value, 1, 1, save_stream) < 1) + { + if (!savegame_error) + { + fprintf(stderr, "saveg_write8: Error while writing save game\n"); + + savegame_error = true; + } + } +} + +static short saveg_read16(void) +{ + int result; + + result = saveg_read8(); + result |= saveg_read8() << 8; + + return result; +} + +static void saveg_write16(short value) +{ + saveg_write8(value & 0xff); + saveg_write8((value >> 8) & 0xff); +} + +static int saveg_read32(void) +{ + int result; + + result = saveg_read8(); + result |= saveg_read8() << 8; + result |= saveg_read8() << 16; + result |= saveg_read8() << 24; + + return result; +} + +static void saveg_write32(int value) +{ + saveg_write8(value & 0xff); + saveg_write8((value >> 8) & 0xff); + saveg_write8((value >> 16) & 0xff); + saveg_write8((value >> 24) & 0xff); +} + +/* Pad to 4-byte boundaries */ + +static void saveg_read_pad(void) +{ + unsigned long pos; + int padding; + int i; + + pos = ftell(save_stream); + + padding = (4 - (pos & 3)) & 3; + + for (i = 0; i < padding; ++i) + { + saveg_read8(); + } +} + +static void saveg_write_pad(void) +{ + unsigned long pos; + int padding; + int i; + + pos = ftell(save_stream); + + padding = (4 - (pos & 3)) & 3; + + for (i = 0; i < padding; ++i) + { + saveg_write8(0); + } +} + +/* Pointers */ + +static void *saveg_readp(void) +{ + return (void *)(intptr_t)saveg_read32(); +} + +static void saveg_writep(const void *p) +{ + saveg_write32((intptr_t)p); +} + +/* Structure read/write functions */ + +/* mapthing_t */ + +static void saveg_read_mapthing_t(mapthing_t *str) +{ + /* short x; */ + + str->x = saveg_read16(); + + /* short y; */ + + str->y = saveg_read16(); + + /* short angle; */ + + str->angle = saveg_read16(); + + /* short type; */ + + str->type = saveg_read16(); + + /* short options; */ + + str->options = saveg_read16(); +} + +static void saveg_write_mapthing_t(mapthing_t *str) +{ + /* short x; */ + + saveg_write16(str->x); + + /* short y; */ + + saveg_write16(str->y); + + /* short angle; */ + + saveg_write16(str->angle); + + /* short type; */ + + saveg_write16(str->type); + + /* short options; */ + + saveg_write16(str->options); +} + +/* actionf_t */ + +static void saveg_read_actionf_t(actionf_t *str) +{ + /* actionf_p1 acp1; */ + + str->acp1 = saveg_readp(); +} + +static void saveg_write_actionf_t(actionf_t *str) +{ + /* actionf_p1 acp1; */ + + saveg_writep(str->acp1); +} + +/* thinker_t */ + +static void saveg_read_thinker_t(thinker_t *str) +{ + /* struct thinker_s* prev; */ + + str->prev = saveg_readp(); + + /* struct thinker_s* next; */ + + str->next = saveg_readp(); + + /* think_t function; */ + + saveg_read_think_t(&str->function); +} + +static void saveg_write_thinker_t(thinker_t *str) +{ + /* struct thinker_s* prev; */ + + saveg_writep(str->prev); + + /* struct thinker_s* next; */ + + saveg_writep(str->next); + + /* think_t function; */ + + saveg_write_think_t(&str->function); +} + +/* mobj_t */ + +static void saveg_read_mobj_t(mobj_t *str) +{ + int pl; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* fixed_t x; */ + + str->x = saveg_read32(); + + /* fixed_t y; */ + + str->y = saveg_read32(); + + /* fixed_t z; */ + + str->z = saveg_read32(); + + /* struct mobj_s* snext; */ + + str->snext = saveg_readp(); + + /* struct mobj_s* sprev; */ + + str->sprev = saveg_readp(); + + /* angle_t angle; */ + + str->angle = saveg_read32(); + + /* spritenum_t sprite; */ + + str->sprite = saveg_read_enum(); + + /* int frame; */ + + str->frame = saveg_read32(); + + /* struct mobj_s* bnext; */ + + str->bnext = saveg_readp(); + + /* struct mobj_s* bprev; */ + + str->bprev = saveg_readp(); + + /* struct subsector_s* subsector; */ + + str->subsector = saveg_readp(); + + /* fixed_t floorz; */ + + str->floorz = saveg_read32(); + + /* fixed_t ceilingz; */ + + str->ceilingz = saveg_read32(); + + /* fixed_t radius; */ + + str->radius = saveg_read32(); + + /* fixed_t height; */ + + str->height = saveg_read32(); + + /* fixed_t momx; */ + + str->momx = saveg_read32(); + + /* fixed_t momy; */ + + str->momy = saveg_read32(); + + /* fixed_t momz; */ + + str->momz = saveg_read32(); + + /* int validcount; */ + + str->validcount = saveg_read32(); + + /* mobjtype_t type; */ + + str->type = saveg_read_enum(); + + /* mobjinfo_t* info; */ + + str->info = saveg_readp(); + + /* int tics; */ + + str->tics = saveg_read32(); + + /* state_t* state; */ + + str->state = &states[saveg_read32()]; + + /* int flags; */ + + str->flags = saveg_read32(); + + /* int health; */ + + str->health = saveg_read32(); + + /* int movedir; */ + + str->movedir = saveg_read32(); + + /* int movecount; */ + + str->movecount = saveg_read32(); + + /* struct mobj_s* target; */ + + str->target = saveg_readp(); + + /* int reactiontime; */ + + str->reactiontime = saveg_read32(); + + /* int threshold; */ + + str->threshold = saveg_read32(); + + /* struct player_s* player; */ + + pl = saveg_read32(); + + if (pl > 0) + { + str->player = &players[pl - 1]; + str->player->mo = str; + } + else + { + str->player = NULL; + } + + /* int lastlook; */ + + str->lastlook = saveg_read32(); + + /* mapthing_t spawnpoint; */ + + saveg_read_mapthing_t(&str->spawnpoint); + + /* struct mobj_s* tracer; */ + + str->tracer = saveg_readp(); +} + +static void saveg_write_mobj_t(mobj_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* fixed_t x; */ + + saveg_write32(str->x); + + /* fixed_t y; */ + + saveg_write32(str->y); + + /* fixed_t z; */ + + saveg_write32(str->z); + + /* struct mobj_s* snext; */ + + saveg_writep(str->snext); + + /* struct mobj_s* sprev; */ + + saveg_writep(str->sprev); + + /* angle_t angle; */ + + saveg_write32(str->angle); + + /* spritenum_t sprite; */ + + saveg_write_enum(str->sprite); + + /* int frame; */ + + saveg_write32(str->frame); + + /* struct mobj_s* bnext; */ + + saveg_writep(str->bnext); + + /* struct mobj_s* bprev; */ + + saveg_writep(str->bprev); + + /* struct subsector_s* subsector; */ + + saveg_writep(str->subsector); + + /* fixed_t floorz; */ + + saveg_write32(str->floorz); + + /* fixed_t ceilingz; */ + + saveg_write32(str->ceilingz); + + /* fixed_t radius; */ + + saveg_write32(str->radius); + + /* fixed_t height; */ + + saveg_write32(str->height); + + /* fixed_t momx; */ + + saveg_write32(str->momx); + + /* fixed_t momy; */ + + saveg_write32(str->momy); + + /* fixed_t momz; */ + + saveg_write32(str->momz); + + /* int validcount; */ + + saveg_write32(str->validcount); + + /* mobjtype_t type; */ + + saveg_write_enum(str->type); + + /* mobjinfo_t* info; */ + + saveg_writep(str->info); + + /* int tics; */ + + saveg_write32(str->tics); + + /* state_t* state; */ + + saveg_write32(str->state - states); + + /* int flags; */ + + saveg_write32(str->flags); + + /* int health; */ + + saveg_write32(str->health); + + /* int movedir; */ + + saveg_write32(str->movedir); + + /* int movecount; */ + + saveg_write32(str->movecount); + + /* struct mobj_s* target; */ + + saveg_writep(str->target); + + /* int reactiontime; */ + + saveg_write32(str->reactiontime); + + /* int threshold; */ + + saveg_write32(str->threshold); + + /* struct player_s* player; */ + + if (str->player) + { + saveg_write32(str->player - players + 1); + } + else + { + saveg_write32(0); + } + + /* int lastlook; */ + + saveg_write32(str->lastlook); + + /* mapthing_t spawnpoint; */ + + saveg_write_mapthing_t(&str->spawnpoint); + + /* struct mobj_s* tracer; */ + + saveg_writep(str->tracer); +} + +/* ticcmd_t */ + +static void saveg_read_ticcmd_t(ticcmd_t *str) +{ + /* signed char forwardmove; */ + + str->forwardmove = saveg_read8(); + + /* signed char sidemove; */ + + str->sidemove = saveg_read8(); + + /* short angleturn; */ + + str->angleturn = saveg_read16(); + + /* short consistency; */ + + str->consistency = saveg_read16(); + + /* byte chatchar; */ + + str->chatchar = saveg_read8(); + + /* byte buttons; */ + + str->buttons = saveg_read8(); +} + +static void saveg_write_ticcmd_t(ticcmd_t *str) +{ + /* signed char forwardmove; */ + + saveg_write8(str->forwardmove); + + /* signed char sidemove; */ + + saveg_write8(str->sidemove); + + /* short angleturn; */ + + saveg_write16(str->angleturn); + + /* short consistency; */ + + saveg_write16(str->consistency); + + /* byte chatchar; */ + + saveg_write8(str->chatchar); + + /* byte buttons; */ + + saveg_write8(str->buttons); +} + +/* pspdef_t */ + +static void saveg_read_pspdef_t(pspdef_t *str) +{ + int state; + + /* state_t* state; */ + + state = saveg_read32(); + + if (state > 0) + { + str->state = &states[state]; + } + else + { + str->state = NULL; + } + + /* int tics; */ + + str->tics = saveg_read32(); + + /* fixed_t sx; */ + + str->sx = saveg_read32(); + + /* fixed_t sy; */ + + str->sy = saveg_read32(); +} + +static void saveg_write_pspdef_t(pspdef_t *str) +{ + /* state_t* state; */ + + if (str->state) + { + saveg_write32(str->state - states); + } + else + { + saveg_write32(0); + } + + /* int tics; */ + + saveg_write32(str->tics); + + /* fixed_t sx; */ + + saveg_write32(str->sx); + + /* fixed_t sy; */ + + saveg_write32(str->sy); +} + +/* player_t */ + +static void saveg_read_player_t(player_t *str) +{ + int i; + + /* mobj_t* mo; */ + + str->mo = saveg_readp(); + + /* playerstate_t playerstate; */ + + str->playerstate = saveg_read_enum(); + + /* ticcmd_t cmd; */ + + saveg_read_ticcmd_t(&str->cmd); + + /* fixed_t viewz; */ + + str->viewz = saveg_read32(); + + /* fixed_t viewheight; */ + + str->viewheight = saveg_read32(); + + /* fixed_t deltaviewheight; */ + + str->deltaviewheight = saveg_read32(); + + /* fixed_t bob; */ + + str->bob = saveg_read32(); + + /* int health; */ + + str->health = saveg_read32(); + + /* int armorpoints; */ + + str->armorpoints = saveg_read32(); + + /* int armortype; */ + + str->armortype = saveg_read32(); + + /* int powers[NUMPOWERS]; */ + + for (i = 0; i < NUMPOWERS; ++i) + { + str->powers[i] = saveg_read32(); + } + + /* boolean cards[NUMCARDS]; */ + + for (i = 0; i < NUMCARDS; ++i) + { + str->cards[i] = saveg_read32(); + } + + /* boolean backpack; */ + + str->backpack = saveg_read32(); + + /* int frags[MAXPLAYERS]; */ + + for (i = 0; i < MAXPLAYERS; ++i) + { + str->frags[i] = saveg_read32(); + } + + /* weapontype_t readyweapon; */ + + str->readyweapon = saveg_read_enum(); + + /* weapontype_t pendingweapon; */ + + str->pendingweapon = saveg_read_enum(); + + /* boolean weaponowned[NUMWEAPONS]; */ + + for (i = 0; i < NUMWEAPONS; ++i) + { + str->weaponowned[i] = saveg_read32(); + } + + /* int ammo[NUMAMMO]; */ + + for (i = 0; i < NUMAMMO; ++i) + { + str->ammo[i] = saveg_read32(); + } + + /* int maxammo[NUMAMMO]; */ + + for (i = 0; i < NUMAMMO; ++i) + { + str->maxammo[i] = saveg_read32(); + } + + /* int attackdown; */ + + str->attackdown = saveg_read32(); + + /* int usedown; */ + + str->usedown = saveg_read32(); + + /* int cheats; */ + + str->cheats = saveg_read32(); + + /* int refire; */ + + str->refire = saveg_read32(); + + /* int killcount; */ + + str->killcount = saveg_read32(); + + /* int itemcount; */ + + str->itemcount = saveg_read32(); + + /* int secretcount; */ + + str->secretcount = saveg_read32(); + + /* char* message; */ + + str->message = saveg_readp(); + + /* int damagecount; */ + + str->damagecount = saveg_read32(); + + /* int bonuscount; */ + + str->bonuscount = saveg_read32(); + + /* mobj_t* attacker; */ + + str->attacker = saveg_readp(); + + /* int extralight; */ + + str->extralight = saveg_read32(); + + /* int fixedcolormap; */ + + str->fixedcolormap = saveg_read32(); + + /* int colormap; */ + + str->colormap = saveg_read32(); + + /* pspdef_t psprites[NUMPSPRITES]; */ + + for (i = 0; i < NUMPSPRITES; ++i) + { + saveg_read_pspdef_t(&str->psprites[i]); + } + + /* boolean didsecret; */ + + str->didsecret = saveg_read32(); +} + +static void saveg_write_player_t(player_t *str) +{ + int i; + + /* mobj_t* mo; */ + + saveg_writep(str->mo); + + /* playerstate_t playerstate; */ + + saveg_write_enum(str->playerstate); + + /* ticcmd_t cmd; */ + + saveg_write_ticcmd_t(&str->cmd); + + /* fixed_t viewz; */ + + saveg_write32(str->viewz); + + /* fixed_t viewheight; */ + + saveg_write32(str->viewheight); + + /* fixed_t deltaviewheight; */ + + saveg_write32(str->deltaviewheight); + + /* fixed_t bob; */ + + saveg_write32(str->bob); + + /* int health; */ + + saveg_write32(str->health); + + /* int armorpoints; */ + + saveg_write32(str->armorpoints); + + /* int armortype; */ + + saveg_write32(str->armortype); + + /* int powers[NUMPOWERS]; */ + + for (i = 0; i < NUMPOWERS; ++i) + { + saveg_write32(str->powers[i]); + } + + /* boolean cards[NUMCARDS]; */ + + for (i = 0; i < NUMCARDS; ++i) + { + saveg_write32(str->cards[i]); + } + + /* boolean backpack; */ + + saveg_write32(str->backpack); + + /* int frags[MAXPLAYERS]; */ + + for (i = 0; i < MAXPLAYERS; ++i) + { + saveg_write32(str->frags[i]); + } + + /* weapontype_t readyweapon; */ + + saveg_write_enum(str->readyweapon); + + /* weapontype_t pendingweapon; */ + + saveg_write_enum(str->pendingweapon); + + /* boolean weaponowned[NUMWEAPONS]; */ + + for (i = 0; i < NUMWEAPONS; ++i) + { + saveg_write32(str->weaponowned[i]); + } + + /* int ammo[NUMAMMO]; */ + + for (i = 0; i < NUMAMMO; ++i) + { + saveg_write32(str->ammo[i]); + } + + /* int maxammo[NUMAMMO]; */ + + for (i = 0; i < NUMAMMO; ++i) + { + saveg_write32(str->maxammo[i]); + } + + /* int attackdown; */ + + saveg_write32(str->attackdown); + + /* int usedown; */ + + saveg_write32(str->usedown); + + /* int cheats; */ + + saveg_write32(str->cheats); + + /* int refire; */ + + saveg_write32(str->refire); + + /* int killcount; */ + + saveg_write32(str->killcount); + + /* int itemcount; */ + + saveg_write32(str->itemcount); + + /* int secretcount; */ + + saveg_write32(str->secretcount); + + /* char* message; */ + + saveg_writep(str->message); + + /* int damagecount; */ + + saveg_write32(str->damagecount); + + /* int bonuscount; */ + + saveg_write32(str->bonuscount); + + /* mobj_t* attacker; */ + + saveg_writep(str->attacker); + + /* int extralight; */ + + saveg_write32(str->extralight); + + /* int fixedcolormap; */ + + saveg_write32(str->fixedcolormap); + + /* int colormap; */ + + saveg_write32(str->colormap); + + /* pspdef_t psprites[NUMPSPRITES]; */ + + for (i = 0; i < NUMPSPRITES; ++i) + { + saveg_write_pspdef_t(&str->psprites[i]); + } + + /* boolean didsecret; */ + + saveg_write32(str->didsecret); +} + +/* ceiling_t */ + +static void saveg_read_ceiling_t(ceiling_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* ceiling_e type; */ + + str->type = saveg_read_enum(); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* fixed_t bottomheight; */ + + str->bottomheight = saveg_read32(); + + /* fixed_t topheight; */ + + str->topheight = saveg_read32(); + + /* fixed_t speed; */ + + str->speed = saveg_read32(); + + /* boolean crush; */ + + str->crush = saveg_read32(); + + /* int direction; */ + + str->direction = saveg_read32(); + + /* int tag; */ + + str->tag = saveg_read32(); + + /* int olddirection; */ + + str->olddirection = saveg_read32(); +} + +static void saveg_write_ceiling_t(ceiling_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* ceiling_e type; */ + + saveg_write_enum(str->type); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* fixed_t bottomheight; */ + + saveg_write32(str->bottomheight); + + /* fixed_t topheight; */ + + saveg_write32(str->topheight); + + /* fixed_t speed; */ + + saveg_write32(str->speed); + + /* boolean crush; */ + + saveg_write32(str->crush); + + /* int direction; */ + + saveg_write32(str->direction); + + /* int tag; */ + + saveg_write32(str->tag); + + /* int olddirection; */ + + saveg_write32(str->olddirection); +} + +/* vldoor_t */ + +static void saveg_read_vldoor_t(vldoor_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* vldoor_e type; */ + + str->type = saveg_read_enum(); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* fixed_t topheight; */ + + str->topheight = saveg_read32(); + + /* fixed_t speed; */ + + str->speed = saveg_read32(); + + /* int direction; */ + + str->direction = saveg_read32(); + + /* int topwait; */ + + str->topwait = saveg_read32(); + + /* int topcountdown; */ + + str->topcountdown = saveg_read32(); +} + +static void saveg_write_vldoor_t(vldoor_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* vldoor_e type; */ + + saveg_write_enum(str->type); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* fixed_t topheight; */ + + saveg_write32(str->topheight); + + /* fixed_t speed; */ + + saveg_write32(str->speed); + + /* int direction; */ + + saveg_write32(str->direction); + + /* int topwait; */ + + saveg_write32(str->topwait); + + /* int topcountdown; */ + + saveg_write32(str->topcountdown); +} + +/* floormove_t */ + +static void saveg_read_floormove_t(floormove_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* floor_e type; */ + + str->type = saveg_read_enum(); + + /* boolean crush; */ + + str->crush = saveg_read32(); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* int direction; */ + + str->direction = saveg_read32(); + + /* int newspecial; */ + + str->newspecial = saveg_read32(); + + /* short texture; */ + + str->texture = saveg_read16(); + + /* fixed_t floordestheight; */ + + str->floordestheight = saveg_read32(); + + /* fixed_t speed; */ + + str->speed = saveg_read32(); +} + +static void saveg_write_floormove_t(floormove_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* floor_e type; */ + + saveg_write_enum(str->type); + + /* boolean crush; */ + + saveg_write32(str->crush); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* int direction; */ + + saveg_write32(str->direction); + + /* int newspecial; */ + + saveg_write32(str->newspecial); + + /* short texture; */ + + saveg_write16(str->texture); + + /* fixed_t floordestheight; */ + + saveg_write32(str->floordestheight); + + /* fixed_t speed; */ + + saveg_write32(str->speed); +} + +/* plat_t */ + +static void saveg_read_plat_t(plat_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* fixed_t speed; */ + + str->speed = saveg_read32(); + + /* fixed_t low; */ + + str->low = saveg_read32(); + + /* fixed_t high; */ + + str->high = saveg_read32(); + + /* int wait; */ + + str->wait = saveg_read32(); + + /* int count; */ + + str->count = saveg_read32(); + + /* plat_e status; */ + + str->status = saveg_read_enum(); + + /* plat_e oldstatus; */ + + str->oldstatus = saveg_read_enum(); + + /* boolean crush; */ + + str->crush = saveg_read32(); + + /* int tag; */ + + str->tag = saveg_read32(); + + /* plattype_e type; */ + + str->type = saveg_read_enum(); +} + +static void saveg_write_plat_t(plat_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* fixed_t speed; */ + + saveg_write32(str->speed); + + /* fixed_t low; */ + + saveg_write32(str->low); + + /* fixed_t high; */ + + saveg_write32(str->high); + + /* int wait; */ + + saveg_write32(str->wait); + + /* int count; */ + + saveg_write32(str->count); + + /* plat_e status; */ + + saveg_write_enum(str->status); + + /* plat_e oldstatus; */ + + saveg_write_enum(str->oldstatus); + + /* boolean crush; */ + + saveg_write32(str->crush); + + /* int tag; */ + + saveg_write32(str->tag); + + /* plattype_e type; */ + + saveg_write_enum(str->type); +} + +/* lightflash_t */ + +static void saveg_read_lightflash_t(lightflash_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* int count; */ + + str->count = saveg_read32(); + + /* int maxlight; */ + + str->maxlight = saveg_read32(); + + /* int minlight; */ + + str->minlight = saveg_read32(); + + /* int maxtime; */ + + str->maxtime = saveg_read32(); + + /* int mintime; */ + + str->mintime = saveg_read32(); +} + +static void saveg_write_lightflash_t(lightflash_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* int count; */ + + saveg_write32(str->count); + + /* int maxlight; */ + + saveg_write32(str->maxlight); + + /* int minlight; */ + + saveg_write32(str->minlight); + + /* int maxtime; */ + + saveg_write32(str->maxtime); + + /* int mintime; */ + + saveg_write32(str->mintime); +} + +/* strobe_t */ + +static void saveg_read_strobe_t(strobe_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* int count; */ + + str->count = saveg_read32(); + + /* int minlight; */ + + str->minlight = saveg_read32(); + + /* int maxlight; */ + + str->maxlight = saveg_read32(); + + /* int darktime; */ + + str->darktime = saveg_read32(); + + /* int brighttime; */ + + str->brighttime = saveg_read32(); +} + +static void saveg_write_strobe_t(strobe_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* int count; */ + + saveg_write32(str->count); + + /* int minlight; */ + + saveg_write32(str->minlight); + + /* int maxlight; */ + + saveg_write32(str->maxlight); + + /* int darktime; */ + + saveg_write32(str->darktime); + + /* int brighttime; */ + + saveg_write32(str->brighttime); +} + +/* glow_t */ + +static void saveg_read_glow_t(glow_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* int minlight; */ + + str->minlight = saveg_read32(); + + /* int maxlight; */ + + str->maxlight = saveg_read32(); + + /* int direction; */ + + str->direction = saveg_read32(); +} + +static void saveg_write_glow_t(glow_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* int minlight; */ + + saveg_write32(str->minlight); + + /* int maxlight; */ + + saveg_write32(str->maxlight); + + /* int direction; */ + + saveg_write32(str->direction); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Write the header for a savegame */ + +void p_write_save_game_header(char *description) +{ + char name[VERSIONSIZE]; + int i; + + for (i = 0; description[i] != '\0'; ++i) + saveg_write8(description[i]); + for (; i < SAVESTRINGSIZE; ++i) + saveg_write8(0); + + memset(name, 0, sizeof(name)); + snprintf(name, sizeof(name), "version %i", g_vanilla_version_code()); + + for (i = 0; i < VERSIONSIZE; ++i) + saveg_write8(name[i]); + + saveg_write8(gameskill); + saveg_write8(gameepisode); + saveg_write8(gamemap); + + for (i = 0; i < MAXPLAYERS; i++) + saveg_write8(playeringame[i]); + + saveg_write8((leveltime >> 16) & 0xff); + saveg_write8((leveltime >> 8) & 0xff); + saveg_write8(leveltime & 0xff); +} + +/* Get the filename of a temporary file to write the savegame to. After + * the file has been successfully saved, it will be renamed to the + * real file. + */ + +char *p_temp_save_game_file(void) +{ + static char *filename = NULL; + + if (filename == NULL) + { + filename = m_string_join(savegamedir, "temp.dsg", NULL); + } + + return filename; +} + +/* Get the filename of the save game file to use for the specified slot. */ + +char *p_save_game_file(int slot) +{ + static char *filename = NULL; + static size_t filename_size = 0; + char basename[32]; + + if (filename == NULL) + { + filename_size = strlen(savegamedir) + 32; + filename = malloc(filename_size); + } + + snprintf(basename, 32, SAVEGAMENAME "%d.dsg", slot); + snprintf(filename, filename_size, "%s%s", savegamedir, basename); + + return filename; +} + +/* Read the header for a savegame */ + +boolean p_read_save_game_header(void) +{ + int i; + byte a, b, c; + char vcheck[VERSIONSIZE]; + char read_vcheck[VERSIONSIZE]; + + /* skip the description field */ + + for (i = 0; i < SAVESTRINGSIZE; ++i) + saveg_read8(); + + for (i = 0; i < VERSIONSIZE; ++i) + read_vcheck[i] = saveg_read8(); + + memset(vcheck, 0, sizeof(vcheck)); + snprintf(vcheck, sizeof(vcheck), "version %i", g_vanilla_version_code()); + if (strcmp(read_vcheck, vcheck) != 0) return false; /* bad version */ + + gameskill = saveg_read8(); + gameepisode = saveg_read8(); + gamemap = saveg_read8(); + + for (i = 0; i < MAXPLAYERS; i++) + playeringame[i] = saveg_read8(); + + /* get the times */ + + a = saveg_read8(); + b = saveg_read8(); + c = saveg_read8(); + leveltime = (a << 16) + (b << 8) + c; + + return true; +} + +/* Read the end of file marker. Returns true if read successfully. */ + +boolean p_read_save_game_eof(void) +{ + int value; + + value = saveg_read8(); + + return value == SAVEGAME_EOF; +} + +/* Write the end of file marker */ + +void p_write_save_game_eof(void) +{ + saveg_write8(SAVEGAME_EOF); +} + +void p_archive_players(void) +{ + int i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + saveg_write_pad(); + + saveg_write_player_t(&players[i]); + } +} + +void p_unarchive_players(void) +{ + int i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + saveg_read_pad(); + + saveg_read_player_t(&players[i]); + + /* will be set when unarc thinker */ + + players[i].mo = NULL; + players[i].message = NULL; + players[i].attacker = NULL; + } +} + +void p_archive_world(void) +{ + int i; + int j; + sector_t *sec; + line_t *li; + side_t *si; + + /* do sectors */ + + for (i = 0, sec = sectors; i < numsectors; i++, sec++) + { + saveg_write16(sec->floorheight >> FRACBITS); + saveg_write16(sec->ceilingheight >> FRACBITS); + saveg_write16(sec->floorpic); + saveg_write16(sec->ceilingpic); + saveg_write16(sec->lightlevel); + saveg_write16(sec->special); /* needed? */ + + saveg_write16(sec->tag); /* needed? */ + } + + /* do lines */ + + for (i = 0, li = lines; i < numlines; i++, li++) + { + saveg_write16(li->flags); + saveg_write16(li->special); + saveg_write16(li->tag); + for (j = 0; j < 2; j++) + { + if (li->sidenum[j] == -1) continue; + + si = &sides[li->sidenum[j]]; + + saveg_write16(si->textureoffset >> FRACBITS); + saveg_write16(si->rowoffset >> FRACBITS); + saveg_write16(si->toptexture); + saveg_write16(si->bottomtexture); + saveg_write16(si->midtexture); + } + } +} + +void p_unarchive_world(void) +{ + int i; + int j; + sector_t *sec; + line_t *li; + side_t *si; + + /* do sectors */ + + for (i = 0, sec = sectors; i < numsectors; i++, sec++) + { + sec->floorheight = saveg_read16() << FRACBITS; + sec->ceilingheight = saveg_read16() << FRACBITS; + sec->floorpic = saveg_read16(); + sec->ceilingpic = saveg_read16(); + sec->lightlevel = saveg_read16(); + sec->special = saveg_read16(); /* needed? */ + + sec->tag = saveg_read16(); /* needed? */ + + sec->specialdata = 0; + sec->soundtarget = 0; + } + + /* do lines */ + + for (i = 0, li = lines; i < numlines; i++, li++) + { + li->flags = saveg_read16(); + li->special = saveg_read16(); + li->tag = saveg_read16(); + for (j = 0; j < 2; j++) + { + if (li->sidenum[j] == -1) continue; + si = &sides[li->sidenum[j]]; + si->textureoffset = saveg_read16() << FRACBITS; + si->rowoffset = saveg_read16() << FRACBITS; + si->toptexture = saveg_read16(); + si->bottomtexture = saveg_read16(); + si->midtexture = saveg_read16(); + } + } +} + +void p_archive_thinkers(void) +{ + thinker_t *th; + + /* save off the current thinkers */ + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 == (actionf_p1)p_mobj_thinker) + { + saveg_write8(tc_mobj); + saveg_write_pad(); + saveg_write_mobj_t((mobj_t *)th); + + continue; + } + } + + /* add a terminating marker */ + + saveg_write8(tc_end); +} + +void p_unarchive_thinkers(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_mobj_thinker) + p_remove_mobj((mobj_t *)currentthinker); + else + z_free(currentthinker); + + currentthinker = next; + } + + p_init_thinkers(); + + /* read in saved thinkers */ + + while (1) + { + tclass = saveg_read8(); + switch (tclass) + { + case tc_end: + return; /* end of list */ + + case tc_mobj: + saveg_read_pad(); + mobj = z_malloc(sizeof(*mobj), PU_LEVEL, NULL); + saveg_read_mobj_t(mobj); + + mobj->target = NULL; + mobj->tracer = NULL; + p_set_thing_position(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_mobj_thinker; + p_add_thinker(&mobj->thinker); + break; + + default: + i_error("Unknown tclass %i in savegame", tclass); + } + } +} + +/* Things to handle: + * + * t_move_ceiling, (ceiling_t: sector_t * swizzle), - active list + * t_vertical_door, (vldoor_t: sector_t * swizzle), + * t_move_floor, (floormove_t: sector_t * swizzle), + * t_light_flash, (lightflash_t: sector_t * swizzle), + * t_strobe_flash, (strobe_t: sector_t *), + * t_glow, (glow_t: sector_t *), + * t_plat_raise, (plat_t: sector_t *), - active list + */ + +void p_archive_specials(void) +{ + thinker_t *th; + int i; + + /* save off the current thinkers */ + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acv == (actionf_v)NULL) + { + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] == (ceiling_t *)th) break; + } + + if (i < MAXCEILINGS) + { + saveg_write8(tc_ceiling); + saveg_write_pad(); + saveg_write_ceiling_t((ceiling_t *)th); + } + + continue; + } + + if (th->function.acp1 == (actionf_p1)t_move_ceiling) + { + saveg_write8(tc_ceiling); + saveg_write_pad(); + saveg_write_ceiling_t((ceiling_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_vertical_door) + { + saveg_write8(tc_door); + saveg_write_pad(); + saveg_write_vldoor_t((vldoor_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_move_floor) + { + saveg_write8(tc_floor); + saveg_write_pad(); + saveg_write_floormove_t((floormove_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_plat_raise) + { + saveg_write8(tc_plat); + saveg_write_pad(); + saveg_write_plat_t((plat_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_light_flash) + { + saveg_write8(tc_flash); + saveg_write_pad(); + saveg_write_lightflash_t((lightflash_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_strobe_flash) + { + saveg_write8(tc_strobe); + saveg_write_pad(); + saveg_write_strobe_t((strobe_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_glow) + { + saveg_write8(tc_glow); + saveg_write_pad(); + saveg_write_glow_t((glow_t *)th); + continue; + } + } + + /* add a terminating marker */ + + saveg_write8(tc_endspecials); +} + +void p_unarchive_specials(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 = saveg_read8(); + + switch (tclass) + { + case tc_endspecials: + return; /* end of list */ + + case tc_ceiling: + saveg_read_pad(); + ceiling = z_malloc(sizeof(*ceiling), PU_LEVEL, NULL); + saveg_read_ceiling_t(ceiling); + ceiling->sector->specialdata = ceiling; + + if (ceiling->thinker.function.acp1) + ceiling->thinker.function.acp1 = (actionf_p1)t_move_ceiling; + + p_add_thinker(&ceiling->thinker); + p_add_active_ceiling(ceiling); + break; + + case tc_door: + saveg_read_pad(); + door = z_malloc(sizeof(*door), PU_LEVEL, NULL); + saveg_read_vldoor_t(door); + door->sector->specialdata = door; + door->thinker.function.acp1 = (actionf_p1)t_vertical_door; + p_add_thinker(&door->thinker); + break; + + case tc_floor: + saveg_read_pad(); + floor = z_malloc(sizeof(*floor), PU_LEVEL, NULL); + saveg_read_floormove_t(floor); + floor->sector->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + p_add_thinker(&floor->thinker); + break; + + case tc_plat: + saveg_read_pad(); + plat = z_malloc(sizeof(*plat), PU_LEVEL, NULL); + saveg_read_plat_t(plat); + plat->sector->specialdata = plat; + + if (plat->thinker.function.acp1) + plat->thinker.function.acp1 = (actionf_p1)t_plat_raise; + + p_add_thinker(&plat->thinker); + p_add_active_plat(plat); + break; + + case tc_flash: + saveg_read_pad(); + flash = z_malloc(sizeof(*flash), PU_LEVEL, NULL); + saveg_read_lightflash_t(flash); + flash->thinker.function.acp1 = (actionf_p1)t_light_flash; + p_add_thinker(&flash->thinker); + break; + + case tc_strobe: + saveg_read_pad(); + strobe = z_malloc(sizeof(*strobe), PU_LEVEL, NULL); + saveg_read_strobe_t(strobe); + strobe->thinker.function.acp1 = (actionf_p1)t_strobe_flash; + p_add_thinker(&strobe->thinker); + break; + + case tc_glow: + saveg_read_pad(); + glow = z_malloc(sizeof(*glow), PU_LEVEL, NULL); + saveg_read_glow_t(glow); + glow->thinker.function.acp1 = (actionf_p1)t_glow; + p_add_thinker(&glow->thinker); + break; + + default: + i_error("P_UnarchiveSpecials:Unknown tclass %i " + "in savegame", + tclass); + } + } +} diff --git a/games/NXDoom/src/doom/p_saveg.h b/games/NXDoom/src/doom/p_saveg.h new file mode 100644 index 00000000000..8c27b6dc745 --- /dev/null +++ b/games/NXDoom/src/doom/p_saveg.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_saveg.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Savegame I/O, archiving, persistence. + * + ****************************************************************************/ + +#ifndef __P_SAVEG__ +#define __P_SAVEG__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SAVEGAME_EOF 0x1d +#define VERSIONSIZE 16 + +/* maximum size of a savegame description */ + +#define SAVESTRINGSIZE 24 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern FILE *save_stream; +extern boolean savegame_error; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* temporary filename to use while saving. */ + +char *p_temp_save_game_file(void); + +/* filename to use for a savegame slot */ + +char *p_save_game_file(int slot); + +/* Savegame file header read/write functions */ + +boolean p_read_save_game_header(void); +void p_write_save_game_header(char *description); + +/* Savegame end-of-file read/write functions */ + +boolean p_read_save_game_eof(void); +void p_write_save_game_eof(void); + +/* Persistent storage/archiving. + * These are the load / save game routines. + */ + +void p_archive_players(void); +void p_unarchive_players(void); +void p_archive_world(void); +void p_unarchive_world(void); +void p_archive_thinkers(void); +void p_unarchive_thinkers(void); +void p_archive_specials(void); +void p_unarchive_specials(void); + +#endif /* __P_SAVEG__ */ diff --git a/games/NXDoom/src/doom/p_setup.c b/games/NXDoom/src/doom/p_setup.c new file mode 100644 index 00000000000..3dc608abf23 --- /dev/null +++ b/games/NXDoom/src/doom/p_setup.c @@ -0,0 +1,834 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_setup.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Do all the WAD I/O, get map description, set up initial state and misc. + * LUTs. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "z_zone.h" + +#include "deh_main.h" +#include "i_swap.h" +#include "m_argv.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 "p_rejectpad.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#endif + +#include "doomstat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Maintain single and multi player starting spots. */ + +#define MAX_DEATHMATCH_STARTS 10 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void p_spawn_map_thing(mapthing_t *mthing); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int totallines; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* MAP related Lookup tables. + * Store VERTICES, LINEDEFS, SIDEDEFS, etc. + */ + +int numvertices; +vertex_t *vertices; + +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]; +boolean playerstartsingame[MAXPLAYERS]; + +/* pointer to the current map lump info struct */ + +lumpinfo_t *maplumpinfo; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void p_load_vertices(int lump) +{ + byte *data; + int i; + mapvertex_t *ml; + vertex_t *li; + + /* Determine number of lumps: total lump length / vertex record length. */ + + numvertices = w_lump_length(lump) / sizeof(mapvertex_t); + + /* Allocate zone memory for buffer. */ + + vertices = z_malloc(numvertices * sizeof(vertex_t), PU_LEVEL, 0); + + /* Load data into cache. */ + + data = w_cache_lump_num(lump, PU_STATIC); + + ml = (mapvertex_t *)data; + li = vertices; + + /* Copy and convert vertex coordinates, internal representation as fixed. */ + + for (i = 0; i < numvertices; i++, li++, ml++) + { + li->x = SHORT(ml->x) << FRACBITS; + li->y = SHORT(ml->y) << FRACBITS; + } + + /* Free buffer memory. */ + + w_release_lump_num(lump); +} + +static sector_t *get_sector_at_null_address(void) +{ + static boolean null_sector_is_initialized = false; + static sector_t null_sector; + + if (!null_sector_is_initialized) + { + memset(&null_sector, 0, sizeof(null_sector)); + i_get_memory_value(0, &null_sector.floorheight, 4); + i_get_memory_value(4, &null_sector.ceilingheight, 4); + null_sector_is_initialized = true; + } + + return &null_sector; +} + +static void p_load_segs(int lump) +{ + byte *data; + int i; + mapseg_t *ml; + seg_t *li; + line_t *ldef; + int linedef_int; + int side; + int sidenum; + + numsegs = w_lump_length(lump) / sizeof(mapseg_t); + segs = z_malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0); + memset(segs, 0, numsegs * sizeof(seg_t)); + data = w_cache_lump_num(lump, PU_STATIC); + + ml = (mapseg_t *)data; + li = segs; + for (i = 0; i < numsegs; i++, li++, ml++) + { + li->v1 = &vertices[SHORT(ml->v1)]; + li->v2 = &vertices[SHORT(ml->v2)]; + + li->angle = (SHORT(ml->angle)) << FRACBITS; + li->offset = (SHORT(ml->offset)) << FRACBITS; + linedef_int = SHORT(ml->linedef); + ldef = &lines[linedef_int]; + li->linedef = ldef; + side = SHORT(ml->side); + + /* e6y: check for wrong indexes */ + + if ((unsigned)ldef->sidenum[side] >= (unsigned)numsides) + { + i_error("p_load_segs: linedef %d for seg %d references a " + "non-existent sidedef %d", + linedef_int, i, (unsigned)ldef->sidenum[side]); + } + + li->sidedef = &sides[ldef->sidenum[side]]; + li->frontsector = sides[ldef->sidenum[side]].sector; + + if (ldef->flags & ML_TWOSIDED) + { + sidenum = ldef->sidenum[side ^ 1]; + + /* If the sidenum is out of range, this may be a "glass hack" + * impassable window. Point at side #0 (this may not be + * the correct Vanilla behavior; however, it seems to work for + * OTTAWAU.WAD, which is the one place I've seen this trick + * used). + */ + + if (sidenum < 0 || sidenum >= numsides) + { + li->backsector = get_sector_at_null_address(); + } + else + { + li->backsector = sides[sidenum].sector; + } + } + else + { + li->backsector = 0; + } + } + + w_release_lump_num(lump); +} + +static void p_load_subsectors(int lump) +{ + byte *data; + int i; + mapsubsector_t *ms; + subsector_t *ss; + + numsubsectors = w_lump_length(lump) / sizeof(mapsubsector_t); + subsectors = z_malloc(numsubsectors * sizeof(subsector_t), PU_LEVEL, 0); + data = w_cache_lump_num(lump, PU_STATIC); + + ms = (mapsubsector_t *)data; + 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); + } + + w_release_lump_num(lump); +} + +static void p_load_sectors(int lump) +{ + byte *data; + int i; + mapsector_t *ms; + sector_t *ss; + + numsectors = w_lump_length(lump) / sizeof(mapsector_t); + sectors = z_malloc(numsectors * sizeof(sector_t), PU_LEVEL, 0); + memset(sectors, 0, numsectors * sizeof(sector_t)); + data = w_cache_lump_num(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_flat_num_for_name(ms->floorpic); + ss->ceilingpic = r_flat_num_for_name(ms->ceilingpic); + ss->lightlevel = SHORT(ms->lightlevel); + ss->special = SHORT(ms->special); + ss->tag = SHORT(ms->tag); + ss->thinglist = NULL; + } + + w_release_lump_num(lump); +} + +static void p_load_nodes(int lump) +{ + byte *data; + int i; + int j; + int k; + mapnode_t *mn; + node_t *no; + + numnodes = w_lump_length(lump) / sizeof(mapnode_t); + nodes = z_malloc(numnodes * sizeof(node_t), PU_LEVEL, 0); + data = w_cache_lump_num(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; + } + } + + w_release_lump_num(lump); +} + +static void p_load_things(int lump) +{ + byte *data; + int i; + mapthing_t *mt; + mapthing_t spawnthing; + int numthings; + boolean spawn; + + data = w_cache_lump_num(lump, PU_STATIC); + numthings = w_lump_length(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 (SHORT(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. */ + + spawnthing.x = SHORT(mt->x); + spawnthing.y = SHORT(mt->y); + spawnthing.angle = SHORT(mt->angle); + spawnthing.type = SHORT(mt->type); + spawnthing.options = SHORT(mt->options); + + p_spawn_map_thing(&spawnthing); + } + + if (!deathmatch) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !playerstartsingame[i]) + { + i_error("p_load_things: Player %d start missing (vanilla " + "crashes here)", + i + 1); + } + + playerstartsingame[i] = false; + } + } + + w_release_lump_num(lump); +} + +/* p_load_linedefs + * Also counts secret lines for intermissions. + */ + +static void p_load_linedefs(int lump) +{ + byte *data; + int i; + maplinedef_t *mld; + line_t *ld; + vertex_t *v1; + vertex_t *v2; + + numlines = w_lump_length(lump) / sizeof(maplinedef_t); + lines = z_malloc(numlines * sizeof(line_t), PU_LEVEL, 0); + memset(lines, 0, numlines * sizeof(line_t)); + data = w_cache_lump_num(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 = &vertices[SHORT(mld->v1)]; + v2 = ld->v2 = &vertices[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 (fixed_div(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; + } + + w_release_lump_num(lump); +} + +static void p_load_side_defs(int lump) +{ + byte *data; + int i; + mapsidedef_t *msd; + side_t *sd; + + numsides = w_lump_length(lump) / sizeof(mapsidedef_t); + sides = z_malloc(numsides * sizeof(side_t), PU_LEVEL, 0); + memset(sides, 0, numsides * sizeof(side_t)); + data = w_cache_lump_num(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_texture_num_for_name(msd->toptexture); + sd->bottomtexture = r_texture_num_for_name(msd->bottomtexture); + sd->midtexture = r_texture_num_for_name(msd->midtexture); + sd->sector = §ors[SHORT(msd->sector)]; + } + + w_release_lump_num(lump); +} + +static void p_load_block_map(int lump) +{ + int i; + int count; + int lumplen; + + lumplen = w_lump_length(lump); + count = lumplen / 2; + + blockmaplump = z_malloc(lumplen, PU_LEVEL, NULL); + w_read_lump(lump, blockmaplump); + blockmap = blockmaplump + 4; + + /* Swap all short integers to native byte ordering. */ + + for (i = 0; i < count; i++) + { + blockmaplump[i] = SHORT(blockmaplump[i]); + } + + /* Read the header */ + + 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); + memset(blocklinks, 0, count); +} + +/* p_group_lines + * Builds sector line lists and subsector sector numbers. + * Finds block bounding boxes for sectors. + */ + +static void p_group_lines(void) +{ + line_t **linebuffer; + int i; + int j; + 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; + totallines = 0; + for (i = 0; i < numlines; i++, li++) + { + totallines++; + li->frontsector->linecount++; + + if (li->backsector && li->backsector != li->frontsector) + { + li->backsector->linecount++; + totallines++; + } + } + + /* build line tables for each sector */ + + linebuffer = z_malloc(totallines * sizeof(line_t *), PU_LEVEL, 0); + + for (i = 0; i < numsectors; ++i) + { + /* Assign the line buffer for this sector */ + + sectors[i].lines = linebuffer; + linebuffer += sectors[i].linecount; + + /* Reset linecount to zero so in the next stage we can count + * lines into the list. + */ + + sectors[i].linecount = 0; + } + + /* Assign lines to sectors */ + + for (i = 0; i < numlines; ++i) + { + li = &lines[i]; + + if (li->frontsector != NULL) + { + sector = li->frontsector; + + sector->lines[sector->linecount] = li; + ++sector->linecount; + } + + if (li->backsector != NULL && li->frontsector != li->backsector) + { + sector = li->backsector; + + sector->lines[sector->linecount] = li; + ++sector->linecount; + } + } + + /* Generate bounding boxes for sectors */ + + sector = sectors; + for (i = 0; i < numsectors; i++, sector++) + { + m_clear_box(bbox); + + for (j = 0; j < sector->linecount; j++) + { + li = sector->lines[j]; + + m_add_to_box(bbox, li->v1->x, li->v1->y); + m_add_to_box(bbox, li->v2->x, li->v2->y); + } + + /* 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; + } +} + +static void p_load_reject(int lumpnum) +{ + int minlength; + int lumplen; + + /* Calculate the size that the REJECT lump *should* be. */ + + minlength = (numsectors * numsectors + 7) / 8; + + /* If the lump meets the minimum length, it can be loaded directly. + * Otherwise, we need to allocate a buffer of the correct size + * and pad it with appropriate data. + */ + + lumplen = w_lump_length(lumpnum); + + if (lumplen >= minlength) + { + rejectmatrix = w_cache_lump_num(lumpnum, PU_LEVEL); + } + else + { + rejectmatrix = z_malloc(minlength, PU_LEVEL, &rejectmatrix); + w_read_lump(lumpnum, rejectmatrix); + + pad_reject_array(rejectmatrix + lumplen, minlength - lumplen, + totallines); + } +} + +/* p_setup_level */ + +void p_setup_level(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_free_tags. */ + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start(); +#endif + + z_free_tags(PU_LEVEL, PU_PURGELEVEL - 1); + + /* UNUSED w_profile (); */ + + p_init_thinkers(); + + /* if working with a development map, reload it */ + + w_reload(); + + /* find map name */ + + if (gamemode == commercial) + { + if (map < 10) + snprintf(lumpname, 9, "map0%d", (int8_t)map); + else + snprintf(lumpname, 9, "map%d", (int8_t)map); + } + else + { + lumpname[0] = 'E'; + lumpname[1] = '0' + episode; + lumpname[2] = 'M'; + lumpname[3] = '0' + map; + lumpname[4] = 0; + } + + lumpnum = w_get_num_for_name(lumpname); + + maplumpinfo = lumpinfo[lumpnum]; + + leveltime = 0; + + /* note: most of this ordering is important */ + + p_load_block_map(lumpnum + ML_BLOCKMAP); + p_load_vertices(lumpnum + ML_VERTICES); + p_load_sectors(lumpnum + ML_SECTORS); + p_load_side_defs(lumpnum + ML_SIDEDEFS); + + p_load_linedefs(lumpnum + ML_LINEDEFS); + p_load_subsectors(lumpnum + ML_SSECTORS); + p_load_nodes(lumpnum + ML_NODES); + p_load_segs(lumpnum + ML_SEGS); + + p_group_lines(); + p_load_reject(lumpnum + ML_REJECT); + + bodyqueslot = 0; + deathmatch_p = deathmatchstarts; + p_load_things(lumpnum + ML_THINGS); + + /* if deathmatch, randomly spawn the active players */ + + if (deathmatch) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + players[i].mo = NULL; + g_death_match_spawn_player(i); + } + } + } + + /* clear special respawning que */ + + iquehead = iquetail = 0; + + /* set up world state */ + + p_spawn_specials(); + + /* preload graphics */ + + if (precache) r_precache_level(); +} + +void p_init(void) +{ + p_init_switch_list(); + p_init_pic_anims(); + r_init_sprites(sprnames); +} diff --git a/games/NXDoom/src/doom/p_setup.h b/games/NXDoom/src/doom/p_setup.h new file mode 100644 index 00000000000..ee4cdb56370 --- /dev/null +++ b/games/NXDoom/src/doom/p_setup.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_setup.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Setup a game, startup stuff. + * + ****************************************************************************/ + +#ifndef __P_SETUP__ +#define __P_SETUP__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "w_wad.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern lumpinfo_t *maplumpinfo; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* NOT called by W_Ticker. Fixme. */ + +void p_setup_level(int episode, int map, int playermask, skill_t skill); + +/* Called by startup code. */ + +void p_init(void); + +#endif /* __P_SETUP__ */ diff --git a/games/NXDoom/src/doom/p_sight.c b/games/NXDoom/src/doom/p_sight.c new file mode 100644 index 00000000000..84167065e19 --- /dev/null +++ b/games/NXDoom/src/doom/p_sight.c @@ -0,0 +1,405 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_sight.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * LineOfSight/Visibility checks, uses REJECT Lookup Table. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" +#include "doomstat.h" + +#include "i_system.h" +#include "p_local.h" + +/* State. */ + +#include "r_state.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* p_check_sight */ + +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]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* PTR_SightTraverse() for Doom 1.2 sight calculations + * taken from prboom-plus/src/p_sight.c:69-102 + */ + +static boolean ptr_sight_traverse(intercept_t *in) +{ + line_t *li; + fixed_t slope; + + li = in->d.line; + + /* crosses a two sided line */ + + p_line_opening(li); + + /* quick test for totally closed doors */ + + if (openbottom >= opentop) + { + return false; /* stop */ + } + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = fixed_div(openbottom - sightzstart, in->frac); + if (slope > bottomslope) bottomslope = slope; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = fixed_div(opentop - sightzstart, in->frac); + if (slope < topslope) topslope = slope; + } + + if (topslope <= bottomslope) return false; /* stop */ + + return true; /* keep going */ +} + +/* p_divline_side + * Returns side 0 (front), 1 (back), or 2 (on). + */ + +static int p_divline_side(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_intercept_vector2 + * Returns the fractional intercept point along the first divline. + * This is only called by the addthings and addlines traversers. + */ + +static fixed_t p_intercept_vector2(divline_t *v2, divline_t *v1) +{ + fixed_t frac; + fixed_t num; + fixed_t den; + + den = fixed_mul(v1->dy >> 8, v2->dx) - fixed_mul(v1->dx >> 8, v2->dy); + + if (den == 0) return 0; + + num = fixed_mul((v1->x - v2->x) >> 8, v1->dy) + + fixed_mul((v2->y - v1->y) >> 8, v1->dx); + frac = fixed_div(num, den); + + return frac; +} + +/* p_cross_subsector + * Returns true if strace crosses the given subsector successfully. + */ + +static boolean p_cross_subsector(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 l_opentop; + fixed_t l_openbottom; + divline_t divl; + vertex_t *v1; + vertex_t *v2; + fixed_t frac; + fixed_t slope; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (num >= numsubsectors) + i_error("p_cross_subsector: ss %i with numss = %i", num, numsubsectors); +#endif + + sub = &subsectors[num]; + + /* check lines */ + + count = sub->numlines; + seg = &segs[sub->firstline]; + + for (; count; seg++, count--) + { + line = seg->linedef; + + /* already checked other side? */ + + if (line->validcount == validcount) continue; + + line->validcount = validcount; + + v1 = line->v1; + v2 = line->v2; + s1 = p_divline_side(v1->x, v1->y, &strace); + s2 = p_divline_side(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_divline_side(strace.x, strace.y, &divl); + s2 = p_divline_side(t2x, t2y, &divl); + + /* line isn't crossed? */ + + if (s1 == s2) continue; + + /* Backsector may be NULL if this is an "impassable + * glass" hack line. + */ + + if (line->backsector == NULL) + { + return false; + } + + /* 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) + l_opentop = front->ceilingheight; + else + l_opentop = back->ceilingheight; + + /* because of ceiling height differences */ + + if (front->floorheight > back->floorheight) + l_openbottom = front->floorheight; + else + l_openbottom = back->floorheight; + + /* quick test for totally closed doors */ + + if (l_openbottom >= l_opentop) return false; /* stop */ + + frac = p_intercept_vector2(&strace, &divl); + + if (front->floorheight != back->floorheight) + { + slope = fixed_div(l_openbottom - sightzstart, frac); + if (slope > bottomslope) bottomslope = slope; + } + + if (front->ceilingheight != back->ceilingheight) + { + slope = fixed_div(l_opentop - sightzstart, frac); + if (slope < topslope) topslope = slope; + } + + if (topslope <= bottomslope) return false; /* stop */ + } + + /* passed the subsector ok */ + + return true; +} + +/* p_cross_bsp_node + * Returns true if strace crosses the given node successfully. + */ + +static boolean p_cross_bsp_node(int bspnum) +{ + node_t *bsp; + int side; + + if (bspnum & NF_SUBSECTOR) + { + if (bspnum == -1) + { + return p_cross_subsector(0); + } + else + { + return p_cross_subsector(bspnum & (~NF_SUBSECTOR)); + } + } + + bsp = &nodes[bspnum]; + + /* decide which side the start point is on */ + + side = p_divline_side(strace.x, strace.y, (divline_t *)bsp); + if (side == 2) side = 0; /* an "on" should cross both sides */ + + /* cross the starting side */ + + if (!p_cross_bsp_node(bsp->children[side])) return false; + + /* the partition plane is crossed here */ + + if (side == p_divline_side(t2x, t2y, (divline_t *)bsp)) + { + /* the line doesn't touch the other side */ + + return true; + } + + /* cross the ending side */ + + return p_cross_bsp_node(bsp->children[side ^ 1]); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_check_sight + * Returns true if a straight line between t1 and t2 is unobstructed. + * Uses REJECT. + */ + +boolean p_check_sight(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 = (t1->subsector->sector - sectors); + s2 = (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; + + if (gameversion <= exe_doom_1_2) + { + return p_path_traverse(t1->x, t1->y, t2->x, t2->y, + PT_EARLYOUT | PT_ADDLINES, + ptr_sight_traverse); + } + + 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_cross_bsp_node(numnodes - 1); +} diff --git a/games/NXDoom/src/doom/p_spec.c b/games/NXDoom/src/doom/p_spec.c new file mode 100644 index 00000000000..2b095210d11 --- /dev/null +++ b/games/NXDoom/src/doom/p_spec.c @@ -0,0 +1,1376 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_spec.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomdef.h" +#include "doomstat.h" + +#include "deh_main.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_misc.h" +#include "m_random.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "p_local.h" +#include "r_local.h" + +#include "g_game.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAXANIMS 32 + +/* Animating line specials */ + +#define MAXLINEANIMS 64 + +/* version <= 1.2 did not have a limit and could handle up to 66 scrolling + * linedefs before displaying adverse effects. All other versions have a + * limit of 64. + */ + +#define MAXLINEANIMS1_2 66 + +/* Thanks to entryway for the Vanilla overflow emulation. + * 20 adjoining sectors max! + */ + +#define MAX_ADJOINING_SECTORS 20 + +/* Donut overrun emulation + * + * Derived from the code from PrBoom+. Thanks go to Andrey Budko (entryway) + * as usual :-) + */ + +#define DONUT_FLOORHEIGHT_DEFAULT 0x00000000 +#define DONUT_FLOORPIC_DEFAULT 0x16 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Animating textures and planes + * There is another anim_t used in wi_stuff, unrelated. + */ + +typedef struct +{ + boolean istexture; + int picnum; + int basepic; + int numpics; + int speed; +} anim_t; + +/* source animation definition */ + +typedef struct +{ + int istexture; /* if false, it is a flat */ + char endname[9]; + char startname[9]; + int speed; +} animdef_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* p_init_pic_anims */ + +/* 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. + */ + +static animdef_t g_animdefs[] = +{ + {false, "NUKAGE3", "NUKAGE1", 8}, + {false, "FWATER4", "FWATER1", 8}, + {false, "SWATER4", "SWATER1", 8}, + {false, "LAVA4", "LAVA1", 8}, + {false, "BLOOD3", "BLOOD1", 8}, + {false, "RROCK08", "RROCK05", 8}, /* DOOM II flat animations. */ + {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, "", "", 0}, +}; + +static anim_t g_anims[MAXANIMS]; +static anim_t *g_lastanim; + +static short g_numlinespecials; +static line_t *g_linespeciallist[MAXLINEANIMS1_2]; + +/* p_update_specials + * Animate planes, scroll walls, etc. + */ + +static boolean g_level_timer; +static int g_level_time_count; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void donut_overrun(fixed_t *s3_floorheight, short *s3_floorpic, + line_t *line, sector_t *pillar_sector) +{ + static int first = 1; + static int tmp_s3_floorheight; + static int tmp_s3_floorpic; + + if (first) + { + int p; + + /* This is the first time we have had an overrun. */ + + first = 0; + + /* Default values */ + + tmp_s3_floorheight = DONUT_FLOORHEIGHT_DEFAULT; + tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; + + /* @category compat + * @arg + * + * Use the specified magic values when emulating behavior caused + * by memory overruns from improperly constructed donuts. + * In Vanilla Doom this can differ depending on the operating + * system. The default (if this option is not specified) is to + * emulate the behavior when running under Windows 98. + */ + + p = m_check_parm_with_args("-donut", 2); + + if (p > 0) + { + /* Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008 + * + * C:\>debug + * -d 0:0 + * + * DOS 6.22: + * 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) + * DOS 7.1: + * 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) + * Win98: + * 0000:0000 (00 00 00 00) 65 04 70 00-(16 00) + * DOSBox under XP: + * 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) + */ + + m_str_to_int(myargv[p + 1], &tmp_s3_floorheight); + m_str_to_int(myargv[p + 2], &tmp_s3_floorpic); + + if (tmp_s3_floorpic >= numflats) + { + fprintf(stderr, + "DonutOverrun: The second parameter for \"-donut\" " + "switch should be greater than 0 and less than number " + "of flats (%d). Using default value (%d) instead. \n", + numflats, DONUT_FLOORPIC_DEFAULT); + tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; + } + } + } + + *s3_floorheight = (fixed_t)tmp_s3_floorheight; + *s3_floorpic = (short)tmp_s3_floorpic; +} + +/* p_spawn_specials + * After the map has been loaded, scan for specials that spawn thinkers + */ + +static unsigned int num_scrollers(void) +{ + unsigned int i; + unsigned int scrollers = 0; + + for (i = 0; i < numlines; i++) + { + if (48 == lines[i].special) + { + scrollers++; + } + } + + return scrollers; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void p_init_pic_anims(void) +{ + int i; + + /* Init animation */ + + g_lastanim = g_anims; + for (i = 0; g_animdefs[i].istexture != -1; i++) + { + const char *startname; + const char *endname; + + startname = (g_animdefs[i].startname); + endname = (g_animdefs[i].endname); + + if (g_animdefs[i].istexture) + { + /* different episode ? */ + + if (r_check_texture_num_for_name(startname) == -1) continue; + + g_lastanim->picnum = r_texture_num_for_name(endname); + g_lastanim->basepic = r_texture_num_for_name(startname); + } + else + { + if (w_check_num_for_name(startname) == -1) continue; + + g_lastanim->picnum = r_flat_num_for_name(endname); + g_lastanim->basepic = r_flat_num_for_name(startname); + } + + g_lastanim->istexture = g_animdefs[i].istexture; + g_lastanim->numpics = g_lastanim->picnum - g_lastanim->basepic + 1; + + if (g_lastanim->numpics < 2) + i_error("p_init_pic_anims: bad cycle from %s to %s", startname, + endname); + + g_lastanim->speed = g_animdefs[i].speed; + g_lastanim++; + } +} + +/* UTILITIES */ + +/* get_side() + * 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 *get_side(int current_sector, int line, int side) +{ + return &sides[(sectors[current_sector].lines[line])->sidenum[side]]; +} + +/* get_sector() + * 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 *get_sector(int current_sector, int line, int side) +{ + return sides[(sectors[current_sector].lines[line])->sidenum[side]].sector; +} + +/* two_sided() + * Given the sector number and the line number, it will tell you whether the + * line is two-sided or not. + */ + +int two_sided(int sector, int line) +{ + return (sectors[sector].lines[line])->flags & ML_TWOSIDED; +} + +/* get_next_sector() + * Return sector_t * of sector next to current. NULL if not two-sided line + */ + +sector_t *get_next_sector(line_t *line, sector_t *sec) +{ + if (!(line->flags & ML_TWOSIDED)) return NULL; + + if (line->frontsector == sec) return line->backsector; + + return line->frontsector; +} + +/* p_find_lowest_floor_surrounding() + * FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS + */ + +fixed_t p_find_lowest_floor_surrounding(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 = get_next_sector(check, sec); + + if (!other) continue; + + if (other->floorheight < floor) floor = other->floorheight; + } + + return floor; +} + +/* p_find_highest_floor_surrounding() + * FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS + */ + +fixed_t p_find_highest_floor_surrounding(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 = get_next_sector(check, sec); + + if (!other) continue; + + if (other->floorheight > floor) floor = other->floorheight; + } + + return floor; +} + +/* p_find_next_highest_floor + * FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS + * Note: this should be doable w/o a fixed array. + */ + +fixed_t p_find_next_highest_floor(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 + 2]; + + for (i = 0, h = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = get_next_sector(check, sec); + + if (!other) continue; + + if (other->floorheight > height) + { + /* Emulation of memory (stack) overflow */ + + if (h == MAX_ADJOINING_SECTORS + 1) + { + height = other->floorheight; + } + else if (h == MAX_ADJOINING_SECTORS + 2) + { + /* Fatal overflow: game crashes at 22 sectors */ + + i_error("Sector with more than 22 adjoining sectors. " + "Vanilla will crash here"); + } + + heightlist[h++] = other->floorheight; + } + } + + /* 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_find_lowest_ceiling_surrounding(sector_t *sec) +{ + int i; + line_t *check; + sector_t *other; + fixed_t height = INT_MAX; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = get_next_sector(check, sec); + + if (!other) continue; + + if (other->ceilingheight < height) height = other->ceilingheight; + } + + return height; +} + +/* FIND HIGHEST CEILING IN THE SURROUNDING SECTORS */ + +fixed_t p_find_heighest_ceiling_surrounding(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 = get_next_sector(check, sec); + + if (!other) continue; + + if (other->ceilingheight > height) height = other->ceilingheight; + } + + return height; +} + +/* RETURN NEXT SECTOR # THAT LINE TAG REFERS TO */ + +int p_find_sector_from_line_tag(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_find_min_surrounding(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 = get_next_sector(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_cross_special_line - TRIGGER + * Called every time a thing origin is about to cross a line with a non 0 + * special. + */ + +void p_cross_special_line(int linenum, int side, mobj_t *thing) +{ + line_t *line; + int is_ok; + + line = &lines[linenum]; + + if (gameversion <= exe_doom_1_2) + { + if (line->special > 98 && line->special != 104) + { + return; + } + } + else + { + /* 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; + + default: + break; + } + } + } + + if (!thing->player) + { + is_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 */ + is_ok = 1; + break; + } + + if (!is_ok) return; + } + + /* Note: could use some const's here. */ + + switch (line->special) + { + /* TRIGGERS. + * All from here to RETRIGGERS. + */ + + case 2: /* Open Door */ + ev_do_door(line, VLD_OPEN); + line->special = 0; + break; + + case 3: /* Close Door */ + ev_do_door(line, VLD_CLOSE); + line->special = 0; + break; + + case 4: /* Raise Door */ + ev_do_door(line, VLD_NORMAL); + line->special = 0; + break; + + case 5: /* Raise Floor */ + ev_do_floor(line, FLOOR_RAISEFLOOR); + line->special = 0; + break; + + case 6: /* Fast Ceiling Crush & Raise */ + ev_do_ceiling(line, CEIL_FASTCRUSHANDRAISE); + line->special = 0; + break; + + case 8: /* Build Stairs */ + ev_build_stairs(line, STAIR_BUILD8); + line->special = 0; + break; + + case 10: /* PlatDownWaitUp */ + ev_do_plat(line, PLAT_DOWNWAITUPSTAY, 0); + line->special = 0; + break; + + case 12: /* Light Turn On - brightest near */ + ev_light_turn_on(line, 0); + line->special = 0; + break; + + case 13: /* Light Turn On 255 */ + ev_light_turn_on(line, 255); + line->special = 0; + break; + + case 16: /* Close Door 30 */ + ev_do_door(line, VLD_CLOSE30THENOPEN); + line->special = 0; + break; + + case 17: /* Start Light Strobing */ + ev_start_light_strobing(line); + line->special = 0; + break; + + case 19: /* Lower Floor */ + ev_do_floor(line, FLOOR_LOWERFLOOR); + line->special = 0; + break; + + case 22: /* Raise floor to nearest height and change texture */ + ev_do_plat(line, PLAT_RAISETONEARESTANDCHANGE, 0); + line->special = 0; + break; + + case 25: /* Ceiling Crush and Raise */ + ev_do_ceiling(line, CEIL_CRUSHANDRAISE); + line->special = 0; + break; + + case 30: + + /* Raise floor to shortest texture height on either side of lines. */ + + ev_do_floor(line, FLOOR_RAISETOTEXTURE); + line->special = 0; + break; + + case 35: /* Lights Very Dark */ + ev_light_turn_on(line, 35); + line->special = 0; + break; + + case 36: /* Lower Floor (TURBO) */ + ev_do_floor(line, FLOOR_TURBOLOWER); + line->special = 0; + break; + + case 37: /* LowerAndChange */ + ev_do_floor(line, FLOOR_LOWERANDCHANGE); + line->special = 0; + break; + + case 38: /* Lower Floor To Lowest */ + ev_do_floor(line, FLOOR_LOWERFLOORTOLOWEST); + line->special = 0; + break; + + case 39: /* TELEPORT! */ + ev_teleport(line, side, thing); + line->special = 0; + break; + + case 40: /* RaiseCeilingLowerFloor */ + ev_do_ceiling(line, CEIL_RAISETOHIGHEST); + ev_do_floor(line, FLOOR_LOWERFLOORTOLOWEST); + line->special = 0; + break; + + case 44: /* Ceiling Crush */ + ev_do_ceiling(line, CEIL_LOWERANDCRUSH); + line->special = 0; + break; + + case 52: /* EXIT! */ + g_exit_level(); + break; + + case 53: /* Perpetual Platform Raise */ + ev_do_plat(line, PLAT_PERPETUALRAISE, 0); + line->special = 0; + break; + + case 54: /* Platform Stop */ + ev_stop_plat(line); + line->special = 0; + break; + + case 56: /* Raise Floor Crush */ + ev_do_floor(line, FLOOR_RAISEFLOORCRUSH); + line->special = 0; + break; + + case 57: /* Ceiling Crush Stop */ + ev_ceiling_crush_stop(line); + line->special = 0; + break; + + case 58: /* Raise Floor 24 */ + ev_do_floor(line, FLOOR_RAISEFLOOR24); + line->special = 0; + break; + + case 59: /* Raise Floor 24 And Change */ + ev_do_floor(line, FLOOR_RAISEFLOOR24ANDCHANGE); + line->special = 0; + break; + + case 104: /* Turn lights off in sector(tag) */ + ev_turn_tag_lights_off(line); + line->special = 0; + break; + + case 108: /* Blazing Door Raise (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZERAISE); + line->special = 0; + break; + + case 109: /* Blazing Door Open (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZEOPEN); + line->special = 0; + break; + + case 100: /* Build Stairs Turbo 16 */ + ev_build_stairs(line, STAIR_TURBO16); + line->special = 0; + break; + + case 110: /* Blazing Door Close (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZECLOSE); + line->special = 0; + break; + + case 119: /* Raise floor to nearest surr. floor */ + ev_do_floor(line, FLOOR_RAISEFLOORTONEAREST); + line->special = 0; + break; + + case 121: /* Blazing PlatDownWaitUpStay */ + ev_do_plat(line, PLAT_BLAZEDWUS, 0); + line->special = 0; + break; + + case 124: /* Secret EXIT */ + g_secret_exit_level(); + break; + + case 125: /* TELEPORT MonsterONLY */ + if (!thing->player) + { + ev_teleport(line, side, thing); + line->special = 0; + } + break; + + case 130: /* Raise Floor Turbo */ + ev_do_floor(line, FLOOR_RAISEFLOORTURBO); + line->special = 0; + break; + + case 141: /* Silent Ceiling Crush & Raise */ + ev_do_ceiling(line, CEIL_SILENTCRUSHANDRAISE); + line->special = 0; + break; + + /* RETRIGGERS. All from here till end. */ + + case 72: /* Ceiling Crush */ + ev_do_ceiling(line, CEIL_LOWERANDCRUSH); + break; + + case 73: /* Ceiling Crush and Raise */ + ev_do_ceiling(line, CEIL_CRUSHANDRAISE); + break; + + case 74: /* Ceiling Crush Stop */ + ev_ceiling_crush_stop(line); + break; + + case 75: /* Close Door */ + ev_do_door(line, VLD_CLOSE); + break; + + case 76: /* Close Door 30 */ + ev_do_door(line, VLD_CLOSE30THENOPEN); + break; + + case 77: /* Fast Ceiling Crush & Raise */ + ev_do_ceiling(line, CEIL_FASTCRUSHANDRAISE); + break; + + case 79: /* Lights Very Dark */ + ev_light_turn_on(line, 35); + break; + + case 80: /* Light Turn On - brightest near */ + ev_light_turn_on(line, 0); + break; + + case 81: /* Light Turn On 255 */ + ev_light_turn_on(line, 255); + break; + + case 82: /* Lower Floor To Lowest */ + ev_do_floor(line, FLOOR_LOWERFLOORTOLOWEST); + break; + + case 83: /* Lower Floor */ + ev_do_floor(line, FLOOR_LOWERFLOOR); + break; + + case 84: /* LowerAndChange */ + ev_do_floor(line, FLOOR_LOWERANDCHANGE); + break; + + case 86: /* Open Door */ + ev_do_door(line, VLD_OPEN); + break; + + case 87: /* Perpetual Platform Raise */ + ev_do_plat(line, PLAT_PERPETUALRAISE, 0); + break; + + case 88: /* PlatDownWaitUp */ + ev_do_plat(line, PLAT_DOWNWAITUPSTAY, 0); + break; + + case 89: /* Platform Stop */ + ev_stop_plat(line); + break; + + case 90: /* Raise Door */ + ev_do_door(line, VLD_NORMAL); + break; + + case 91: /* Raise Floor */ + ev_do_floor(line, FLOOR_RAISEFLOOR); + break; + + case 92: /* Raise Floor 24 */ + ev_do_floor(line, FLOOR_RAISEFLOOR24); + break; + + case 93: /* Raise Floor 24 And Change */ + ev_do_floor(line, FLOOR_RAISEFLOOR24ANDCHANGE); + break; + + case 94: /* Raise Floor Crush */ + ev_do_floor(line, FLOOR_RAISEFLOORCRUSH); + break; + + case 95: /* Raise floor to nearest height and change texture. */ + ev_do_plat(line, PLAT_RAISETONEARESTANDCHANGE, 0); + break; + + case 96: + + /* Raise floor to shortest texture height on either side of lines. */ + + ev_do_floor(line, FLOOR_RAISETOTEXTURE); + break; + + case 97: /* TELEPORT! */ + ev_teleport(line, side, thing); + break; + + case 98: /* Lower Floor (TURBO) */ + ev_do_floor(line, FLOOR_TURBOLOWER); + break; + + case 105: /* Blazing Door Raise (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZERAISE); + break; + + case 106: /* Blazing Door Open (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZEOPEN); + break; + + case 107: /* Blazing Door Close (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZECLOSE); + break; + + case 120: /* Blazing PlatDownWaitUpStay. */ + ev_do_plat(line, PLAT_BLAZEDWUS, 0); + break; + + case 126: /* TELEPORT MonsterONLY. */ + if (!thing->player) ev_teleport(line, side, thing); + break; + + case 128: /* Raise To Nearest Floor */ + ev_do_floor(line, FLOOR_RAISEFLOORTONEAREST); + break; + + case 129: /* Raise Floor Turbo */ + ev_do_floor(line, FLOOR_RAISEFLOORTURBO); + break; + } +} + +/* p_shoot_special_line - IMPACT SPECIALS + * Called when a thing shoots a special line. + */ + +void p_shoot_special_line(mobj_t *thing, line_t *line) +{ + int is_ok; + + /* Impacts that other things can activate. */ + + if (!thing->player) + { + is_ok = 0; + switch (line->special) + { + case 46: /* OPEN DOOR IMPACT */ + is_ok = 1; + break; + } + + if (!is_ok) return; + } + + switch (line->special) + { + case 24: /* RAISE FLOOR */ + ev_do_floor(line, FLOOR_RAISEFLOOR); + p_change_switch_texture(line, 0); + break; + + case 46: /* OPEN DOOR */ + ev_do_door(line, VLD_OPEN); + p_change_switch_texture(line, 1); + break; + + case 47: /* RAISE FLOOR NEAR AND CHANGE */ + ev_do_plat(line, PLAT_RAISETONEARESTANDCHANGE, 0); + p_change_switch_texture(line, 0); + break; + } +} + +/* p_player_in_special_sector + * Called every tic frame that the player origin is in a special sector + */ + +void p_player_in_special_sector(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_damage_mobj(player->mo, NULL, NULL, 10); + } + + break; + + case 7: /* NUKAGE DAMAGE */ + if (!player->powers[pw_ironfeet]) + { + if (!(leveltime & 0x1f)) p_damage_mobj(player->mo, NULL, NULL, 5); + } + + break; + + case 16: /* SUPER HELLSLIME DAMAGE */ + case 4: /* STROBE HURT */ + if (!player->powers[pw_ironfeet] || (p_random() < 5)) + { + if (!(leveltime & 0x1f)) p_damage_mobj(player->mo, NULL, NULL, 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_damage_mobj(player->mo, NULL, NULL, 20); + + if (player->health <= 10) g_exit_level(); + break; + + default: + i_error("p_player_in_special_sector: " + "unknown special %i", + sector->special); + break; + }; +} + +void p_update_specials(void) +{ + anim_t *anim; + int pic; + int i; + line_t *line; + + /* LEVEL TIMER */ + + if (g_level_timer == true) + { + g_level_time_count--; + if (!g_level_time_count) g_exit_level(); + } + + /* ANIMATE FLATS AND TEXTURES GLOBALLY */ + + for (anim = g_anims; anim < g_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 < g_numlinespecials; i++) + { + line = g_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; + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&buttonlist[i].soundorg, SFX_SWTCHN); +#endif + memset(&buttonlist[i], 0, sizeof(button_t)); + } + } +} + +/* Special Stuff that can not be categorized */ + +int ev_do_donut(line_t *line) +{ + sector_t *s1; + sector_t *s2; + sector_t *s3; + int secnum; + int rtn; + int i; + floormove_t *floor; + fixed_t s3_floorheight; + short s3_floorpic; + + secnum = -1; + rtn = 0; + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + s1 = §ors[secnum]; + + /* ALREADY MOVING? IF SO, KEEP GOING... */ + + if (s1->specialdata) continue; + + rtn = 1; + s2 = get_next_sector(s1->lines[0], s1); + + /* Vanilla Doom does not check if the linedef is one sided. The + * game does not crash, but reads invalid memory and causes the + * sector floor to move "down" to some unknown height. + * DOSbox prints a warning about an invalid memory access. + * + * I'm not sure exactly what invalid memory is being read. This + * isn't something that should be done, anyway. + * Just print a warning and return. + */ + + if (s2 == NULL) + { + fprintf(stderr, + "ev_do_donut: linedef had no second sidedef! " + "Unexpected behavior may occur in Vanilla Doom. \n"); + break; + } + + for (i = 0; i < s2->linecount; i++) + { + s3 = s2->lines[i]->backsector; + + if (s3 == s1) continue; + + if (s3 == NULL) + { + /* e6y + * s3 is NULL, so + * s3->floorheight is an int at 0000:0000 + * s3->floorpic is a short at 0000:0008 + * Trying to emulate + */ + + fprintf(stderr, "ev_do_donut: WARNING: emulating buffer " + "overrun due to NULL back sector. Unexpected " + "behavior may occur in Vanilla Doom.\n"); + + donut_overrun(&s3_floorheight, &s3_floorpic, line, s1); + } + else + { + s3_floorheight = s3->floorheight; + s3_floorpic = s3->floorpic; + } + + /* Spawn rising slime */ + + floor = z_malloc(sizeof(*floor), PU_LEVSPEC, 0); + p_add_thinker(&floor->thinker); + s2->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + floor->type = FLOOR_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_add_thinker(&floor->thinker); + s1->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + floor->type = FLOOR_LOWERFLOOR; + floor->crush = false; + floor->direction = -1; + floor->sector = s1; + floor->speed = FLOORSPEED / 2; + floor->floordestheight = s3_floorheight; + break; + } + } + + return rtn; +} + +/* SPECIAL SPAWNING */ + +/* Parses command line parameters. */ + +void p_spawn_specials(void) +{ + sector_t *sector; + int i; + short maxlineanims = + (gameversion <= exe_doom_1_2) ? MAXLINEANIMS1_2 : MAXLINEANIMS; + + /* See if -TIMER was specified. */ + + if (timelimit > 0 && deathmatch) + { + g_level_timer = true; + g_level_time_count = timelimit * 60 * TICRATE; + } + else + { + g_level_timer = false; + } + + /* Init special SECTORs. */ + + sector = sectors; + for (i = 0; i < numsectors; i++, sector++) + { + if (!sector->special) continue; + + switch (sector->special) + { + case 1: /* FLICKERING LIGHTS */ + p_spawn_light_flash(sector); + break; + + case 2: /* STROBE FAST */ + p_spawn_strobe_flash(sector, FASTDARK, 0); + break; + + case 3: /* STROBE SLOW */ + p_spawn_strobe_flash(sector, SLOWDARK, 0); + break; + + case 4: /* STROBE FAST/DEATH SLIME */ + p_spawn_strobe_flash(sector, FASTDARK, 0); + sector->special = 4; + break; + + case 8: /* GLOWING LIGHT */ + p_spawn_glowing_light(sector); + break; + + case 9: /* SECRET SECTOR */ + totalsecret++; + break; + + case 10: /* DOOR CLOSE IN 30 SECONDS */ + p_spawn_door_close_in30(sector); + break; + + case 12: /* SYNC STROBE SLOW */ + p_spawn_strobe_flash(sector, SLOWDARK, 1); + break; + + case 13: /* SYNC STROBE FAST */ + p_spawn_strobe_flash(sector, FASTDARK, 1); + break; + + case 14: /* DOOR RAISE IN 5 MINUTES */ + p_spawn_door_raise_in_5min(sector, i); + break; + + case 17: /* first introduced in official v1.4 beta */ + if (gameversion > exe_doom_1_2) + { + p_spawn_fire_flicker(sector); + } + break; + } + } + + /* Init line EFFECTs */ + + g_numlinespecials = 0; + + for (i = 0; i < numlines; i++) + { + switch (lines[i].special) + { + case 48: + if (g_numlinespecials >= maxlineanims) + { + i_error("p_spawn_specials: Too many scrolling wall linedefs " + "(%d)! (Vanilla limit is %d)", + num_scrollers(), maxlineanims); + } + + /* EFFECT FIRSTCOL SCROLL+ */ + + g_linespeciallist[g_numlinespecials] = &lines[i]; + g_numlinespecials++; + break; + } + } + + /* Init other misc stuff */ + + for (i = 0; i < MAXCEILINGS; i++) + activeceilings[i] = NULL; + + for (i = 0; i < MAXPLATS; i++) + activeplats[i] = NULL; + + for (i = 0; i < MAXBUTTONS; i++) + memset(&buttonlist[i], 0, sizeof(button_t)); + + /* UNUSED: no horizontal sliders. + * p_init_sliding_door_frames(); + */ +} diff --git a/games/NXDoom/src/doom/p_spec.h b/games/NXDoom/src/doom/p_spec.h new file mode 100644 index 00000000000..f69c818b4c6 --- /dev/null +++ b/games/NXDoom/src/doom/p_spec.h @@ -0,0 +1,503 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_spec.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Define values for map objects */ + +#define MO_TELEPORTMAN 14 + +#define GLOWSPEED 8 +#define STROBEBRIGHT 5 +#define FASTDARK 15 +#define SLOWDARK 35 + +/* 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 + +#define PLATWAIT 3 +#define PLATSPEED FRACUNIT +#define MAXPLATS 30 + +#define FLOORSPEED FRACUNIT + +#define VDOORSPEED FRACUNIT * 2 +#define VDOORWAIT 150 + +#define CEILSPEED FRACUNIT +#define CEILWAIT 150 +#define MAXCEILINGS 30 + +#if 0 /* UNUSED */ + +/* how many frames of animation */ + +#define SNUMFRAMES 4 + +#define SDOORWAIT 35 * 3 +#define SWAITTICS 4 + +/* how many diff. types of anims */ + +#define MAXSLIDEDOORS 5 +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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; + +/* 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; + degenmobj_t *soundorg; +} button_t; + +/* P_PLATS */ + +typedef enum +{ + up, + down, + waiting, + in_stasis +} plat_e; + +typedef enum +{ + PLAT_PERPETUALRAISE, + PLAT_DOWNWAITUPSTAY, + PLAT_RAISEANDCHANGE, + PLAT_RAISETONEARESTANDCHANGE, + PLAT_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; + boolean crush; + int tag; + plattype_e type; +} plat_t; + +/* P_FLOOR */ + +typedef enum +{ + /* lower floor to highest surrounding floor */ + + FLOOR_LOWERFLOOR, + + /* lower floor to lowest surrounding floor */ + + FLOOR_LOWERFLOORTOLOWEST, + + /* lower floor to highest surrounding floor VERY FAST */ + + FLOOR_TURBOLOWER, + + /* raise floor to lowest surrounding CEILING */ + + FLOOR_RAISEFLOOR, + + /* raise floor to next highest surrounding floor */ + + FLOOR_RAISEFLOORTONEAREST, + + /* raise floor to shortest height texture around it */ + + FLOOR_RAISETOTEXTURE, + + /* lower floor to lowest surrounding floor and change floorpic */ + + FLOOR_LOWERANDCHANGE, + + FLOOR_RAISEFLOOR24, + FLOOR_RAISEFLOOR24ANDCHANGE, + FLOOR_RAISEFLOORCRUSH, + + /* raise to next highest floor, turbo-speed */ + + FLOOR_RAISEFLOORTURBO, + FLOOR_DONUTRAISE, + FLOOR_RAISEFLOOR512 +} floor_e; + +typedef enum +{ + STAIR_BUILD8, /* slowly build by 8 */ + STAIR_TURBO16 /* quickly build by 16 */ +} stair_e; + +typedef struct +{ + thinker_t thinker; + floor_e type; + boolean crush; + sector_t *sector; + int direction; + int newspecial; + short texture; + fixed_t floordestheight; + fixed_t speed; +} floormove_t; + +typedef enum +{ + ok, + crushed, + pastdest +} result_e; + +typedef enum +{ + VLD_NORMAL, + VLD_CLOSE30THENOPEN, + VLD_CLOSE, + VLD_OPEN, + VLD_RAISEIN5MINS, + VLD_BLAZERAISE, + VLD_BLAZEOPEN, + VLD_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; + +/* P_CEILNG */ + +typedef enum +{ + CEIL_LOWERTOFLOOR, + CEIL_RAISETOHIGHEST, + CEIL_LOWERANDCRUSH, + CEIL_CRUSHANDRAISE, + CEIL_FASTCRUSHANDRAISE, + CEIL_SILENTCRUSHANDRAISE +} ceiling_e; + +typedef struct +{ + thinker_t thinker; + ceiling_e type; + sector_t *sector; + fixed_t bottomheight; + fixed_t topheight; + fixed_t speed; + boolean crush; + + /* 1 = up, 0 = waiting, -1 = down */ + + int direction; + + /* ID */ + + int tag; + int olddirection; +} ceiling_t; + +#if 0 /* UNUSED */ + +/* Sliding doors... */ + +typedef enum +{ + sd_opening, + sd_waiting, + sd_closing +} sd_e; + +typedef enum +{ + SDT_OPENONLY, + SDT_CLOSEONLY, + SDT_OPENANDCLOSE +} sdt_e; + +typedef struct +{ + thinker_t thinker; + sdt_e type; + line_t *line; + int frame; + int which_door_index; + int timer; + sector_t *frontsector; + sector_t *backsector; + sd_e status; +} slidedoor_t; + +typedef struct +{ + char front_frame1[9]; + char front_frame2[9]; + char front_frame3[9]; + char front_frame4[9]; + char back_frame1[9]; + char back_frame2[9]; + char back_frame3[9]; + char back_frame4[9]; +} slidename_t; + +typedef struct +{ + int front_frames[4]; + int back_frames[4]; +} slideframe_t; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* End-level timer (-TIMER option) */ + +extern button_t buttonlist[MAXBUTTONS]; + +extern plat_t *activeplats[MAXPLATS]; + +extern ceiling_t *activeceilings[MAXCEILINGS]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* at game start */ + +void p_init_pic_anims(void); + +/* at map load */ + +void p_spawn_specials(void); + +/* every tic */ + +void p_update_specials(void); + +/* when needed */ + +boolean p_use_special_line(mobj_t *thing, line_t *line, int side); + +void p_shoot_special_line(mobj_t *thing, line_t *line); + +void p_cross_special_line(int linenum, int side, mobj_t *thing); + +void p_player_in_special_sector(player_t *player); + +int two_sided(int sector, int line); + +sector_t *get_sector(int current_sector, int line, int side); + +side_t *get_side(int current_sector, int line, int side); + +fixed_t p_find_lowest_floor_surrounding(sector_t *sec); +fixed_t p_find_highest_floor_surrounding(sector_t *sec); + +fixed_t p_find_next_highest_floor(sector_t *sec, int currentheight); + +fixed_t p_find_lowest_ceiling_surrounding(sector_t *sec); +fixed_t p_find_heighest_ceiling_surrounding(sector_t *sec); + +int p_find_sector_from_line_tag(line_t *line, int start); + +int p_find_min_surrounding(sector_t *sector, int max); + +sector_t *get_next_sector(line_t *line, sector_t *sec); + +/* SPECIAL */ + +int ev_do_donut(line_t *line); + +void p_spawn_fire_flicker(sector_t *sector); +void t_light_flash(lightflash_t *flash); +void p_spawn_light_flash(sector_t *sector); +void t_strobe_flash(strobe_t *flash); + +void p_spawn_strobe_flash(sector_t *sector, int fast_or_slow, int in_sync); + +void ev_start_light_strobing(line_t *line); +void ev_turn_tag_lights_off(line_t *line); + +void ev_light_turn_on(line_t *line, int bright); + +void t_glow(glow_t *g); +void p_spawn_glowing_light(sector_t *sector); + +void p_change_switch_texture(line_t *line, int use_again); + +void p_init_switch_list(void); + +void t_plat_raise(plat_t *plat); + +int ev_do_plat(line_t *line, plattype_e type, int amount); + +void p_add_active_plat(plat_t *plat); +void p_remove_active_plat(plat_t *plat); +void ev_stop_plat(line_t *line); +void p_activate_in_stasis(int tag); + +/* P_DOORS */ + +void ev_vertical_door(line_t *line, mobj_t *thing); + +int ev_do_door(line_t *line, vldoor_e type); + +int ev_do_locked_door(line_t *line, vldoor_e type, mobj_t *thing); + +void t_vertical_door(vldoor_t *door); +void p_spawn_door_close_in30(sector_t *sec); + +void p_spawn_door_raise_in_5min(sector_t *sec, int secnum); + +int ev_do_ceiling(line_t *line, ceiling_e type); + +void t_move_ceiling(ceiling_t *ceiling); +void p_add_active_ceiling(ceiling_t *c); +void p_remove_active_ceiling(ceiling_t *c); +int ev_ceiling_crush_stop(line_t *line); +void p_activate_in_stasis_ceiling(line_t *line); + +result_e t_move_plane(sector_t *sector, fixed_t speed, fixed_t dest, + boolean crush, int floor_or_ceiling, int direction); + +int ev_build_stairs(line_t *line, stair_e type); + +int ev_do_floor(line_t *line, floor_e floortype); + +void t_move_floor(floormove_t *floor); + +/* P_TELEPT */ + +int ev_teleport(line_t *line, int side, mobj_t *thing); + +#if 0 /* UNUSED */ +void p_init_sliding_door_frames(void); + +void ev_sliding_door(line_t *line, mobj_t *thing); +#endif + +#endif /* __P_SPEC__ */ diff --git a/games/NXDoom/src/doom/p_switch.c b/games/NXDoom/src/doom/p_switch.c new file mode 100644 index 00000000000..27640d5ec21 --- /dev/null +++ b/games/NXDoom/src/doom/p_switch.c @@ -0,0 +1,596 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_switch.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * + * DESCRIPTION: + * Switches, buttons. Two-state animation. Exits. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "deh_main.h" +#include "doomdef.h" +#include "i_system.h" +#include "p_local.h" + +#include "g_game.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE */ + +static switchlist_t g_alph_switch_list[] = +{ + {"SW1BRCOM", "SW2BRCOM", 1}, /* Doom shareware episode 1 switches */ + {"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}, + {"SW1BLUE", "SW2BLUE", 2}, /* Doom registered episodes 2 & 3 switches */ + {"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}, + {"SW1PANEL", "SW2PANEL", 3}, /* Doom II switches */ + {"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}, +}; + +static int g_numswitches; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int switchlist[MAXSWITCHES * 2]; +button_t buttonlist[MAXBUTTONS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Start a button counting down till it turns off. */ + +static void p_start_button(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 = &line->frontsector->soundorg; + return; + } + } + + i_error("P_StartButton: no button slots left!"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_init_switch_list + * Only called at game initialization. + */ + +void p_init_switch_list(void) +{ + int i; + int slindex; + int episode; + + /* Note that this is called "episode" here but it's actually something + * quite different. As we progress from Shareware->Registered->Doom II + * we support more switch textures. + */ + + switch (gamemode) + { + case registered: + case retail: + episode = 2; + break; + case commercial: + episode = 3; + break; + default: + episode = 1; + break; + } + + slindex = 0; + + for (i = 0; i < arrlen(g_alph_switch_list); i++) + { + if (g_alph_switch_list[i].episode <= episode) + { + switchlist[slindex++] = + r_texture_num_for_name((g_alph_switch_list[i].name1)); + switchlist[slindex++] = + r_texture_num_for_name((g_alph_switch_list[i].name2)); + } + } + + g_numswitches = slindex / 2; + switchlist[slindex] = -1; +} + +/* Function that changes wall texture. + * Tell it if switch is ok to use again (1=yes, it's a button). + */ + +void p_change_switch_texture(line_t *line, int use_again) +{ + int tex_top; + int tex_mid; + int tex_bot; + int i; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + int sound; +#endif + + if (!use_again) line->special = 0; + + tex_top = sides[line->sidenum[0]].toptexture; + tex_mid = sides[line->sidenum[0]].midtexture; + tex_bot = sides[line->sidenum[0]].bottomtexture; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_SWTCHN; + + /* EXIT SWITCH? */ + + if (line->special == 11) sound = SFX_SWTCHX; +#endif + + for (i = 0; i < g_numswitches * 2; i++) + { + if (switchlist[i] == tex_top) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(buttonlist->soundorg, sound); +#endif + sides[line->sidenum[0]].toptexture = switchlist[i ^ 1]; + + if (use_again) + { + p_start_button(line, top, switchlist[i], BUTTONTIME); + } + + return; + } + else + { + if (switchlist[i] == tex_mid) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(buttonlist->soundorg, sound); +#endif + sides[line->sidenum[0]].midtexture = switchlist[i ^ 1]; + + if (use_again) + p_start_button(line, middle, switchlist[i], BUTTONTIME); + + return; + } + else + { + if (switchlist[i] == tex_bot) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(buttonlist->soundorg, sound); +#endif + sides[line->sidenum[0]].bottomtexture = switchlist[i ^ 1]; + + if (use_again) + p_start_button(line, bottom, switchlist[i], BUTTONTIME); + + return; + } + } + } + } +} + +/* p_use_special_line + * Called when a thing uses a special line. + * Only the front sides of lines are usable. + */ + +boolean p_use_special_line(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_vertical_door(line, thing); + break; + + /* UNUSED - Door Slide Open&Close + * case 124: + * ev_sliding_door (line, thing); + * break; + */ + + /* SWITCHES */ + + case 7: /* Build Stairs */ + if (ev_build_stairs(line, STAIR_BUILD8)) + p_change_switch_texture(line, 0); + break; + + case 9: /* Change Donut */ + if (ev_do_donut(line)) p_change_switch_texture(line, 0); + break; + + case 11: /* Exit level */ + p_change_switch_texture(line, 0); + g_exit_level(); + break; + + case 14: /* Raise Floor 32 and change texture */ + if (ev_do_plat(line, PLAT_RAISEANDCHANGE, 32)) + p_change_switch_texture(line, 0); + break; + + case 15: /* Raise Floor 24 and change texture */ + if (ev_do_plat(line, PLAT_RAISEANDCHANGE, 24)) + p_change_switch_texture(line, 0); + break; + + case 18: /* Raise Floor to next highest floor */ + if (ev_do_floor(line, FLOOR_RAISEFLOORTONEAREST)) + p_change_switch_texture(line, 0); + break; + + case 20: /* Raise Plat next highest floor and change texture */ + if (ev_do_plat(line, PLAT_RAISETONEARESTANDCHANGE, 0)) + p_change_switch_texture(line, 0); + break; + + case 21: /* PlatDownWaitUpStay */ + if (ev_do_plat(line, PLAT_DOWNWAITUPSTAY, 0)) + p_change_switch_texture(line, 0); + break; + + case 23: /* Lower Floor to Lowest */ + if (ev_do_floor(line, FLOOR_LOWERFLOORTOLOWEST)) + p_change_switch_texture(line, 0); + break; + + case 29: /* Raise Door */ + if (ev_do_door(line, VLD_NORMAL)) p_change_switch_texture(line, 0); + break; + + case 41: /* Lower Ceiling to Floor */ + if (ev_do_ceiling(line, CEIL_LOWERTOFLOOR)) + p_change_switch_texture(line, 0); + break; + + case 71: /* Turbo Lower Floor */ + if (ev_do_floor(line, FLOOR_TURBOLOWER)) + p_change_switch_texture(line, 0); + break; + + case 49: /* Ceiling Crush And Raise */ + if (ev_do_ceiling(line, CEIL_CRUSHANDRAISE)) + p_change_switch_texture(line, 0); + break; + + case 50: /* Close Door */ + if (ev_do_door(line, VLD_CLOSE)) p_change_switch_texture(line, 0); + break; + + case 51: /* Secret EXIT */ + p_change_switch_texture(line, 0); + g_secret_exit_level(); + break; + + case 55: /* Raise Floor Crush */ + if (ev_do_floor(line, FLOOR_RAISEFLOORCRUSH)) + p_change_switch_texture(line, 0); + break; + + case 101: /* Raise Floor */ + if (ev_do_floor(line, FLOOR_RAISEFLOOR)) + p_change_switch_texture(line, 0); + break; + + case 102: /* Lower Floor to Surrounding floor height */ + if (ev_do_floor(line, FLOOR_LOWERFLOOR)) + p_change_switch_texture(line, 0); + break; + + case 103: /* Open Door */ + if (ev_do_door(line, VLD_OPEN)) p_change_switch_texture(line, 0); + break; + + case 111: /* Blazing Door Raise (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZERAISE)) p_change_switch_texture(line, 0); + break; + + case 112: /* Blazing Door Open (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZEOPEN)) p_change_switch_texture(line, 0); + break; + + case 113: /* Blazing Door Close (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZECLOSE)) p_change_switch_texture(line, 0); + break; + + case 122: /* Blazing PlatDownWaitUpStay */ + if (ev_do_plat(line, PLAT_BLAZEDWUS, 0)) + p_change_switch_texture(line, 0); + break; + + case 127: /* Build Stairs Turbo 16 */ + if (ev_build_stairs(line, STAIR_TURBO16)) + p_change_switch_texture(line, 0); + break; + + case 131: /* Raise Floor Turbo */ + if (ev_do_floor(line, FLOOR_RAISEFLOORTURBO)) + p_change_switch_texture(line, 0); + break; + + case 133: /* BlzOpenDoor BLUE */ + case 135: /* BlzOpenDoor RED */ + case 137: /* BlzOpenDoor YELLOW */ + if (ev_do_locked_door(line, VLD_BLAZEOPEN, thing)) + p_change_switch_texture(line, 0); + break; + + case 140: /* Raise Floor 512 */ + if (ev_do_floor(line, FLOOR_RAISEFLOOR512)) + p_change_switch_texture(line, 0); + break; + + /* BUTTONS */ + + case 42: /* Close Door */ + if (ev_do_door(line, VLD_CLOSE)) p_change_switch_texture(line, 1); + break; + + case 43: /* Lower Ceiling to Floor */ + if (ev_do_ceiling(line, CEIL_LOWERTOFLOOR)) + p_change_switch_texture(line, 1); + break; + + case 45: /* Lower Floor to Surrounding floor height */ + if (ev_do_floor(line, FLOOR_LOWERFLOOR)) + p_change_switch_texture(line, 1); + break; + + case 60: /* Lower Floor to Lowest */ + if (ev_do_floor(line, FLOOR_LOWERFLOORTOLOWEST)) + p_change_switch_texture(line, 1); + break; + + case 61: /* Open Door */ + if (ev_do_door(line, VLD_OPEN)) p_change_switch_texture(line, 1); + break; + + case 62: /* PlatDownWaitUpStay */ + if (ev_do_plat(line, PLAT_DOWNWAITUPSTAY, 1)) + p_change_switch_texture(line, 1); + break; + + case 63: /* Raise Door */ + if (ev_do_door(line, VLD_NORMAL)) p_change_switch_texture(line, 1); + break; + + case 64: /* Raise Floor to ceiling */ + if (ev_do_floor(line, FLOOR_RAISEFLOOR)) + p_change_switch_texture(line, 1); + break; + + case 66: /* Raise Floor 24 and change texture */ + if (ev_do_plat(line, PLAT_RAISEANDCHANGE, 24)) + p_change_switch_texture(line, 1); + break; + + case 67: /* Raise Floor 32 and change texture */ + if (ev_do_plat(line, PLAT_RAISEANDCHANGE, 32)) + p_change_switch_texture(line, 1); + break; + + case 65: /* Raise Floor Crush */ + if (ev_do_floor(line, FLOOR_RAISEFLOORCRUSH)) + p_change_switch_texture(line, 1); + break; + + case 68: /* Raise Plat to next highest floor and change texture */ + if (ev_do_plat(line, PLAT_RAISETONEARESTANDCHANGE, 0)) + p_change_switch_texture(line, 1); + break; + + case 69: /* Raise Floor to next highest floor */ + if (ev_do_floor(line, FLOOR_RAISEFLOORTONEAREST)) + p_change_switch_texture(line, 1); + break; + + case 70: /* Turbo Lower Floor */ + if (ev_do_floor(line, FLOOR_TURBOLOWER)) + p_change_switch_texture(line, 1); + break; + + case 114: /* Blazing Door Raise (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZERAISE)) p_change_switch_texture(line, 1); + break; + + case 115: /* Blazing Door Open (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZEOPEN)) p_change_switch_texture(line, 1); + break; + + case 116: /* Blazing Door Close (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZECLOSE)) p_change_switch_texture(line, 1); + break; + + case 123: /* Blazing PlatDownWaitUpStay */ + if (ev_do_plat(line, PLAT_BLAZEDWUS, 0)) + p_change_switch_texture(line, 1); + break; + + case 132: /* Raise Floor Turbo */ + if (ev_do_floor(line, FLOOR_RAISEFLOORTURBO)) + p_change_switch_texture(line, 1); + break; + + case 99: /* BlzOpenDoor BLUE */ + case 134: /* BlzOpenDoor RED */ + case 136: /* BlzOpenDoor YELLOW */ + if (ev_do_locked_door(line, VLD_BLAZEOPEN, thing)) + p_change_switch_texture(line, 1); + break; + + case 138: + + /* Light Turn On */ + + ev_light_turn_on(line, 255); + p_change_switch_texture(line, 1); + break; + + case 139: + + /* Light Turn Off */ + + ev_light_turn_on(line, 35); + p_change_switch_texture(line, 1); + break; + } + + return true; +} diff --git a/games/NXDoom/src/doom/p_telept.c b/games/NXDoom/src/doom/p_telept.c new file mode 100644 index 00000000000..3986a0543d6 --- /dev/null +++ b/games/NXDoom/src/doom/p_telept.c @@ -0,0 +1,141 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_telept.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Teleportation. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" +#include "doomstat.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "p_local.h" + +/* State. */ + +#include "r_state.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +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) + { + for (thinker = thinkercap.next; thinker != &thinkercap; + thinker = thinker->next) + { + /* not a mobj */ + + if (thinker->function.acp1 != (actionf_p1)p_mobj_thinker) + 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_teleport_move(thing, m->x, m->y)) return 0; + + /* The first Final Doom executable does not set thing->z + * when teleporting. This quirk is unique to this + * particular version; the later version included in + * some versions of the Id Anthology fixed this. + */ + + if (gameversion != exe_final) thing->z = thing->floorz; + + if (thing->player) + thing->player->viewz = thing->z + thing->player->viewheight; + + /* spawn teleport fog at source and destination */ + + fog = p_spawn_mobj(oldx, oldy, oldz, MT_TFOG); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(fog, SFX_TELEPT); +#endif + an = m->angle >> ANGLETOFINESHIFT; + fog = p_spawn_mobj(m->x + 20 * finecosine[an], + m->y + 20 * finesine[an], + thing->z, MT_TFOG); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + + /* emit sound, where? */ + + s_start_sound(fog, SFX_TELEPT); +#else + UNUSED(fog); +#endif + + /* 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/games/NXDoom/src/doom/p_tick.c b/games/NXDoom/src/doom/p_tick.c new file mode 100644 index 00000000000..d2655dd18b4 --- /dev/null +++ b/games/NXDoom/src/doom/p_tick.c @@ -0,0 +1,145 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_tick.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Archiving: SaveGame I/O. + * Thinker, Ticker. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "p_local.h" +#include "z_zone.h" + +#include "doomstat.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +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; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void p_run_thinkers(void) +{ + thinker_t *currentthinker, *nextthinker; + + currentthinker = thinkercap.next; + while (currentthinker != &thinkercap) + { + if (currentthinker->function.acv == (actionf_v)(-1)) + { + /* time to remove it */ + + nextthinker = currentthinker->next; + currentthinker->next->prev = currentthinker->prev; + currentthinker->prev->next = currentthinker->next; + z_free(currentthinker); + } + else + { + if (currentthinker->function.acp1) + currentthinker->function.acp1(currentthinker); + nextthinker = currentthinker->next; + } + + currentthinker = nextthinker; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void p_init_thinkers(void) +{ + thinkercap.prev = thinkercap.next = &thinkercap; +} + +/* p_add_thinker + * Adds a new thinker at the end of the list. + */ + +void p_add_thinker(thinker_t *thinker) +{ + thinkercap.prev->next = thinker; + thinker->next = &thinkercap; + thinker->prev = thinkercap.prev; + thinkercap.prev = thinker; +} + +/* p_remove_thinker Deallocation is lazy -- it will not actually be freed + * until its thinking turn comes up. + */ + +void p_remove_thinker(thinker_t *thinker) +{ + /* FIXME: NOP. */ + + thinker->function.acv = (actionf_v)(-1); +} + +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 && g_menuactive && !demoplayback && + players[consoleplayer].viewz != 1) + { + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + p_player_think(&players[i]); + } + } + + p_run_thinkers(); + p_update_specials(); + p_respawn_specials(); + + /* for par times */ + + leveltime++; +} diff --git a/games/NXDoom/src/doom/p_tick.h b/games/NXDoom/src/doom/p_tick.h new file mode 100644 index 00000000000..0b2d5dee17d --- /dev/null +++ b/games/NXDoom/src/doom/p_tick.h @@ -0,0 +1,32 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_tick.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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 /* __P_TICK__ */ diff --git a/games/NXDoom/src/doom/p_user.c b/games/NXDoom/src/doom/p_user.c new file mode 100644 index 00000000000..0a5d0107b90 --- /dev/null +++ b/games/NXDoom/src/doom/p_user.c @@ -0,0 +1,371 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_user.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Player related stuff. + * Bobbing POV/weapon, movement. + * Pending weapon. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "doomdef.h" + +#include "p_local.h" + +#include "doomstat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Index of the special effects (INVUL inverse) map. */ + +#define INVERSECOLORMAP 32 + +/* Movement. */ + +/* 16 pixels of bob */ + +#define MAXBOB 0x100000 + +#define ANG5 (ANG90 / 18) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +boolean onground; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* P_Thrust + * Moves the given origin along a given angle. + */ + +static void p_thrust(player_t *player, angle_t angle, fixed_t move) +{ + angle >>= ANGLETOFINESHIFT; + + player->mo->momx += fixed_mul(move, finecosine[angle]); + player->mo->momy += fixed_mul(move, finesine[angle]); +} + +/* Calculate the walking / running height adjustment */ + +static void p_calc_height(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 = fixed_mul(player->mo->momx, player->mo->momx) + + fixed_mul(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 = fixed_mul(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; +} + +static void p_move_player(player_t *player) +{ + ticcmd_t *cmd; + + cmd = &player->cmd; + + player->mo->angle += (cmd->angleturn << FRACBITS); + + /* 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_set_mobj_state(player->mo, S_PLAY_RUN1); + } +} + +/* P_DeathThink + * Fall on your face when dying. + * Decrease POV height to floor height. + */ + +static void p_death_think(player_t *player) +{ + angle_t angle; + angle_t delta; + + p_move_psprites(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_calc_height(player); + + if (player->attacker && player->attacker != player->mo) + { + angle = r_point_to_angle2(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; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void p_player_think(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_death_think(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_move_player(player); + + p_calc_height(player); + + if (player->mo->subsector->sector->special) + { + p_player_in_special_sector(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_use_lines(player); + player->usedown = true; + } + } + else + player->usedown = false; + + /* cycle psprites */ + + p_move_psprites(player); + + /* Counters, time dependent 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/games/NXDoom/src/doom/r_bsp.c b/games/NXDoom/src/doom/r_bsp.c new file mode 100644 index 00000000000..605b21beaa8 --- /dev/null +++ b/games/NXDoom/src/doom/r_bsp.c @@ -0,0 +1,684 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_bsp.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * BSP traversal, handling of LineSegs for rendering. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#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" +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* We must expand MAXSEGS to the theoretical limit of the number of solidsegs + * that can be generated in a scene by the DOOM engine. This was determined + * by Lee Killough during BOOM development to be a function of the + * screensize. The simplest thing we can do, other than fix this bug, is to + * let the game render overage and then bomb out by detecting the overflow + * after the fact. -haleyjd + */ + +/* #define MAXSEGS 32 */ + +#define MAXSEGS (SCREENWIDTH / 2 + 1) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* ClipWallSegment + * Clips the given range of columns and includes it in the new clip list. + */ + +typedef struct +{ + int first; + int last; +} cliprange_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +seg_t *curline; +side_t *sidedef; +line_t *linedef; +sector_t *frontsector; +sector_t *backsector; + +drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_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, + }, +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void r_store_wall_range(int start, int stop); +void r_clip_solid_wall_segment(int first, int last); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_clip_pass_wall_segment + * + * Description: + * 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. + * + ****************************************************************************/ + +static void r_clip_pass_wall_segment(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_store_wall_range(first, last); + return; + } + + /* There is a fragment above *start. */ + + r_store_wall_range(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_store_wall_range(start->last + 1, (start + 1)->first - 1); + start++; + + if (last <= start->last) return; + } + + /* There is a fragment after *next. */ + + r_store_wall_range(start->last + 1, last); +} + +/* r_check_b_box + * Checks BSP node/subtree bounding box. + * Returns true if some part of the bbox might be visible. + */ + +static boolean r_check_b_box(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_point_to_angle(x1, y1) - viewangle; + angle2 = r_point_to_angle(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; + + angle2 = -clipangle; + } + + /* 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; +} + +/**************************************************************************** + * Name: r_add_line + * + * Description: + * Clips the given segment + * and adds any visible pieces to the line list. + * + ****************************************************************************/ + +static void r_add_line(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_point_to_angle(line->v1->x, line->v1->y); + angle2 = r_point_to_angle(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; + angle2 = -clipangle; + } + + /* 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_clip_pass_wall_segment(x1, x2 - 1); + return; + +clipsolid: + r_clip_solid_wall_segment(x1, x2 - 1); +} + +/* r_subsector + * Determine floor/ceiling planes. + * Add sprites of things in sector. + * Draw one or more line segments. + */ + +static void r_subsector(int num) +{ + int count; + seg_t *line; + subsector_t *sub; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (num >= numsubsectors) + i_error("R_Subsector: ss %i with numss = %i", num, numsubsectors); +#endif + + sscount++; + sub = &subsectors[num]; + frontsector = sub->sector; + count = sub->numlines; + line = &segs[sub->firstline]; + + if (frontsector->floorheight < viewz) + { + floorplane = + r_find_plane(frontsector->floorheight, frontsector->floorpic, + frontsector->lightlevel); + } + else + floorplane = NULL; + + if (frontsector->ceilingheight > viewz || + frontsector->ceilingpic == skyflatnum) + { + ceilingplane = + r_find_plane(frontsector->ceilingheight, frontsector->ceilingpic, + frontsector->lightlevel); + } + else + ceilingplane = NULL; + + r_add_sprites(frontsector); + + while (count--) + { + r_add_line(line); + line++; + } + + /* check for solidsegs overflow - extremely unsatisfactory! */ + + if (newend > &solidsegs[32]) + { + i_error("R_Subsector: solidsegs overflow (vanilla may crash here)\n"); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_clear_draw_segs + ****************************************************************************/ + +void r_clear_draw_segs(void) +{ + ds_p = drawsegs; +} + +/**************************************************************************** + * Name: r_clip_solid_wall_segment + * + * Description: + * Does handle solid walls, e.g. single sided LineDefs (middle texture) + * that entirely block the view. + * + ****************************************************************************/ + +void r_clip_solid_wall_segment(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_store_wall_range(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_store_wall_range(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_store_wall_range(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_store_wall_range(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; +} + +/**************************************************************************** + * Name: r_clear_clip_segs + ****************************************************************************/ + +void r_clear_clip_segs(void) +{ + solidsegs[0].first = -0x7fffffff; + solidsegs[0].last = -1; + solidsegs[1].first = viewwidth; + solidsegs[1].last = 0x7fffffff; + newend = solidsegs + 2; +} + +/* RenderBSPNode + * Renders all subsectors below a given node, traversing subtree recursively. + * Just call with BSP root. + */ + +void r_render_bsp_node(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_point_on_side(viewx, viewy, bsp); + + /* Recursively divide front space. */ + + r_render_bsp_node(bsp->children[side]); + + /* Possibly divide back space. */ + + if (r_check_b_box(bsp->bbox[side ^ 1])) + r_render_bsp_node(bsp->children[side ^ 1]); +} diff --git a/games/NXDoom/src/doom/r_bsp.h b/games/NXDoom/src/doom/r_bsp.h new file mode 100644 index 00000000000..66c6d611fff --- /dev/null +++ b/games/NXDoom/src/doom/r_bsp.h @@ -0,0 +1,73 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_bsp.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Refresh module, BSP traversal and handling. + * + ****************************************************************************/ + +#ifndef __R_BSP__ +#define __R_BSP__ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*drawfunc_t)(int start, int stop); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +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 boolean segtextured; + +/* false if the back side is the same plane */ + +extern boolean markfloor; +extern boolean markceiling; + +extern boolean skymap; + +extern drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS]; +extern drawseg_t *ds_p; + +extern lighttable_t **hscalelight; +extern lighttable_t **vscalelight; +extern lighttable_t **dscalelight; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* BSP? */ + +void r_clear_clip_segs(void); +void r_clear_draw_segs(void); + +void r_render_bsp_node(int bspnum); + +#endif /* __R_BSP__ */ diff --git a/games/NXDoom/src/doom/r_data.c b/games/NXDoom/src/doom/r_data.c new file mode 100644 index 00000000000..2a35c2eda99 --- /dev/null +++ b/games/NXDoom/src/doom/r_data.c @@ -0,0 +1,944 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_data.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Preparation of data for rendering, + * generation of lookups, caching, retrieval by name. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "deh_main.h" +#include "i_swap.h" +#include "i_system.h" +#include "z_zone.h" + +#include "w_wad.h" + +#include "doomdef.h" +#include "m_misc.h" +#include "p_local.h" +#include "r_local.h" + +#include "doomstat.h" +#include "r_sky.h" + +#include "r_data.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* 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. + */ + +begin_packed_struct struct mappatch_t +{ + short originx; + short originy; + short patch; + short stepdir; + short colormap; +} end_packed_struct; + +typedef struct mappatch_t mappatch_t; + +/* Texture definition. + * A DOOM wall texture is a list of patches + * which are to be combined in a predefined order. + */ + +begin_packed_struct struct maptexture_t +{ + char name[8]; + int masked; + short width; + short height; + int obsolete; + short patchcount; + mappatch_t patches[1]; +} end_packed_struct; + +typedef struct maptexture_t maptexture_t; + +/* A single patch from a texture definition, basically a rectangular area + * within the texture rectangle. + */ + +typedef struct +{ + /* Block origin (always UL), which has already accounted for the internal + * origin of the patch. + */ + + short originx; + short 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 texture_s texture_t; + +struct texture_s +{ + /* Keep name for switch changing, etc. */ + + char name[8]; + short width; + short height; + + /* Index in textures list */ + + int index; + + /* Next in hash table chain */ + + texture_t *next; + + /* All the patches[patchcount] are drawn back to front into the cached + * texture. + */ + + short patchcount; + texpatch_t patches[1]; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int firstflat; +int lastflat; +int numflats; + +int firstpatch; +int lastpatch; +int numpatches; + +int firstspritelump; +int lastspritelump; +int numspritelumps; + +int numtextures; +texture_t **textures; +texture_t **textures_hashtable; + +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; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void generate_texture_hashtable(void) +{ + texture_t **rover; + int i; + int key; + + textures_hashtable = + z_malloc(sizeof(texture_t *) * numtextures, PU_STATIC, 0); + + memset(textures_hashtable, 0, sizeof(texture_t *) * numtextures); + + /* Add all textures to hash table */ + + for (i = 0; i < numtextures; ++i) + { + /* Store index */ + + textures[i]->index = i; + + /* Vanilla Doom does a linear search of the textures array + * and stops at the first entry it finds. If there are two + * entries with the same name, the first one in the array + * wins. The new entry must therefore be added at the end + * of the hash chain, so that earlier entries win. + */ + + key = w_lump_name_hash(textures[i]->name) % numtextures; + + rover = &textures_hashtable[key]; + + while (*rover != NULL) + { + rover = &(*rover)->next; + } + + /* Hook into hash table */ + + textures[i]->next = NULL; + *rover = textures[i]; + } +} + +/**************************************************************************** + * Name: r_generate_lookup + ****************************************************************************/ + +static void r_generate_lookup(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 *)z_malloc(texture->width, PU_STATIC, &patchcount); + memset(patchcount, 0, texture->width); + + for (i = 0, patch = texture->patches; + i < texture->patchcount; + i++, patch++) + { + realpatch = w_cache_lump_num(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]) + { + printf("r_generate_lookup: column without a patch (%s)\n", + texture->name); + return; + } + + if (patchcount[x] > 1) + { + /* Use the cached block. */ + + collump[x] = -1; + colofs[x] = texturecompositesize[texnum]; + + if (texturecompositesize[texnum] > 0x10000 - texture->height) + { + i_error("r_generate_lookup: texture %i is >64k", texnum); + } + + texturecompositesize[texnum] += texture->height; + } + } + + z_free(patchcount); +} + +/* 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. + */ + +/**************************************************************************** + * Name: r_init_textures + * + * Description: + * Initializes the texture list + * with the textures from the world map. + * + ****************************************************************************/ + +static void r_init_textures(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 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_cache_lump_name(("PNAMES"), PU_STATIC); + nummappatches = LONG(*((int *)names)); + name_p = names + 4; + patchlookup = + z_malloc(nummappatches * sizeof(*patchlookup), PU_STATIC, NULL); + + for (i = 0; i < nummappatches; i++) + { + m_str_copy(name, name_p + i * 8, sizeof(name)); + patchlookup[i] = w_check_num_for_name(name); + } + + w_release_lump_name(("PNAMES")); + + /* 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_cache_lump_name(("TEXTURE1"), PU_STATIC); + numtextures1 = LONG(*maptex); + maxoff = w_lump_length(w_get_num_for_name(("TEXTURE1"))); + directory = maptex + 1; + + if (w_check_num_for_name(("TEXTURE2")) != -1) + { + maptex2 = w_cache_lump_name(("TEXTURE2"), PU_STATIC); + numtextures2 = LONG(*maptex2); + maxoff2 = w_lump_length(w_get_num_for_name(("TEXTURE2"))); + } + else + { + maptex2 = NULL; + numtextures2 = 0; + maxoff2 = 0; + } + + numtextures = numtextures1 + numtextures2; + + textures = z_malloc(numtextures * sizeof(*textures), PU_STATIC, 0); + texturecolumnlump = + z_malloc(numtextures * sizeof(*texturecolumnlump), PU_STATIC, 0); + texturecolumnofs = + z_malloc(numtextures * sizeof(*texturecolumnofs), PU_STATIC, 0); + texturecomposite = + z_malloc(numtextures * sizeof(*texturecomposite), PU_STATIC, 0); + texturecompositesize = + z_malloc(numtextures * sizeof(*texturecompositesize), PU_STATIC, 0); + texturewidthmask = + z_malloc(numtextures * sizeof(*texturewidthmask), PU_STATIC, 0); + textureheight = + z_malloc(numtextures * sizeof(*textureheight), PU_STATIC, 0); + + /* Really complex printing shit... */ + + temp1 = w_get_num_for_name(("S_START")); /* P_??????? */ + temp2 = w_get_num_for_name(("S_END")) - 1; + temp3 = ((temp2 - temp1 + 63) / 64) + ((numtextures + 63) / 64); + + /* If stdout is a real console, use the classic vanilla "filling + * up the box" effect, which uses backspace to "step back" inside + * the box. If stdout is a file, don't draw the box. + */ + + if (i_console_stdout()) + { + printf("["); + for (i = 0; i < temp3 + 9; i++) + printf(" "); + printf("]"); + for (i = 0; i < temp3 + 10; i++) + printf("\b"); + } + + for (i = 0; i < numtextures; i++, directory++) + { + if (!(i & 63)) printf("."); + + if (i == numtextures1) + { + /* Start looking in second texture file. */ + + maptex = maptex2; + maxoff = maxoff2; + directory = maptex + 1; + } + + offset = LONG(*directory); + + if (offset > maxoff) i_error("r_init_textures: 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); + + 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("r_init_textures: Missing patch in texture %s", + texture->name); + } + } + + texturecolumnlump[i] = z_malloc( + texture->width * sizeof(**texturecolumnlump), PU_STATIC, 0); + texturecolumnofs[i] = + z_malloc(texture->width * sizeof(**texturecolumnofs), + PU_STATIC, 0); + + j = 1; + while (j * 2 <= texture->width) + j <<= 1; + + texturewidthmask[i] = j - 1; + textureheight[i] = texture->height << FRACBITS; + } + + z_free(patchlookup); + + w_release_lump_name(("TEXTURE1")); + if (maptex2) w_release_lump_name(("TEXTURE2")); + + /* Precalculate whatever possible. */ + + for (i = 0; i < numtextures; i++) + r_generate_lookup(i); + + /* Create translation table for global animation. */ + + texturetranslation = + z_malloc((numtextures + 1) * sizeof(*texturetranslation), + PU_STATIC, 0); + + for (i = 0; i < numtextures; i++) + texturetranslation[i] = i; + + generate_texture_hashtable(); +} + +/**************************************************************************** + * Name: r_init_flats + ****************************************************************************/ + +static void r_init_flats(void) +{ + int i; + + firstflat = w_get_num_for_name(("F_START")) + 1; + lastflat = w_get_num_for_name(("F_END")) - 1; + numflats = lastflat - firstflat + 1; + + /* Create translation table for global animation. */ + + flattranslation = + z_malloc((numflats + 1) * sizeof(*flattranslation), PU_STATIC, 0); + + for (i = 0; i < numflats; i++) + flattranslation[i] = i; +} + +/**************************************************************************** + * Name: r_init_sprite_lumps + * + * Description: + * 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. + * + ****************************************************************************/ + +static void r_init_sprite_lumps(void) +{ + int i; + patch_t *patch; + + firstspritelump = w_get_num_for_name(("S_START")) + 1; + lastspritelump = w_get_num_for_name(("S_END")) - 1; + + numspritelumps = lastspritelump - firstspritelump + 1; + spritewidth = + z_malloc(numspritelumps * sizeof(*spritewidth), PU_STATIC, 0); + spriteoffset = + z_malloc(numspritelumps * sizeof(*spriteoffset), PU_STATIC, 0); + spritetopoffset = + z_malloc(numspritelumps * sizeof(*spritetopoffset), PU_STATIC, 0); + + for (i = 0; i < numspritelumps; i++) + { + if (!(i & 63)) printf("."); + + patch = w_cache_lump_num(firstspritelump + i, PU_CACHE); + spritewidth[i] = SHORT(patch->width) << FRACBITS; + spriteoffset[i] = SHORT(patch->leftoffset) << FRACBITS; + spritetopoffset[i] = SHORT(patch->topoffset) << FRACBITS; + } +} + +/**************************************************************************** + * Name: r_init_colourmaps + ****************************************************************************/ + +static void r_init_colourmaps(void) +{ + int lump; + + /* Load in the light tables, 256 byte align tables. */ + + lump = w_get_num_for_name(("COLORMAP")); + colormaps = w_cache_lump_num(lump, PU_STATIC); +} + +/**************************************************************************** + * Name: r_draw_column_in_cache + * + * Description: + * Clip and draw a column from a patch into a cached post. + * + ****************************************************************************/ + +static void r_draw_column_in_cache(column_t *patch, byte *cache, int originy, + int cacheheight) +{ + int count; + int position; + byte *source; + + 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) memcpy(cache + position, source, count); + + patch = (column_t *)((byte *)patch + patch->length + 4); + } +} + +/**************************************************************************** + * Name: r_generate_composite + * + * Description: + * Using the texture definition, the composite texture is created from the + * patches, and each column is cached. + * + ****************************************************************************/ + +static void r_generate_composite(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. */ + + for (i = 0, patch = texture->patches; + i < texture->patchcount; + i++, patch++) + { + realpatch = w_cache_lump_num(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_draw_column_in_cache(patchcol, block + colofs[x], + patch->originy, texture->height); + } + } + + /* Now that the texture has been built in column cache, + * it is purgeable from zone memory. + */ + + z_change_tag(block, PU_CACHE); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_get_column + ****************************************************************************/ + +byte *r_get_column(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_cache_lump_num(lump, PU_CACHE) + ofs; + + if (!texturecomposite[tex]) r_generate_composite(tex); + + return texturecomposite[tex] + ofs; +} + +/**************************************************************************** + * Name: r_init_data + * + * Description: + * Locates all the lumps that will be used by all views + * Must be called after W_Init. + * + ****************************************************************************/ + +void r_init_data(void) +{ + r_init_textures(); + printf("."); + r_init_flats(); + printf("."); + r_init_sprite_lumps(); + printf("."); + r_init_colourmaps(); +} + +/**************************************************************************** + * Name: r_flat_num_for_name + * + * Description: + * Retrieval, get a flat number for a flat name. + * + ****************************************************************************/ + +int r_flat_num_for_name(const char *name) +{ + int i; + char namet[9]; + + i = w_check_num_for_name(name); + + if (i == -1) + { + namet[8] = 0; + memcpy(namet, name, 8); + i_error("r_flat_num_for_name: %s not found", namet); + } + + return i - firstflat; +} + +/**************************************************************************** + * Name: r_check_texture_num_for_name + * + * Description: + * Check whether texture is available. + * Filter out NoTexture indicator. + * + ****************************************************************************/ + +int r_check_texture_num_for_name(const char *name) +{ + texture_t *texture; + int key; + + /* "NoTexture" marker. */ + + if (name[0] == '-') return 0; + + key = w_lump_name_hash(name) % numtextures; + + texture = textures_hashtable[key]; + + while (texture != NULL) + { + if (!strncasecmp(texture->name, name, 8)) return texture->index; + + texture = texture->next; + } + + return -1; +} + +/**************************************************************************** + * Name: r_texture_num_for_name + * + * Description: + * Calls r_check_texture_num_for_name, aborts with error message. + * + ****************************************************************************/ + +int r_texture_num_for_name(const char *name) +{ + int i; + + i = r_check_texture_num_for_name(name); + + if (i == -1) + { + i_error("r_texture_num_for_name: %s not found", name); + } + + return i; +} + +/**************************************************************************** + * Name: r_precache_level + * + * Description: + * Preloads all relevant graphics for the level. + * + ****************************************************************************/ + +void r_precache_level(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 = z_malloc(numflats, PU_STATIC, NULL); + 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_cache_lump_num(lump, PU_CACHE); + } + } + + z_free(flatpresent); + + /* Precache textures. */ + + texturepresent = z_malloc(numtextures, PU_STATIC, NULL); + 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 dependent 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_cache_lump_num(lump, PU_CACHE); + } + } + + z_free(texturepresent); + + /* Precache sprites. */ + + spritepresent = z_malloc(numsprites, PU_STATIC, NULL); + memset(spritepresent, 0, numsprites); + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 == (actionf_p1)p_mobj_thinker) + 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_cache_lump_num(lump, PU_CACHE); + } + } + } + + z_free(spritepresent); +} diff --git a/games/NXDoom/src/doom/r_data.h b/games/NXDoom/src/doom/r_data.h new file mode 100644 index 00000000000..f320ed0f7bb --- /dev/null +++ b/games/NXDoom/src/doom/r_data.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_data.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Refresh module, data I/O, caching, retrieval of graphics + * by name. + * + ****************************************************************************/ + +#ifndef __R_DATA__ +#define __R_DATA__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "r_defs.h" +#include "r_state.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int numflats; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Retrieve column data for span blitting. */ + +byte *r_get_column(int tex, int col); + +/* I/O, setting up the stuff. */ + +void r_init_data(void); +void r_precache_level(void); + +/* Retrieval. + * Floor/ceiling opaque texture tiles, + * lookup by name. For animation? + */ + +int r_flat_num_for_name(const char *name); + +/* Called by p_ticker for switches and animations, + * returns the texture number for the texture name. + */ + +int r_texture_num_for_name(const char *name); +int r_check_texture_num_for_name(const char *name); + +#endif /* __R_DATA__ */ diff --git a/games/NXDoom/src/doom/r_defs.h b/games/NXDoom/src/doom/r_defs.h new file mode 100644 index 00000000000..ae84ae63e88 --- /dev/null +++ b/games/NXDoom/src/doom/r_defs.h @@ -0,0 +1,444 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_defs.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Refresh/rendering module, shared data struct definitions. + * + ****************************************************************************/ + +#ifndef __R_DEFS__ +#define __R_DEFS__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* 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" + +#include "i_video.h" + +#include "v_patch.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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 reversible 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 reversible 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 NULL 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; + +/* PC direct to screen pointers + * B UNUSED - keep till detailshift in r_draw.c resolved + * extern byte* destview; + * extern byte* destscreen; + */ + +/* OTHER TYPES */ + +/* This could be wider for >8 bit display. + * + * Indeed, true color support is possible precalculating 24bpp + * lightmap/colormap LUT. from darkening PLAYPAL to all black. Could even + * use more than 32 levels. + */ + +typedef pixel_t 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; + +/* 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_init_sprites. + * + * 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. + */ + + 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 /* __R_DEFS__ */ diff --git a/games/NXDoom/src/doom/r_draw.c b/games/NXDoom/src/doom/r_draw.c new file mode 100644 index 00000000000..ff243912aed --- /dev/null +++ b/games/NXDoom/src/doom/r_draw.c @@ -0,0 +1,965 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_draw.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * The actual span/column drawing functions. + * Here find the main potential for optimization, e.g. inline assembly, + * different algorithms. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_main.h" +#include "doomdef.h" + +#include "i_system.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "r_local.h" + +/* Needs access to LFB (guess what). */ + +#include "v_video.h" + +/* State. */ + +#include "doomstat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* ? */ + +#define MAXWIDTH 1120 +#define MAXHEIGHT 832 + +/* status bar height at bottom of screen */ + +#define SBARHEIGHT 32 + +/* Spectre/Invisibility. */ + +#define FUZZTABLE 50 +#define FUZZOFF (SCREENWIDTH) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* All drawing to the view buffer is accomplished in this file. + * + * The other refresh files only know about coordinates, 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; +pixel_t *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]; + +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; + +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; + +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; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Backing buffer containing the bezel drawn around the screen and + * surrounding background. + */ + +static pixel_t *background_buffer = NULL; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Source is the top of the column to scale. + * + * 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_draw_column(void) +{ + int count; + pixel_t *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 CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + i_error("r_draw_column: %i to %i at %i", dc_yl, dc_yh, dc_x); +#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--); +} + +/* UNUSED. Loop unrolled. */ + +#if 0 +void r_draw_column(void) +{ + int count; + byte *source; + byte *dest; + byte *colormap; + + unsigned frac; + unsigned fracstep; + unsigned fracstep2; + unsigned fracstep3; + unsigned fracstep4; + + count = dc_yh - dc_yl + 1; + + source = dc_source; + colormap = dc_colormap; + dest = ylookup[dc_yl] + columnofs[dc_x]; + + fracstep = dc_iscale << 9; + frac = (dc_texturemid + (dc_yl - centery) * dc_iscale) << 9; + + fracstep2 = fracstep + fracstep; + fracstep3 = fracstep2 + fracstep; + fracstep4 = fracstep3 + fracstep; + + while (count >= 8) + { + dest[0] = colormap[source[frac >> 25]]; + dest[SCREENWIDTH] = colormap[source[(frac + fracstep) >> 25]]; + dest[SCREENWIDTH * 2] = colormap[source[(frac + fracstep2) >> 25]]; + dest[SCREENWIDTH * 3] = colormap[source[(frac + fracstep3) >> 25]]; + + frac += fracstep4; + + dest[SCREENWIDTH * 4] = colormap[source[frac >> 25]]; + dest[SCREENWIDTH * 5] = colormap[source[(frac + fracstep) >> 25]]; + dest[SCREENWIDTH * 6] = colormap[source[(frac + fracstep2) >> 25]]; + dest[SCREENWIDTH * 7] = colormap[source[(frac + fracstep3) >> 25]]; + + frac += fracstep4; + dest += SCREENWIDTH * 8; + count -= 8; + } + + while (count > 0) + { + *dest = colormap[source[frac >> 25]]; + dest += SCREENWIDTH; + frac += fracstep; + count--; + } +} +#endif + +void r_draw_column_low(void) +{ + int count; + pixel_t *dest; + pixel_t *dest2; + fixed_t frac; + fixed_t fracstep; + int x; + + count = dc_yh - dc_yl; + + /* Zero length. */ + + if (count < 0) return; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + i_error("r_draw_column: %i to %i at %i", dc_yl, dc_yh, dc_x); + } +#endif + + /* Blocky mode, need to multiply by 2. */ + + x = dc_x << 1; + + dest = ylookup[dc_yl] + columnofs[x]; + dest2 = ylookup[dc_yl] + columnofs[x + 1]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + do + { + /* Hack. Does not work correctly. */ + + *dest2 = *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]]; + dest += SCREENWIDTH; + dest2 += SCREENWIDTH; + frac += fracstep; + } + while (count--); +} + +/* 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_draw_fuzz_column(void) +{ + int count; + pixel_t *dest; + + /* 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 CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + i_error("r_draw_fuzz_column: %i to %i at %i", dc_yl, dc_yh, dc_x); + } +#endif + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + /* 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; + } + while (count--); +} + +/* low detail mode version */ + +void r_draw_fuzz_column_low(void) +{ + int count; + pixel_t *dest; + pixel_t *dest2; + int x; + + /* 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; + + /* low detail mode, need to multiply by 2 */ + + x = dc_x << 1; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + i_error("r_draw_fuzz_column: %i to %i at %i", dc_yl, dc_yh, dc_x); + } +#endif + + dest = ylookup[dc_yl] + columnofs[x]; + dest2 = ylookup[dc_yl] + columnofs[x + 1]; + + /* 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]]]; + *dest2 = colormaps[6 * 256 + dest2[fuzzoffset[fuzzpos]]]; + + /* Clamp table lookup index. */ + + if (++fuzzpos == FUZZTABLE) fuzzpos = 0; + + dest += SCREENWIDTH; + dest2 += SCREENWIDTH; + } + while (count--); +} + +/* r_draw_translated_column + * + * 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_draw_translated_column(void) +{ + int count; + pixel_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = dc_yh - dc_yl; + if (count < 0) return; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + i_error("r_draw_column: %i to %i at %i", dc_yl, dc_yh, dc_x); + } + +#endif + + 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--); +} + +void r_draw_translated_column_low(void) +{ + int count; + pixel_t *dest; + pixel_t *dest2; + fixed_t frac; + fixed_t fracstep; + int x; + + count = dc_yh - dc_yl; + if (count < 0) return; + + /* low detail, need to scale by 2 */ + + x = dc_x << 1; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + i_error("r_draw_column: %i to %i at %i", dc_yl, dc_yh, x); + } + +#endif + + dest = ylookup[dc_yl] + columnofs[x]; + dest2 = ylookup[dc_yl] + columnofs[x + 1]; + + /* 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]]]; + *dest2 = dc_colormap[dc_translation[dc_source[frac >> FRACBITS]]]; + dest += SCREENWIDTH; + dest2 += SCREENWIDTH; + + frac += fracstep; + } + while (count--); +} + +/* r_init_translation_table + * + * 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_init_translation_table(void) +{ + int i; + + translationtables = z_malloc(256 * 3, PU_STATIC, 0); + + /* 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_draw_span + * + * 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. + * + * Draws the actual span. + */ + +void r_draw_span(void) +{ + unsigned int position; + unsigned int step; + pixel_t *dest; + int count; + int spot; + unsigned int xtemp; + unsigned int ytemp; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || + (unsigned)ds_y > SCREENHEIGHT) + { + i_error("r_draw_span: %i to %i at %i", ds_x1, ds_x2, ds_y); + } +#endif + + /* Pack position and step variables into a single 32-bit integer, + * with x in the top 16 bits and y in the bottom 16 bits. For + * each 16-bit part, the top 6 bits are the integer part and the + * bottom 10 bits are the fractional part of the pixel position. + */ + + position = ((ds_xfrac << 10) & 0xffff0000) | + ((ds_yfrac >> 6) & 0x0000ffff); + step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0x0000ffff); + + dest = ylookup[ds_y] + columnofs[ds_x1]; + + /* We do not check for zero spans here? */ + + count = ds_x2 - ds_x1; + + do + { + /* Calculate current texture index in u,v. */ + + ytemp = (position >> 4) & 0x0fc0; + xtemp = (position >> 26); + spot = xtemp | ytemp; + + /* Lookup pixel from flat texture tile, re-index using light/colormap. + */ + + *dest++ = ds_colormap[ds_source[spot]]; + + position += step; + } + while (count--); +} + +/* UNUSED. Loop unrolled by 4. */ + +#if 0 +void r_draw_span(void) +{ + unsigned position; + unsigned step; + + byte *source; + byte *colormap; + pixel_t *dest; + + unsigned count; + usingned spot; + unsigned value; + unsigned temp; + unsigned xtemp; + unsigned ytemp; + + position = ((ds_xfrac << 10) & 0xffff0000) | ((ds_yfrac >> 6) & 0xffff); + step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0xffff); + + source = ds_source; + colormap = ds_colormap; + dest = ylookup[ds_y] + columnofs[ds_x1]; + count = ds_x2 - ds_x1 + 1; + + while (count >= 4) + { + ytemp = position >> 4; + ytemp = ytemp & 4032; + xtemp = position >> 26; + spot = xtemp | ytemp; + position += step; + dest[0] = colormap[source[spot]]; + + ytemp = position >> 4; + ytemp = ytemp & 4032; + xtemp = position >> 26; + spot = xtemp | ytemp; + position += step; + dest[1] = colormap[source[spot]]; + + ytemp = position >> 4; + ytemp = ytemp & 4032; + xtemp = position >> 26; + spot = xtemp | ytemp; + position += step; + dest[2] = colormap[source[spot]]; + + ytemp = position >> 4; + ytemp = ytemp & 4032; + xtemp = position >> 26; + spot = xtemp | ytemp; + position += step; + dest[3] = colormap[source[spot]]; + + count -= 4; + dest += 4; + } + while (count > 0) + { + ytemp = position >> 4; + ytemp = ytemp & 4032; + xtemp = position >> 26; + spot = xtemp | ytemp; + position += step; + *dest++ = colormap[source[spot]]; + count--; + } +} +#endif + +/* Again.. */ + +void r_draw_span_low(void) +{ + unsigned int position; + unsigned int step; + unsigned int xtemp; + unsigned int ytemp; + pixel_t *dest; + int count; + int spot; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || + (unsigned)ds_y > SCREENHEIGHT) + { + i_error("r_draw_span: %i to %i at %i", ds_x1, ds_x2, ds_y); + } +#endif + + position = ((ds_xfrac << 10) & 0xffff0000) | + ((ds_yfrac >> 6) & 0x0000ffff); + step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0x0000ffff); + + count = (ds_x2 - ds_x1); + + /* Blocky mode, need to multiply by 2. */ + + ds_x1 <<= 1; + ds_x2 <<= 1; + + dest = ylookup[ds_y] + columnofs[ds_x1]; + + do + { + /* Calculate current texture index in u,v. */ + + ytemp = (position >> 4) & 0x0fc0; + xtemp = (position >> 26); + spot = xtemp | ytemp; + + /* Lowres/blocky mode does it twice, while scale is adjusted + * appropriately. + */ + + *dest++ = ds_colormap[ds_source[spot]]; + *dest++ = ds_colormap[ds_source[spot]]; + + position += step; + } + while (count--); +} + +/* r_init_buffer + * + * Creates lookup tables that avoid multiplies and other hazzles for getting + * the framebuffer address of a pixel to draw. + */ + +void r_init_buffer(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] = i_video_buffer + (i + viewwindowy) * SCREENWIDTH; +} + +/* r_fill_back_screen + * + * Fills the back screen with a pattern for variable screen sizes + * Also draws a beveled edge. + */ + +void r_fill_back_screen(void) +{ + byte *src; + pixel_t *dest; + int x; + int y; + patch_t *patch; + + /* DOOM border patch. */ + + const char *name1 = ("FLOOR7_2"); + + /* DOOM II border patch. */ + + const char *name2 = ("GRNROCK"); + + const char *name; + + /* If we are running full screen, there is no need to do any of this, + * and the background buffer can be freed if it was previously in use. + */ + + if (scaledviewwidth == SCREENWIDTH) + { + if (background_buffer != NULL) + { + z_free(background_buffer); + background_buffer = NULL; + } + + return; + } + + /* Allocate the background buffer if necessary */ + + if (background_buffer == NULL) + { + background_buffer = z_malloc( + SCREENWIDTH * (SCREENHEIGHT - SBARHEIGHT) * + sizeof(*background_buffer), PU_STATIC, NULL); + } + + if (gamemode == commercial) + name = name2; + else + name = name1; + + src = w_cache_lump_name(name, PU_CACHE); + dest = background_buffer; + + for (y = 0; y < SCREENHEIGHT - SBARHEIGHT; y++) + { + for (x = 0; x < SCREENWIDTH / 64; x++) + { + memcpy(dest, src + ((y & 63) << 6), 64); + dest += 64; + } + + if (SCREENWIDTH & 63) + { + memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); + dest += (SCREENWIDTH & 63); + } + } + + /* Draw screen and bezel; this is done to a separate screen buffer. */ + + v_use_buffer(background_buffer); + + patch = w_cache_lump_name(("brdr_t"), PU_CACHE); + + for (x = 0; x < scaledviewwidth; x += 8) + v_draw_patch(viewwindowx + x, viewwindowy - 8, patch); + patch = w_cache_lump_name(("brdr_b"), PU_CACHE); + + for (x = 0; x < scaledviewwidth; x += 8) + v_draw_patch(viewwindowx + x, viewwindowy + viewheight, patch); + patch = w_cache_lump_name(("brdr_l"), PU_CACHE); + + for (y = 0; y < viewheight; y += 8) + v_draw_patch(viewwindowx - 8, viewwindowy + y, patch); + patch = w_cache_lump_name(("brdr_r"), PU_CACHE); + + for (y = 0; y < viewheight; y += 8) + v_draw_patch(viewwindowx + scaledviewwidth, viewwindowy + y, patch); + + /* Draw beveled edge. */ + + v_draw_patch(viewwindowx - 8, viewwindowy - 8, + w_cache_lump_name(("brdr_tl"), PU_CACHE)); + + v_draw_patch(viewwindowx + scaledviewwidth, viewwindowy - 8, + w_cache_lump_name(("brdr_tr"), PU_CACHE)); + + v_draw_patch(viewwindowx - 8, viewwindowy + viewheight, + w_cache_lump_name(("brdr_bl"), PU_CACHE)); + + v_draw_patch(viewwindowx + scaledviewwidth, viewwindowy + viewheight, + w_cache_lump_name(("brdr_br"), PU_CACHE)); + + v_restore_buffer(); +} + +/* Copy a screen buffer. */ + +void r_video_erase(unsigned ofs, int count) +{ + /* LFB copy. + * This might not be a good idea if memcpy is not optional, e.g. byte by + * byte on a 32bit CPU, as GNU GCC/Linux libc did at one point. + */ + + if (background_buffer != NULL) + { + memcpy(i_video_buffer + ofs, background_buffer + ofs, + count * sizeof(*i_video_buffer)); + } +} + +/* r_draw_view_border + * Draws the border around the view for different size windows? + */ + +void r_draw_view_border(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_video_erase(0, top * SCREENWIDTH + side); + + /* copy one line of right side and bottom */ + + ofs = (viewheight + top) * SCREENWIDTH - side; + r_video_erase(ofs, top * SCREENWIDTH + side); + + /* copy sides using wraparound */ + + ofs = top * SCREENWIDTH + SCREENWIDTH - side; + side <<= 1; + + for (i = 1; i < viewheight; i++) + { + r_video_erase(ofs, side); + ofs += SCREENWIDTH; + } + + /* ? */ + + v_mark_rect(0, 0, SCREENWIDTH, SCREENHEIGHT - SBARHEIGHT); +} diff --git a/games/NXDoom/src/doom/r_draw.h b/games/NXDoom/src/doom/r_draw.h new file mode 100644 index 00000000000..47e85c1ed43 --- /dev/null +++ b/games/NXDoom/src/doom/r_draw.h @@ -0,0 +1,107 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_draw.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __R_DRAW__ +#define __R_DRAW__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +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; + +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; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Span blitting for rows, floor/ceiling. No Spectre effect needed. */ + +void r_draw_span(void); + +/* Low resolution mode, 160x200? */ + +void r_draw_span_low(void); + +void r_init_buffer(int width, int height); + +/* Initialize color translation tables, for player rendering etc. */ + +void r_init_translation_table(void); + +/* Rendering function. */ + +void r_fill_back_screen(void); + +/* If the view size is not full screen, draws a border around it. */ + +void r_draw_view_border(void); + +/* The span blitting interface. + * Hook in assembler or system specific BLT here. + */ + +void r_draw_column(void); +void r_draw_column_low(void); + +/* The Spectre/Invisibility effect. */ + +void r_draw_fuzz_column(void); +void r_draw_fuzz_column_low(void); + +/* Draw with color translation tables, for player sprite rendering, + * Green/Red/Blue/Indigo shirts. + */ + +void r_draw_translated_column(void); +void r_draw_translated_column_low(void); + +void r_video_erase(unsigned ofs, int count); + +#endif /* __R_DRAW__ */ diff --git a/games/NXDoom/src/doom/r_local.h b/games/NXDoom/src/doom/r_local.h new file mode 100644 index 00000000000..b1fd33e1cee --- /dev/null +++ b/games/NXDoom/src/doom/r_local.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_local.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Refresh (R_*) module, global header. + * All the rendering/drawing stuff is here. + * + ****************************************************************************/ + +#ifndef __R_LOCAL__ +#define __R_LOCAL__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* 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_bsp.h" +#include "r_data.h" +#include "r_draw.h" +#include "r_main.h" +#include "r_plane.h" +#include "r_segs.h" +#include "r_things.h" + +#endif /* __R_LOCAL__ */ diff --git a/games/NXDoom/src/doom/r_main.c b/games/NXDoom/src/doom/r_main.c new file mode 100644 index 00000000000..5eb1a4eeed8 --- /dev/null +++ b/games/NXDoom/src/doom/r_main.c @@ -0,0 +1,881 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_main.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Rendering main loop and setup functions, utility functions (BSP, + * geometry, trigonometry). See tables.c, too. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "d_loop.h" +#include "doomdef.h" + +#include "m_bbox.h" +#include "m_menu.h" + +#include "r_local.h" +#include "r_sky.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Fineangles in the SCREENWIDTH wide window. */ + +#define FIELDOFVIEW 2048 + +#define DISTMAP 2 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +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]; + +lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; +lighttable_t *scalelightfixed[MAXLIGHTSCALE]; +lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; + +/* bumped light from gun blasts */ + +int extralight; + +void (*colfunc)(void); +void (*basecolfunc)(void); +void (*fuzzcolfunc)(void); +void (*transcolfunc)(void); +void (*spanfunc)(void); + +boolean setsizeneeded; +int setblocks; +int setdetail; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_add_point_to_box + * + * Description: + * Expand a given bbox so that it encloses a given point. + * + ****************************************************************************/ + +static void r_add_point_to_box(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; +} + +/**************************************************************************** + * Name: r_setup_frame + ****************************************************************************/ + +static void r_setup_frame(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; + + walllights = scalelightfixed; + + for (i = 0; i < MAXLIGHTSCALE; i++) + scalelightfixed[i] = fixedcolormap; + } + else + fixedcolormap = 0; + + framecount++; + validcount++; +} + +/**************************************************************************** + * Name: r_init_light_tables + * + * Description: + * Only inits the zlight table, because the scalelight table changes with + * view size. + * + ****************************************************************************/ + +static void r_init_light_tables(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 = fixed_div((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; + } + } +} + +/**************************************************************************** + * Name: r_init_tables + ****************************************************************************/ + +static void r_init_tables(void) +{ +#if 0 /* UNUSED: now getting from tables.c */ + int i; + float a; + float fv; + int t; + + /* viewangle tangent table */ + + for (i = 0; i < FINEANGLES / 2; i++) + { + a = (i - FINEANGLES / 4 + 0.5) * PI * 2 / FINEANGLES; + fv = FRACUNIT * tan(a); + t = fv; + finetangent[i] = t; + } + + /* finesine table */ + + for (i = 0; i < 5 * FINEANGLES / 4; i++) + { + /* OPTIMIZE: mirror... */ + + a = (i + 0.5) * PI * 2 / FINEANGLES; + t = FRACUNIT * sin(a); + finesine[i] = t; + } +#endif +} + +/**************************************************************************** + * Name: r_init_point_to_angle + ****************************************************************************/ + +static void r_init_point_to_angle(void) +{ +#if 0 /* UNUSED - now getting from tables.c */ + int i; + long t; + float f; + + /* slope (tangent) to angle lookup */ + + for (i = 0; i <= SLOPERANGE; i++) + { + f = atan((float)i / SLOPERANGE) / (3.141592657 * 2); + t = 0xffffffff * f; + tantoangle[i] = t; + } +#endif +} + +/**************************************************************************** + * Name: r_init_texture_mapping + ****************************************************************************/ + +static void r_init_texture_mapping(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 = + fixed_div(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 = fixed_mul(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 = fixed_mul(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]; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_point_on_side + * + * Description: + * Traverse BSP (sub) tree, check point against partition plane. + * + * Returns: + * Side 0 (front) or 1 (back). + ****************************************************************************/ + +int r_point_on_side(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) + { + return 1; /* (left is negative) */ + } + + return 0; + } + + left = fixed_mul(node->dy >> FRACBITS, dx); + right = fixed_mul(dy, node->dx >> FRACBITS); + + if (right < left) + { + return 0; /* front side */ + } + + return 1; /* back side */ +} + +/**************************************************************************** + * Name: r_point_on_seg_side + ****************************************************************************/ + +int r_point_on_seg_side(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) + { + return 1; /* (left is negative) */ + } + + return 0; + } + + left = fixed_mul(ldy >> FRACBITS, dx); + right = fixed_mul(dy, ldx >> FRACBITS); + + if (right < left) + { + return 0; /* front side */ + } + + return 1; /* back side */ +} + +/**************************************************************************** + * Name: r_point_to_angle + * + * Description: + * 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_point_to_angle(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) + { + return tantoangle[slope_div(y, x)]; /* octant 0 */ + } + else + { + return ANG90 - 1 - tantoangle[slope_div(x, y)]; /* octant 1 */ + } + } + else + { + /* y<0 */ + + y = -y; + + if (x > y) + { + return -tantoangle[slope_div(y, x)]; /* octant 8 */ + } + else + { + return ANG270 + tantoangle[slope_div(x, y)]; /* octant 7 */ + } + } + } + else + { + /* x<0 */ + + x = -x; + + if (y >= 0) + { + /* y>= 0 */ + + if (x > y) + { + return ANG180 - 1 - tantoangle[slope_div(y, x)]; /* octant 3 */ + } + else + { + return ANG90 + tantoangle[slope_div(x, y)]; /* octant 2 */ + } + } + else + { + y = -y; /* y<0 */ + + if (x > y) + { + return ANG180 + tantoangle[slope_div(y, x)]; /* octant 4 */ + } + else + { + return ANG270 - 1 - tantoangle[slope_div(x, y)]; /* octant 5 */ + } + } + } + + return 0; +} + +/**************************************************************************** + * Name: r_point_to_angle2 + ****************************************************************************/ + +angle_t r_point_to_angle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + viewx = x1; + viewy = y1; + + return r_point_to_angle(x2, y2); +} + +/**************************************************************************** + * Name: r_point_to_dist + ****************************************************************************/ + +fixed_t r_point_to_dist(fixed_t x, fixed_t y) +{ + int angle; + fixed_t dx; + fixed_t dy; + fixed_t temp; + fixed_t dist; + fixed_t frac; + + dx = abs(x - viewx); + dy = abs(y - viewy); + + if (dy > dx) + { + temp = dx; + dx = dy; + dy = temp; + } + + /* Fix crashes in udm1.wad */ + + if (dx != 0) + { + frac = fixed_div(dy, dx); + } + else + { + frac = 0; + } + + angle = (tantoangle[frac >> DBITS] + ANG90) >> ANGLETOFINESHIFT; + + dist = fixed_div(dx, finesine[angle]); /* use as cosine */ + + return dist; +} + +/**************************************************************************** + * Name: r_scale_from_global_angle + * + * Description: + * Returns the texture mapping scale for the current line (horizontal span) + * at the given angle. rw_distance must be calculated first. + * + ****************************************************************************/ + +fixed_t r_scale_from_global_angle(angle_t visangle) +{ + fixed_t scale; + angle_t anglea; + angle_t angleb; + int sinea; + int sineb; + fixed_t num; + int den; + +#if 0 /* UNUSED */ + fixed_t dist; + fixed_t z; + fixed_t sinv; + fixed_t cosv; + + sinv = finesine[(visangle - rw_normalangle) >> ANGLETOFINESHIFT]; + dist = fixed_div(rw_distance, sinv); + cosv = finecosine[(viewangle - visangle) >> ANGLETOFINESHIFT]; + z = abs(fixed_mul(dist, cosv)); + scale = fixed_div(projection, z); + return scale; +#endif + + anglea = ANG90 + (visangle - viewangle); + angleb = ANG90 + (visangle - rw_normalangle); + + /* both sines are always positive */ + + sinea = finesine[anglea >> ANGLETOFINESHIFT]; + sineb = finesine[angleb >> ANGLETOFINESHIFT]; + num = fixed_mul(projection, sineb) << detailshift; + den = fixed_mul(rw_distance, sinea); + + if (den > num >> FRACBITS) + { + scale = fixed_div(num, den); + + if (scale > 64 * FRACUNIT) + scale = 64 * FRACUNIT; + else if (scale < 256) + scale = 256; + } + else + scale = 64 * FRACUNIT; + + return scale; +} + +/**************************************************************************** + * Name: r_set_view_size + * + * Description: + * 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_set_view_size(int blocks, int detail) +{ + setsizeneeded = true; + setblocks = blocks; + setdetail = detail; +} + +/**************************************************************************** + * Name: r_execute_set_view_size + ****************************************************************************/ + +void r_execute_set_view_size(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_draw_column; + fuzzcolfunc = r_draw_fuzz_column; + transcolfunc = r_draw_translated_column; + spanfunc = r_draw_span; + } + else + { + colfunc = basecolfunc = r_draw_column_low; + fuzzcolfunc = r_draw_fuzz_column_low; + transcolfunc = r_draw_translated_column_low; + spanfunc = r_draw_span_low; + } + + r_init_buffer(scaledviewwidth, viewheight); + + r_init_texture_mapping(); + + /* 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 = abs(dy); + yslope[i] = fixed_div((viewwidth << detailshift) / 2 * FRACUNIT, dy); + } + + for (i = 0; i < viewwidth; i++) + { + cosadj = abs(finecosine[xtoviewangle[i] >> ANGLETOFINESHIFT]); + distscale[i] = fixed_div(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; + } + } +} + +/**************************************************************************** + * Name: r_init + ****************************************************************************/ + +void r_init(void) +{ + r_init_data(); + printf("."); + r_init_point_to_angle(); + printf("."); + r_init_tables(); + + /* viewwidth / viewheight / g_detail_level are set by the defaults */ + + printf("."); + + r_set_view_size(screenblocks, g_detail_level); + r_init_planes(); + printf("."); + r_init_light_tables(); + printf("."); + r_init_sky_map(); + r_init_translation_table(); + printf("."); + + framecount = 0; +} + +/**************************************************************************** + * Name: r_point_in_subsector + ****************************************************************************/ + +subsector_t *r_point_in_subsector(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_point_on_side(x, y, node); + nodenum = node->children[side]; + } + + return &subsectors[nodenum & ~NF_SUBSECTOR]; +} + +/**************************************************************************** + * Name: r_render_player_view + ****************************************************************************/ + +void r_render_player_view(player_t *player) +{ + r_setup_frame(player); + + /* Clear buffers. */ + + r_clear_clip_segs(); + r_clear_draw_segs(); + r_clear_planes(); + r_clear_sprites(); + + /* check for new console commands. */ + + net_update(); + + /* The head node is the last node output. */ + + r_render_bsp_node(numnodes - 1); + + /* Check for new console commands. */ + + net_update(); + + r_draw_planes(); + + /* Check for new console commands. */ + + net_update(); + + r_draw_masked(); + + /* Check for new console commands. */ + + net_update(); +} diff --git a/games/NXDoom/src/doom/r_main.h b/games/NXDoom/src/doom/r_main.h new file mode 100644 index 00000000000..706ddb6606d --- /dev/null +++ b/games/NXDoom/src/doom/r_main.h @@ -0,0 +1,150 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_main.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __R_MAIN__ +#define __R_MAIN__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_player.h" +#include "r_data.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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 + +/* Number of diminishing brightness levels. + * There a 0-31, i.e. 32 LUT in the COLORMAP lump. + */ + +#define NUMCOLORMAPS 32 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Function pointers to switch refresh/drawing functions. + * Used to select shadow mode etc. + */ + +extern void (*colfunc)(void); +extern void (*transcolfunc)(void); +extern void (*basecolfunc)(void); +extern void (*fuzzcolfunc)(void); + +/* No shadow effects on floors. */ + +extern void (*spanfunc)(void); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* POV related. */ + +extern fixed_t viewcos; +extern fixed_t viewsin; + +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; + +extern boolean setsizeneeded; + +extern lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; +extern lighttable_t *scalelightfixed[MAXLIGHTSCALE]; +extern lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; + +extern int extralight; +extern lighttable_t *fixedcolormap; + +/* Blocky/low detail mode. B remove this? 0 = high, 1 = low + */ + +extern int detailshift; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Utility functions. */ + +int r_point_on_side(fixed_t x, fixed_t y, node_t *node); + +int r_point_on_seg_side(fixed_t x, fixed_t y, seg_t *line); + +angle_t r_point_to_angle(fixed_t x, fixed_t y); + +angle_t r_point_to_angle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); + +fixed_t r_point_to_dist(fixed_t x, fixed_t y); + +fixed_t r_scale_from_global_angle(angle_t visangle); + +subsector_t *r_point_in_subsector(fixed_t x, fixed_t y); + +/* REFRESH - the actual rendering functions. */ + +/* Called by G_Drawer. */ + +void r_render_player_view(player_t *player); + +/* Called by startup code. */ + +void r_init(void); + +/* Called by m_responder. */ + +void r_set_view_size(int blocks, int detail); + +void r_execute_set_view_size(void); + +#endif /* __R_MAIN__ */ diff --git a/games/NXDoom/src/doom/r_plane.c b/games/NXDoom/src/doom/r_plane.c new file mode 100644 index 00000000000..65a2c2fa78b --- /dev/null +++ b/games/NXDoom/src/doom/r_plane.c @@ -0,0 +1,417 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_plane.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "i_system.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "doomdef.h" +#include "doomstat.h" + +#include "r_local.h" +#include "r_sky.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAXOPENINGS (SCREENWIDTH * CONFIG_GAMES_NXDOOM_MAXOPENINGS) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +planefunction_t floorfunc; +planefunction_t ceilingfunc; + +/* opening */ + +/* Here comes the obnoxious "visplane". */ + +visplane_t visplanes[CONFIG_GAMES_NXDOOM_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]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Uses global vars: + * planeheight + * ds_source + * basexscale + * baseyscale + * viewx + * viewy + * + * BASIC PRIMITIVE + */ + +static void r_map_plane(int y, int x1, int x2) +{ + angle_t angle; + fixed_t distance; + fixed_t length; + unsigned index; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > viewheight) + { + i_error("R_MapPlane: %i, %i at %i", x1, x2, y); + } +#endif + + if (planeheight != cachedheight[y]) + { + cachedheight[y] = planeheight; + distance = cacheddistance[y] = fixed_mul(planeheight, yslope[y]); + ds_xstep = cachedxstep[y] = fixed_mul(distance, basexscale); + ds_ystep = cachedystep[y] = fixed_mul(distance, baseyscale); + } + else + { + distance = cacheddistance[y]; + ds_xstep = cachedxstep[y]; + ds_ystep = cachedystep[y]; + } + + length = fixed_mul(distance, distscale[x1]); + angle = (viewangle + xtoviewangle[x1]) >> ANGLETOFINESHIFT; + ds_xfrac = viewx + fixed_mul(finecosine[angle], length); + ds_yfrac = -viewy - fixed_mul(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(); +} + +static void r_make_spans(int x, int t1, int b1, int t2, int b2) +{ + while (t1 < t2 && t1 <= b1) + { + r_map_plane(t1, spanstart[t1], x - 1); + t1++; + } + while (b1 > b2 && b1 >= t1) + { + r_map_plane(b1, spanstart[b1], x - 1); + b1--; + } + + while (t2 < t1 && t2 <= b2) + { + spanstart[t2] = x; + t2++; + } + while (b2 > b1 && b2 >= t2) + { + spanstart[b2] = x; + b2--; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* r_init_planes + * Only at game startup. + */ + +void r_init_planes(void) +{ + /* Doh! */ +} + +/* r_clear_planes + * At beginning of frame. + */ + +void r_clear_planes(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 */ + + memset(cachedheight, 0, sizeof(cachedheight)); + + /* left to right mapping */ + + angle = (viewangle - ANG90) >> ANGLETOFINESHIFT; + + /* scale will be unit scale at SCREENWIDTH/2 distance */ + + basexscale = fixed_div(finecosine[angle], centerxfrac); + baseyscale = -fixed_div(finesine[angle], centerxfrac); +} + +visplane_t *r_find_plane(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 == CONFIG_GAMES_NXDOOM_MAXVISPLANES) + i_error("r_find_plane: no more visplanes"); + + lastvisplane++; + + check->height = height; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->minx = SCREENWIDTH; + check->maxx = -1; + + memset(check->top, 0xff, sizeof(check->top)); + + return check; +} + +visplane_t *r_check_plane(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; + + return pl; /* use the same one */ + } + + /* make a new visplane */ + + lastvisplane->height = pl->height; + lastvisplane->picnum = pl->picnum; + lastvisplane->lightlevel = pl->lightlevel; + + if (lastvisplane - visplanes == CONFIG_GAMES_NXDOOM_MAXVISPLANES) + i_error("r_check_plane: no more visplanes"); + + pl = lastvisplane++; + pl->minx = start; + pl->maxx = stop; + + memset(pl->top, 0xff, sizeof(pl->top)); + + return pl; +} + +/* At the end of each frame. */ + +void r_draw_planes(void) +{ + visplane_t *pl; + int light; + int x; + int stop; + int angle; + int lumpnum; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (ds_p - drawsegs > CONFIG_GAMES_NXDOOM_MAXDRAWSEGS) + i_error("r_draw_planes: drawsegs overflow (%td)", ds_p - drawsegs); + + if (lastvisplane - visplanes > CONFIG_GAMES_NXDOOM_MAXVISPLANES) + i_error("r_draw_planes: visplane overflow (%td)", + lastvisplane - visplanes); + + if (lastopening - openings > MAXOPENINGS) + i_error("r_draw_planes: opening overflow (%td)", lastopening - openings); +#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 always 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_get_column(skytexture, angle); + colfunc(); + } + } + + continue; + } + + /* regular flat */ + + lumpnum = firstflat + flattranslation[pl->picnum]; + ds_source = w_cache_lump_num(lumpnum, PU_STATIC); + + planeheight = 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_make_spans(x, pl->top[x - 1], pl->bottom[x - 1], pl->top[x], + pl->bottom[x]); + } + + w_release_lump_num(lumpnum); + } +} diff --git a/games/NXDoom/src/doom/r_plane.h b/games/NXDoom/src/doom/r_plane.h new file mode 100644 index 00000000000..1ae3b79254b --- /dev/null +++ b/games/NXDoom/src/doom/r_plane.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_plane.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Refresh, visplane stuff (floor, ceilings). + * + ****************************************************************************/ + +#ifndef __R_PLANE__ +#define __R_PLANE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "r_data.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*planefunction_t)(int top, int bottom); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Visplane related. */ + +extern short *lastopening; + +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]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void r_init_planes(void); +void r_clear_planes(void); + +void r_draw_planes(void); + +visplane_t *r_find_plane(fixed_t height, int picnum, int lightlevel); + +visplane_t *r_check_plane(visplane_t *pl, int start, int stop); + +#endif /* __R_PLANE__ */ diff --git a/games/NXDoom/src/doom/r_segs.c b/games/NXDoom/src/doom/r_segs.c new file mode 100644 index 00000000000..83d9b026f45 --- /dev/null +++ b/games/NXDoom/src/doom/r_segs.c @@ -0,0 +1,787 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_segs.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * All the clipping: columns, horizontal spans, sky columns. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "i_system.h" + +#include "doomdef.h" +#include "doomstat.h" + +#include "r_local.h" +#include "r_sky.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HEIGHTBITS 12 +#define HEIGHTUNIT (1 << HEIGHTBITS) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* OPTIMIZE: closed two sided lines as single sided */ + +/* True if any of the segs textures might be visible. */ + +boolean segtextured; + +/* False if the back side is the same plane. */ + +boolean markfloor; +boolean markceiling; + +boolean maskedtexture; +int toptexture; +int bottomtexture; +int midtexture; + +angle_t rw_normalangle; + +/* angle to line origin */ + +int rw_angle1; + +/* regular wall */ + +int rw_x; +int rw_stopx; +angle_t rw_centerangle; +fixed_t rw_offset; +fixed_t rw_distance; +fixed_t rw_scale; +fixed_t rw_scalestep; +fixed_t rw_midtexturemid; +fixed_t rw_toptexturemid; +fixed_t rw_bottomtexturemid; + +int worldtop; +int worldbottom; +int worldhigh; +int worldlow; + +fixed_t pixhigh; +fixed_t pixlow; +fixed_t pixhighstep; +fixed_t pixlowstep; + +fixed_t topfrac; +fixed_t topstep; + +fixed_t bottomfrac; +fixed_t bottomstep; + +lighttable_t **walllights; + +short *maskedtexturecol; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* 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. + */ + +static void r_render_seg_loop(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 - fixed_mul(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; + } + else + { + /* purely to shut up the compiler */ + + texturecolumn = 0; + } + + /* draw the wall tiers */ + + if (midtexture) + { + /* single sided line */ + + dc_yl = yl; + dc_yh = yh; + dc_texturemid = rw_midtexturemid; + dc_source = r_get_column(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_get_column(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_get_column(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; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void r_render_masked_seg_range(drawseg_t *ds, int x1, int x2) +{ + unsigned index; + column_t *col; + int lightnum; + int texnum; + + /* Calculate light table. + * Use different light tables for horizontal / vertical / diagonal. + * Diagonal? OPTIMIZE: get rid of LIGHTSEGSHIFT globally + */ + + curline = ds->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] != SHRT_MAX) + { + if (!fixedcolormap) + { + index = spryscale >> LIGHTSCALESHIFT; + + if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; + + dc_colormap = walllights[index]; + } + + sprtopscreen = centeryfrac - fixed_mul(dc_texturemid, spryscale); + dc_iscale = 0xffffffffu / (unsigned)spryscale; + + /* draw the texture */ + + col = (column_t *)((byte *)r_get_column(texnum, + maskedtexturecol[dc_x]) - + 3); + + r_draw_masked_column(col); + maskedtexturecol[dc_x] = SHRT_MAX; + } + + spryscale += rw_scalestep; + } +} + +/* r_store_wall_range + * A wall segment will be drawn between start and stop pixels (inclusive). + */ + +void r_store_wall_range(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[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS]) return; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (start >= viewwidth || start > stop) + i_error("Bad R_RenderWallRange: %i to %i", start, stop); +#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 = abs((int)rw_normalangle - (int)rw_angle1); + + if (offsetangle > ANG90) offsetangle = ANG90; + + distangle = ANG90 - offsetangle; + hyp = r_point_to_dist(curline->v1->x, curline->v1->y); + sineval = finesine[distangle >> ANGLETOFINESHIFT]; + rw_distance = fixed_mul(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_scale_from_global_angle(viewangle + xtoviewangle[start]); + + if (stop > start) + { + ds_p->scale2 = + r_scale_from_global_angle(viewangle + xtoviewangle[stop]); + ds_p->scalestep = rw_scalestep = + (ds_p->scale2 - rw_scale) / (stop - start); + } + else + { +#if 0 /* UNUSED: try to fix the stretched line bug */ + if (rw_distance < FRACUNIT / 2) + { + fixed_t trx, try; + fixed_t gxt, gyt; + + trx = curline->v1->x - viewx; + try = curline->v1->y - viewy; + + gxt = fixed_mul(trx, viewcos); + gyt = -fixed_mul(try, viewsin); + ds_p->scale1 = fixed_div(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 = NULL; + + 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 = INT_MAX; + ds_p->tsilheight = INT_MIN; + } + else + { + /* two sided line */ + + ds_p->sprtopclip = ds_p->sprbottomclip = NULL; + 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 = INT_MAX; + + /* 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 = INT_MIN; + + /* ds_p->sprtopclip = screenheightarray; */ + } + + if (backsector->ceilingheight <= frontsector->floorheight) + { + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = INT_MAX; + ds_p->silhouette |= SIL_BOTTOM; + } + + if (backsector->floorheight >= frontsector->ceilingheight) + { + ds_p->sprtopclip = screenheightarray; + ds_p->tsilheight = INT_MIN; + 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) offsetangle = -offsetangle; + + if (offsetangle > ANG90) offsetangle = ANG90; + + sineval = finesine[offsetangle >> ANGLETOFINESHIFT]; + rw_offset = fixed_mul(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 = -fixed_mul(rw_scalestep, worldtop); + topfrac = (centeryfrac >> 4) - fixed_mul(worldtop, rw_scale); + + bottomstep = -fixed_mul(rw_scalestep, worldbottom); + bottomfrac = (centeryfrac >> 4) - fixed_mul(worldbottom, rw_scale); + + if (backsector) + { + worldhigh >>= 4; + worldlow >>= 4; + + if (worldhigh < worldtop) + { + pixhigh = (centeryfrac >> 4) - fixed_mul(worldhigh, rw_scale); + pixhighstep = -fixed_mul(rw_scalestep, worldhigh); + } + + if (worldlow > worldbottom) + { + pixlow = (centeryfrac >> 4) - fixed_mul(worldlow, rw_scale); + pixlowstep = -fixed_mul(rw_scalestep, worldlow); + } + } + + /* render it */ + + if (markceiling) + { + ceilingplane = r_check_plane(ceilingplane, rw_x, rw_stopx - 1); + } + + if (markfloor) + { + floorplane = r_check_plane(floorplane, rw_x, rw_stopx - 1); + } + + r_render_seg_loop(); + + /* save sprite clipping info */ + + if (((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip) + { + memcpy(lastopening, ceilingclip + start, + sizeof(*lastopening) * (rw_stopx - start)); + ds_p->sprtopclip = lastopening - start; + lastopening += rw_stopx - start; + } + + if (((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && + !ds_p->sprbottomclip) + { + memcpy(lastopening, floorclip + start, + sizeof(*lastopening) * (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 = INT_MIN; + } + + if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM)) + { + ds_p->silhouette |= SIL_BOTTOM; + ds_p->bsilheight = INT_MAX; + } + + ds_p++; +} diff --git a/games/NXDoom/src/doom/r_segs.h b/games/NXDoom/src/doom/r_segs.h new file mode 100644 index 00000000000..8bd13275f31 --- /dev/null +++ b/games/NXDoom/src/doom/r_segs.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_segs.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Refresh module, drawing LineSegs from BSP. + * + ****************************************************************************/ + +#ifndef __R_SEGS__ +#define __R_SEGS__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern lighttable_t **walllights; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void r_render_masked_seg_range(drawseg_t *ds, int x1, int x2); + +#endif /* __R_SEGS__ */ diff --git a/games/NXDoom/src/doom/r_sky.c b/games/NXDoom/src/doom/r_sky.c new file mode 100644 index 00000000000..d84e6729bd1 --- /dev/null +++ b/games/NXDoom/src/doom/r_sky.c @@ -0,0 +1,68 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_sky.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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? + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Needed for FRACUNIT. */ + +#include "m_fixed.h" + +/* Needed for Flat retrieval. */ + +#include "r_data.h" + +#include "r_sky.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* sky mapping */ + +int skyflatnum; +int skytexture; +int skytexturemid; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_init_sky_map + * + * Description: + * Called whenever the view size changes. + * + ****************************************************************************/ + +void r_init_sky_map(void) +{ + /* skyflatnum = r_flat_num_for_name ( SKYFLATNAME ); */ + + skytexturemid = SCREENHEIGHT / 2 * FRACUNIT; +} diff --git a/games/NXDoom/src/doom/r_sky.h b/games/NXDoom/src/doom/r_sky.h new file mode 100644 index 00000000000..e9455b72e8a --- /dev/null +++ b/games/NXDoom/src/doom/r_sky.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_sky.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Sky rendering. + * + ****************************************************************************/ + +#ifndef __R_SKY__ +#define __R_SKY__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* SKY, store the number for name. */ + +#define SKYFLATNAME "F_SKY1" + +/* The sky map is 256*128*4 maps. */ + +#define ANGLETOSKYSHIFT 22 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int skytexture; +extern int skytexturemid; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Called whenever the view size changes. */ + +void r_init_sky_map(void); + +#endif diff --git a/games/NXDoom/src/doom/r_state.h b/games/NXDoom/src/doom/r_state.h new file mode 100644 index 00000000000..cab81830dea --- /dev/null +++ b/games/NXDoom/src/doom/r_state.h @@ -0,0 +1,130 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_state.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Refresh/render internal state variables (global). + * + ****************************************************************************/ + +#ifndef __R_STATE__ +#define __R_STATE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Need data structure definitions. */ + +#include "d_player.h" +#include "r_data.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* 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 numvertices; +extern vertex_t *vertices; + +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 finetangent[FINEANGLES/2]; */ + +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 /* __R_STATE__ */ diff --git a/games/NXDoom/src/doom/r_things.c b/games/NXDoom/src/doom/r_things.c new file mode 100644 index 00000000000..019d83a7048 --- /dev/null +++ b/games/NXDoom/src/doom/r_things.c @@ -0,0 +1,974 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_things.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Refresh of things, i.e. objects represented by sprites. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_main.h" +#include "doomdef.h" + +#include "i_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "r_local.h" + +#include "doomstat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MINZ (FRACUNIT * 4) +#define BASEYCENTER (SCREENHEIGHT / 2) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + int x1; + int x2; + + int column; + int topclip; + int bottomclip; +} maskdraw_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* 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]; + +/* INITIALIZATION FUNCTIONS */ + +/* variables used to look up and range check thing_t sprites patches */ + +spritedef_t *sprites; +int numsprites; + +spriteframe_t sprtemp[29]; +int maxframe; +const char *spritename; + +vissprite_t vissprites[CONFIG_GAMES_NXDOOM_MAXVISSPRITES]; +vissprite_t *vissprite_p; +int newvissprite; + +vissprite_t overflowsprite; + +short *mfloorclip; +short *mceilingclip; + +fixed_t spryscale; +fixed_t sprtopscreen; + +vissprite_t vsprsortedhead; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* R_InstallSpriteLump + * Local function for r_init_sprites. + */ + +static void r_install_sprite_lump(int lump, unsigned frame, + unsigned rotation, boolean flipped) +{ + int r; + + if (frame >= 29 || rotation > 8) + i_error("R_InstallSpriteLump: " + "Bad frame characters in lump %i", + lump); + + if ((int)frame > maxframe) maxframe = frame; + + if (rotation == 0) + { + /* the lump should be used for all rotations */ + + if (sprtemp[frame].rotate == false) + i_error("r_init_sprites: Sprite %s frame %c has " + "multip rot=0 lump", + spritename, 'A' + frame); + + if (sprtemp[frame].rotate == true) + i_error("r_init_sprites: Sprite %s frame %c has rotations " + "and a rot=0 lump", + spritename, 'A' + frame); + + 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("r_init_sprites: Sprite %s frame %c has rotations " + "and a rot=0 lump", + spritename, 'A' + frame); + + sprtemp[frame].rotate = true; + + /* make 0 based */ + + rotation--; + if (sprtemp[frame].lump[rotation] != -1) + i_error("r_init_sprites: Sprite %s : %c : %c " + "has two lumps mapped to it", + spritename, 'A' + frame, '1' + rotation); + + sprtemp[frame].lump[rotation] = lump - firstspritelump; + sprtemp[frame].flip[rotation] = (byte)flipped; +} + +/* r_init_sprite_defs + * + * 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 inconsistent. 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. + */ + +static void r_init_sprite_defs(const char **namelist) +{ + const char **check; + int i; + int l; + int frame; + int rotation; + int start; + int end; + int patched; + + /* count the number of sprite names */ + + check = namelist; + while (*check != NULL) + check++; + + numsprites = check - namelist; + + if (!numsprites) return; + + sprites = z_malloc(numsprites * sizeof(*sprites), PU_STATIC, NULL); + + 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]); + memset(sprtemp, -1, sizeof(sprtemp)); + + maxframe = -1; + + /* scan the lumps, filling in the frames for whatever is found + */ + + for (l = start + 1; l < end; l++) + { + if (!strncasecmp(lumpinfo[l]->name, spritename, 4)) + { + frame = lumpinfo[l]->name[4] - 'A'; + rotation = lumpinfo[l]->name[5] - '0'; + + if (modifiedgame) + patched = w_get_num_for_name(lumpinfo[l]->name); + else + patched = l; + + r_install_sprite_lump(patched, frame, rotation, false); + + if (lumpinfo[l]->name[6]) + { + frame = lumpinfo[l]->name[6] - 'A'; + rotation = lumpinfo[l]->name[7] - '0'; + r_install_sprite_lump(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("r_init_sprites: No patches found " + "for %s frame %c", + spritename, frame + 'A'); + break; + + case 0: + break; /* only the first rotation is needed */ + + case 1: + + /* must have all 8 frames */ + + for (rotation = 0; rotation < 8; rotation++) + if (sprtemp[frame].lump[rotation] == -1) + i_error("r_init_sprites: Sprite %s frame %c " + "is missing rotations", + spritename, frame + 'A'); + 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, NULL); + memcpy(sprites[i].spriteframes, sprtemp, + maxframe * sizeof(spriteframe_t)); + } +} + +static vissprite_t *r_new_vis_sprite(void) +{ + if (vissprite_p == &vissprites[CONFIG_GAMES_NXDOOM_MAXVISSPRITES]) + { + return &overflowsprite; + } + + vissprite_p++; + return vissprite_p - 1; +} + +/* R_DrawVisSprite + * mfloorclip and mceilingclip should also be set. + */ + +static void r_draw_vis_sprite(vissprite_t *vis, int x1, int x2) +{ + column_t *column; + int texturecolumn; + fixed_t frac; + patch_t *patch; + + patch = w_cache_lump_num(vis->patch + firstspritelump, PU_CACHE); + + dc_colormap = vis->colormap; + + if (!dc_colormap) + { + /* NULL colormap = shadow draw */ + + colfunc = fuzzcolfunc; + } + else if (vis->mobjflags & MF_TRANSLATION) + { + colfunc = transcolfunc; + dc_translation = + translationtables - 256 + + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8)); + } + + dc_iscale = abs(vis->xiscale) >> detailshift; + dc_texturemid = vis->texturemid; + frac = vis->startfrac; + spryscale = vis->scale; + sprtopscreen = centeryfrac - fixed_mul(dc_texturemid, spryscale); + + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) + { + texturecolumn = frac >> FRACBITS; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) + { + i_error("R_DrawSpriteRange: bad texturecolumn"); + } + +#endif + + column = (column_t *)((byte *)patch + + LONG(patch->columnofs[texturecolumn])); + r_draw_masked_column(column); + } + + colfunc = basecolfunc; +} + +/* R_ProjectSprite + * Generates a vissprite for a thing if it might be visible. + */ + +static void r_project_sprite(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; + 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 = fixed_mul(tr_x, viewcos); + gyt = -fixed_mul(tr_y, viewsin); + + tz = gxt - gyt; + + /* thing is behind view plane? */ + + if (tz < MINZ) return; + + xscale = fixed_div(projection, tz); + + gxt = -fixed_mul(tr_x, viewsin); + gyt = fixed_mul(tr_y, viewcos); + tx = -(gyt + gxt); + + /* too far off the side? */ + + if (abs(tx) > (tz << 2)) return; + + /* decide which patch to use for sprite relative to player */ + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned int)thing->sprite >= (unsigned int)numsprites) + i_error("R_ProjectSprite: invalid sprite number %i ", thing->sprite); +#endif + sprdef = &sprites[thing->sprite]; +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((thing->frame & FF_FRAMEMASK) >= sprdef->numframes) + i_error("R_ProjectSprite: invalid sprite frame %i : %i ", thing->sprite, + thing->frame); +#endif + sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; + + if (sprframe->rotate) + { + /* choose a different rotation based on player view */ + + ang = r_point_to_angle(thing->x, thing->y); + rot = (ang - thing->angle + (unsigned)(ANG45 / 2) * 9) >> 29; + lump = sprframe->lump[rot]; + flip = (boolean)sprframe->flip[rot]; + } + else + { + /* use single rotation for all views */ + + lump = sprframe->lump[0]; + flip = (boolean)sprframe->flip[0]; + } + + /* calculate edges of the shape */ + + tx -= spriteoffset[lump]; + x1 = (centerxfrac + fixed_mul(tx, xscale)) >> FRACBITS; + + /* off the right side? */ + + if (x1 > viewwidth) return; + + tx += spritewidth[lump]; + x2 = ((centerxfrac + fixed_mul(tx, xscale)) >> FRACBITS) - 1; + + /* off the left side */ + + if (x2 < 0) return; + + /* store information in a vissprite */ + + vis = r_new_vis_sprite(); + 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 = fixed_div(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 = NULL; + } + 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_DrawPSprite */ + +static void r_draw_psprite(pspdef_t *psp) +{ + fixed_t tx; + int x1; + int x2; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + boolean flip; + vissprite_t *vis; + vissprite_t avis; + + /* decide which patch to use */ + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)psp->state->sprite >= (unsigned int)numsprites) + { + i_error("R_ProjectSprite: invalid sprite number %i ", + psp->state->sprite); + } + +#endif + + sprdef = &sprites[psp->state->sprite]; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes) + { + i_error("R_ProjectSprite: invalid sprite frame %i : %i ", + psp->state->sprite, psp->state->frame); + } + +#endif + sprframe = &sprdef->spriteframes[psp->state->frame & FF_FRAMEMASK]; + + lump = sprframe->lump[0]; + flip = (boolean)sprframe->flip[0]; + + /* calculate edges of the shape */ + + tx = psp->sx - (SCREENWIDTH / 2) * FRACUNIT; + + tx -= spriteoffset[lump]; + x1 = (centerxfrac + fixed_mul(tx, pspritescale)) >> FRACBITS; + + /* off the right side */ + + if (x1 > viewwidth) return; + + tx += spritewidth[lump]; + x2 = ((centerxfrac + fixed_mul(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) + { + vis->colormap = NULL; /* shadow draw */ + } + else if (fixedcolormap) + { + vis->colormap = fixedcolormap; /* fixed color */ + } + else if (psp->state->frame & FF_FULLBRIGHT) + { + vis->colormap = colormaps; /* full bright */ + } + else + { + vis->colormap = spritelights[MAXLIGHTSCALE - 1]; /* local light */ + } + + r_draw_vis_sprite(vis, vis->x1, vis->x2); +} + +static void r_draw_player_sprites(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_draw_psprite(psp); + } +} + +static void r_sort_vis_sprites(void) +{ + int i; + int count; + vissprite_t *ds; + vissprite_t *best; + static vissprite_t unsorted; + fixed_t bestscale; + + count = 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 */ + + vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; + for (i = 0; i < count; i++) + { + bestscale = INT_MAX; + best = unsorted.next; + + 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; + } +} + +static void r_draw_sprite(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)) + { + continue; /* does not cover sprite */ + } + + 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_point_on_seg_side(spr->gx, spr->gy, ds->curline))) + { + /* masked mid texture? */ + + if (ds->maskedtexturecol) r_render_masked_seg_range(ds, r1, r2); + continue; /* seg is behind sprite */ + } + + /* 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_draw_vis_sprite(spr, spr->x1, spr->x2); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* r_init_sprites + * Called at program start. + */ + +void r_init_sprites(const char **namelist) +{ + int i; + + for (i = 0; i < SCREENWIDTH; i++) + { + negonearray[i] = -1; + } + + r_init_sprite_defs(namelist); +} + +/* Called at frame start. */ + +void r_clear_sprites(void) +{ + vissprite_p = vissprites; +} + +/* r_draw_masked_column + * Used for sprites and masked mid textures. + * Masked means: partly transparent, i.e. stored in posts/runs of opaque + * pixels. + */ + +void r_draw_masked_column(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); + + /* Drawn by either r_draw_column or (SHADOW) r_draw_fuzz_column. + */ + + colfunc(); + } + + column = (column_t *)((byte *)column + column->length + 4); + } + + dc_texturemid = basetexturemid; +} + +/* r_add_sprites + * During BSP traversal, this adds sprites by sector. + */ + +void r_add_sprites(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_project_sprite(thing); +} + +void r_draw_masked(void) +{ + vissprite_t *spr; + drawseg_t *ds; + + r_sort_vis_sprites(); + + if (vissprite_p > vissprites) + { + /* draw all vissprites back to front */ + + for (spr = vsprsortedhead.next; + spr != &vsprsortedhead; + spr = spr->next) + { + r_draw_sprite(spr); + } + } + + /* render any remaining masked mid textures */ + + for (ds = ds_p - 1; ds >= drawsegs; ds--) + { + if (ds->maskedtexturecol) + { + r_render_masked_seg_range(ds, ds->x1, ds->x2); + } + } + + /* draw the psprites on top of everything but does not draw on side views + */ + + if (!viewangleoffset) + { + r_draw_player_sprites(); + } +} diff --git a/games/NXDoom/src/doom/r_things.h b/games/NXDoom/src/doom/r_things.h new file mode 100644 index 00000000000..cdd29948368 --- /dev/null +++ b/games/NXDoom/src/doom/r_things.h @@ -0,0 +1,61 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_things.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Rendering of moving objects, sprites. + * + ****************************************************************************/ + +#ifndef __R_THINGS__ +#define __R_THINGS__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern vissprite_t vissprites[CONFIG_GAMES_NXDOOM_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_draw_masked_column */ + +extern short *mfloorclip; +extern short *mceilingclip; +extern fixed_t spryscale; +extern fixed_t sprtopscreen; + +extern fixed_t pspritescale; +extern fixed_t pspriteiscale; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void r_draw_masked_column(column_t *column); + +void r_add_sprites(sector_t *sec); +void r_init_sprites(const char **namelist); +void r_clear_sprites(void); +void r_draw_masked(void); + +#endif /* __R_THINGS__ */ diff --git a/games/NXDoom/src/doom/s_sound.c b/games/NXDoom/src/doom/s_sound.c new file mode 100644 index 00000000000..dbca08a5748 --- /dev/null +++ b/games/NXDoom/src/doom/s_sound.c @@ -0,0 +1,771 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/s_sound.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: none + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "i_sound.h" +#include "i_system.h" + +#include "deh_str.h" + +#include "doomstat.h" +#include "doomtype.h" + +#include "s_sound.h" +#include "sounds.h" + +#include "m_argv.h" +#include "m_misc.h" +#include "m_random.h" + +#include "p_local.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* when to clip out sounds + * Does not fit the large outdoor areas. + */ + +#define S_CLIPPING_DIST (1200 * FRACUNIT) + +/* Distance tp origin when sounds should be maxed out. + * This should relate to movement clipping resolution + * (see BLOCKMAP handling). + * In the source code release: (160*FRACUNIT). Changed back to the + * Vanilla value of 200 (why was this changed?) + */ + +#define S_CLOSE_DIST (200 * FRACUNIT) + +/* The range over which sound attenuates */ + +#define S_ATTENUATOR ((S_CLIPPING_DIST - S_CLOSE_DIST) >> FRACBITS) + +/* Stereo separation */ + +#define S_STEREO_SWING (96 * FRACUNIT) + +#define NORM_PRIORITY 64 +#define NORM_SEP 128 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + /* sound information (if null, channel avail.) */ + + sfxinfo_t *sfxinfo; + + /* origin of sound */ + + mobj_t *origin; + + /* handle of the sound being played */ + + int handle; + + int pitch; +} channel_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The set of channels available */ + +static channel_t *channels; + +/* Internal volume level, ranging from 0-127 */ + +static int g_snd_sfx_volume; + +/* Whether songs are mus_paused */ + +static boolean mus_paused; + +/* Music currently being played */ + +static musicinfo_t *mus_playing = NULL; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Maximum volume of a sound effect. + * Internal default is max out of 0-15. + */ + +int g_sfx_volume = 8; + +/* Maximum volume of music. */ + +int g_music_volume = 8; + +/* Number of channels to use */ + +int snd_channels = 8; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void s_stop_channel(int cnum) +{ + int i; + channel_t *c; + + c = &channels[cnum]; + + if (c->sfxinfo) + { + /* stop the sound playing */ + + if (i_sound_playing(c->handle)) + { + i_stop_sound(c->handle); + } + + /* check to see if other channels are playing the sound */ + + for (i = 0; i < snd_channels; i++) + { + if (cnum != i && c->sfxinfo == channels[i].sfxinfo) + { + break; + } + } + + /* degrade usefulness of sound data */ + + c->sfxinfo->usefulness--; + c->sfxinfo = NULL; + c->origin = NULL; + } +} + +/* S_GetChannel: If none available, return -1. Otherwise channel #. */ + +static int s_get_channel(mobj_t *origin, sfxinfo_t *sfxinfo) +{ + /* channel number to use */ + + int cnum; + + channel_t *c; + + /* Find an open channel */ + + for (cnum = 0; cnum < snd_channels; cnum++) + { + if (!channels[cnum].sfxinfo) + { + break; + } + else if (origin && channels[cnum].origin == origin) + { + s_stop_channel(cnum); + break; + } + } + + /* None available */ + + if (cnum == snd_channels) + { + /* Look for lower priority */ + + for (cnum = 0; cnum < snd_channels; cnum++) + { + if (channels[cnum].sfxinfo->priority >= sfxinfo->priority) + { + break; + } + } + + if (cnum == snd_channels) + { + /* FUCK! No lower priority. Sorry, Charlie. */ + + return -1; + } + else + { + /* Otherwise, kick out lower priority. */ + + s_stop_channel(cnum); + } + } + + c = &channels[cnum]; + + /* channel is decided to be cnum. */ + + c->sfxinfo = sfxinfo; + c->origin = origin; + + return cnum; +} + +/* Changes volume and stereo-separation 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. + */ + +static int s_adjust_sound_params(mobj_t *listener, mobj_t *source, int *vol, + int *sep) +{ + 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 = abs(listener->x - source->x); + ady = abs(listener->y - source->y); + + /* From _GG1_ p.428. Appox. Euclidean 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_point_to_angle2(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 - (fixed_mul(S_STEREO_SWING, finesine[angle]) >> FRACBITS); + + /* volume calculation */ + + if (approx_dist < S_CLOSE_DIST) + { + *vol = g_snd_sfx_volume; + } + else if (gamemap == 8) + { + if (approx_dist > S_CLIPPING_DIST) + { + approx_dist = S_CLIPPING_DIST; + } + + *vol = 15 + ((g_snd_sfx_volume - 15) * + ((S_CLIPPING_DIST - approx_dist) >> FRACBITS)) / + S_ATTENUATOR; + } + else + { + /* distance effect */ + + *vol = (g_snd_sfx_volume * + ((S_CLIPPING_DIST - approx_dist) >> FRACBITS) + ) / S_ATTENUATOR; + } + + return (*vol > 0); +} + +/* clamp supplied integer to the range 0 <= x <= 255. */ + +static int clamp(int x) +{ + if (x < 0) + { + return 0; + } + else if (x > 255) + { + return 255; + } + + return x; +} + +static void s_shutdown(void) +{ + i_shutdown_sound(); + i_shutdown_music(); +} + +static void s_stop_music(void) +{ + if (mus_playing) + { + if (mus_paused) + { + i_resume_song(); + } + + i_stop_song(); + i_unregister_song(mus_playing->handle); + w_release_lump_num(mus_playing->lumpnum); + mus_playing->data = NULL; + mus_playing = NULL; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* 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; + +#if 0 + if (gameversion == exe_doom_1_666) + { + if (logical_gamemission == doom) + { + i_set_opl_driver_ver(opl_doom1_1_666); + } + else + { + i_set_opl_driver_ver(opl_doom2_1_666); + } + } + else + { + i_set_opl_driver_ver(opl_doom_1_9); + } +#endif + + i_precache_sounds(s_sfx, SFX_NUMSFX); + + s_set_sfx_volume(sfxvolume); + s_set_music_volume(musicvolume); + + /* Allocating the internal channels for mixing + * (the maximum number of sounds rendered + * simultaneously) within zone memory. + */ + + channels = z_malloc(snd_channels * sizeof(channel_t), PU_STATIC, 0); + + /* Free all channels for use */ + + for (i = 0; i < snd_channels; i++) + { + channels[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 < SFX_NUMSFX; i++) + { + s_sfx[i].lumpnum = s_sfx[i].usefulness = -1; + } + + /* Doom defaults to pitch-shifting off. */ + + if (snd_pitchshift == -1) + { + snd_pitchshift = 0; + } + + i_at_exit(s_shutdown, true); +} + +/* 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 < snd_channels; cnum++) + { + if (channels[cnum].sfxinfo) + { + s_stop_channel(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]; + } + } + + s_change_music(mnum, true); +} + +void s_stop_sound(mobj_t *origin) +{ + int cnum; + + for (cnum = 0; cnum < snd_channels; cnum++) + { + if (channels[cnum].sfxinfo && channels[cnum].origin == origin) + { + s_stop_channel(cnum); + break; + } + } +} + +void s_start_sound(void *origin_p, int sfx_id) +{ + sfxinfo_t *sfx; + mobj_t *origin; + int rc; + int sep; + int pitch; + int cnum; + int volume; + + origin = (mobj_t *)origin_p; + volume = g_snd_sfx_volume; + + /* check for bogus sound # */ + + if (sfx_id < 1 || sfx_id > SFX_NUMSFX) + { + i_error("Bad sfx #: %d", sfx_id); + } + + sfx = &s_sfx[sfx_id]; + + /* Initialize sound parameters */ + + pitch = NORM_PITCH; + if (sfx->link) + { + volume += sfx->volume; + pitch = sfx->pitch; + + if (volume < 1) + { + return; + } + + if (volume > g_snd_sfx_volume) + { + volume = g_snd_sfx_volume; + } + } + + /* Check to see if it is audible, and if not, modify the params */ + + if (origin && origin != players[consoleplayer].mo) + { + rc = s_adjust_sound_params(players[consoleplayer].mo, origin, &volume, + &sep); + + 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); + } + else if (sfx_id != SFX_ITEMUP && sfx_id != SFX_TINK) + { + pitch += 16 - (m_random() & 31); + } + + pitch = clamp(pitch); + + s_stop_sound(origin); /* kill old sound */ + + /* try to find a channel */ + + cnum = s_get_channel(origin, sfx); + + if (cnum < 0) + { + return; + } + + /* increase the usefulness */ + + if (sfx->usefulness++ < 0) + { + sfx->usefulness = 1; + } + + if (sfx->lumpnum < 0) + { + sfx->lumpnum = i_get_sfx_lumpnum(sfx); + } + + channels[cnum].pitch = pitch; + channels[cnum].handle = + i_start_sound(sfx, cnum, volume, sep, channels[cnum].pitch); +} + +/* Stop and resume music, during game PAUSE. */ + +void s_pause_sound(void) +{ + if (mus_playing && !mus_paused) + { + i_pause_song(); + mus_paused = true; + } +} + +void s_resume_sound(void) +{ + if (mus_playing && mus_paused) + { + i_resume_song(); + mus_paused = false; + } +} + +/* Updates music & sounds */ + +void s_update_sounds(mobj_t *listener) +{ + int audible; + int cnum; + int volume; + int sep; + sfxinfo_t *sfx; + channel_t *c; + + i_update_sound(); + + for (cnum = 0; cnum < snd_channels; cnum++) + { + c = &channels[cnum]; + sfx = c->sfxinfo; + + if (c->sfxinfo) + { + if (i_sound_playing(c->handle)) + { + /* initialize parameters */ + + volume = g_snd_sfx_volume; + sep = NORM_SEP; + + if (sfx->link) + { + volume += sfx->volume; + if (volume < 1) + { + s_stop_channel(cnum); + continue; + } + else if (volume > g_snd_sfx_volume) + { + volume = g_snd_sfx_volume; + } + } + + /* check non-local sounds for distance clipping or modify their + * params + */ + + if (c->origin && listener != c->origin) + { + audible = s_adjust_sound_params(listener, c->origin, + &volume, &sep); + + if (!audible) + { + s_stop_channel(cnum); + } + else + { + i_update_sound_params(c->handle, volume, sep); + } + } + } + else + { + /* if channel is allocated but sound has stopped, free it + */ + + s_stop_channel(cnum); + } + } + } +} + +void s_set_music_volume(int volume) +{ + if (volume < 0 || volume > 127) + { + i_error("Attempt to set music volume at %d", volume); + } + + i_set_music_volume(volume); +} + +void s_set_sfx_volume(int volume) +{ + if (volume < 0 || volume > 127) + { + i_error("Attempt to set sfx volume at %d", volume); + } + + g_snd_sfx_volume = volume; +} + +/* Starts some music with the music id found in sounds.h. */ + +void s_start_music(int m_id) +{ + s_change_music(m_id, false); +} + +void s_change_music(int musicnum, int looping) +{ + musicinfo_t *music = NULL; + char namebuf[9]; + void *handle; + + /* The Doom IWAD file has two versions of the intro music: d_intro + * and d_introa. The latter is used for OPL playback. + */ + + if (musicnum == MUS_INTRO && + (snd_musicdevice == SNDDEVICE_ADLIB || + snd_musicdevice == SNDDEVICE_SB) && + w_check_num_for_name("D_INTROA") >= 0) + { + musicnum = MUS_INTROA; + } + + if (musicnum <= MUS_NONE || musicnum >= MUS_NUMMUSIC) + { + i_error("Bad music number %d", musicnum); + } + else + { + music = &s_music[musicnum]; + } + + if (mus_playing == music) + { + return; + } + + /* shutdown old music */ + + s_stop_music(); + + /* get lumpnum if necessary */ + + if (!music->lumpnum) + { + snprintf(namebuf, sizeof(namebuf), "d_%s", (music->name)); + music->lumpnum = w_get_num_for_name(namebuf); + } + + music->data = w_cache_lump_num(music->lumpnum, PU_STATIC); + + handle = i_register_song(music->data, w_lump_length(music->lumpnum)); + music->handle = handle; + i_play_song(handle, looping); + + mus_playing = music; +} diff --git a/games/NXDoom/src/doom/s_sound.h b/games/NXDoom/src/doom/s_sound.h new file mode 100644 index 00000000000..21f569d9b2b --- /dev/null +++ b/games/NXDoom/src/doom/s_sound.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/s_sound.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * The not so system specific sound interface. + * + ****************************************************************************/ + +#ifndef __S_SOUND__ +#define __S_SOUND__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "p_mobj.h" +#include "sounds.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int snd_channels; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* 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_start_sound(void *origin, int sound_id); + +/* Stop sound for thing at */ + +void s_stop_sound(mobj_t *origin); + +/* Start music using from sounds.h */ + +void s_start_music(int music_id); + +/* Start music using from sounds.h, and set whether looping */ + +void s_change_music(int music_id, int looping); + +/* Stop and resume music, during game PAUSE. */ + +void s_pause_sound(void); +void s_resume_sound(void); + +/* Updates music & sounds */ + +void s_update_sounds(mobj_t *listener); + +void s_set_music_volume(int volume); +void s_set_sfx_volume(int volume); + +#endif /* __S_SOUND__ */ diff --git a/games/NXDoom/src/doom/sounds.c b/games/NXDoom/src/doom/sounds.c new file mode 100644 index 00000000000..b1282cdb28f --- /dev/null +++ b/games/NXDoom/src/doom/sounds.c @@ -0,0 +1,189 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/sounds.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Created by a sound utility. + * Kept as a sample, DOOM2 sounds. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" +#include "sounds.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Information about all the music */ + +#define MUSIC(name) {name, 0, NULL, NULL} + +/* Information about all the sfx */ + +#define SOUND(name, priority) \ + {NULL, name, priority, NULL, -1, -1, 0, 0, -1, NULL} +#define SOUND_LINK(name, priority, link_id, pitch, volume) \ + {NULL, name, priority, &s_sfx[link_id], pitch, volume, 0, 0, -1, NULL} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +musicinfo_t s_music[] = +{ + MUSIC(NULL), MUSIC("e1m1"), MUSIC("e1m2"), MUSIC("e1m3"), + MUSIC("e1m4"), MUSIC("e1m5"), MUSIC("e1m6"), MUSIC("e1m7"), + MUSIC("e1m8"), MUSIC("e1m9"), MUSIC("e2m1"), MUSIC("e2m2"), + MUSIC("e2m3"), MUSIC("e2m4"), MUSIC("e2m5"), MUSIC("e2m6"), + MUSIC("e2m7"), MUSIC("e2m8"), MUSIC("e2m9"), MUSIC("e3m1"), + MUSIC("e3m2"), MUSIC("e3m3"), MUSIC("e3m4"), MUSIC("e3m5"), + MUSIC("e3m6"), MUSIC("e3m7"), MUSIC("e3m8"), MUSIC("e3m9"), + MUSIC("inter"), MUSIC("intro"), MUSIC("bunny"), MUSIC("victor"), + MUSIC("introa"), MUSIC("runnin"), MUSIC("stalks"), MUSIC("countd"), + MUSIC("betwee"), MUSIC("doom"), MUSIC("the_da"), MUSIC("shawn"), + MUSIC("ddtblu"), MUSIC("in_cit"), MUSIC("dead"), MUSIC("stlks2"), + MUSIC("theda2"), MUSIC("doom2"), MUSIC("ddtbl2"), MUSIC("runni2"), + MUSIC("dead2"), MUSIC("stlks3"), MUSIC("romero"), MUSIC("shawn2"), + MUSIC("messag"), MUSIC("count2"), MUSIC("ddtbl3"), MUSIC("ampie"), + MUSIC("theda3"), MUSIC("adrian"), MUSIC("messg2"), MUSIC("romer2"), + MUSIC("tense"), MUSIC("shawn3"), MUSIC("openin"), MUSIC("evil"), + MUSIC("ultima"), MUSIC("read_m"), MUSIC("dm2ttl"), MUSIC("dm2int"), +}; + +sfxinfo_t s_sfx[] = +{ + SOUND("none", 0), /* s_sfx[0] needs to be a dummy for odd reasons. */ + SOUND("pistol", 64), + SOUND("shotgn", 64), + SOUND("sgcock", 64), + SOUND("dshtgn", 64), + SOUND("dbopn", 64), + SOUND("dbcls", 64), + SOUND("dbload", 64), + SOUND("plasma", 64), + SOUND("bfg", 64), + SOUND("sawup", 64), + SOUND("sawidl", 118), + SOUND("sawful", 64), + SOUND("sawhit", 64), + SOUND("rlaunc", 64), + SOUND("rxplod", 70), + SOUND("firsht", 70), + SOUND("firxpl", 70), + SOUND("pstart", 100), + SOUND("pstop", 100), + SOUND("doropn", 100), + SOUND("dorcls", 100), + SOUND("stnmov", 119), + SOUND("swtchn", 78), + SOUND("swtchx", 78), + SOUND("plpain", 96), + SOUND("dmpain", 96), + SOUND("popain", 96), + SOUND("vipain", 96), + SOUND("mnpain", 96), + SOUND("pepain", 96), + SOUND("slop", 78), + SOUND("itemup", 78), + SOUND("wpnup", 78), + SOUND("oof", 96), + SOUND("telept", 32), + SOUND("posit1", 98), + SOUND("posit2", 98), + SOUND("posit3", 98), + SOUND("bgsit1", 98), + SOUND("bgsit2", 98), + SOUND("sgtsit", 98), + SOUND("cacsit", 98), + SOUND("brssit", 94), + SOUND("cybsit", 92), + SOUND("spisit", 90), + SOUND("bspsit", 90), + SOUND("kntsit", 90), + SOUND("vilsit", 90), + SOUND("mansit", 90), + SOUND("pesit", 90), + SOUND("sklatk", 70), + SOUND("sgtatk", 70), + SOUND("skepch", 70), + SOUND("vilatk", 70), + SOUND("claw", 70), + SOUND("skeswg", 70), + SOUND("pldeth", 32), + SOUND("pdiehi", 32), + SOUND("podth1", 70), + SOUND("podth2", 70), + SOUND("podth3", 70), + SOUND("bgdth1", 70), + SOUND("bgdth2", 70), + SOUND("sgtdth", 70), + SOUND("cacdth", 70), + SOUND("skldth", 70), + SOUND("brsdth", 32), + SOUND("cybdth", 32), + SOUND("spidth", 32), + SOUND("bspdth", 32), + SOUND("vildth", 32), + SOUND("kntdth", 32), + SOUND("pedth", 32), + SOUND("skedth", 32), + SOUND("posact", 120), + SOUND("bgact", 120), + SOUND("dmact", 120), + SOUND("bspact", 100), + SOUND("bspwlk", 100), + SOUND("vilact", 100), + SOUND("noway", 78), + SOUND("barexp", 60), + SOUND("punch", 64), + SOUND("hoof", 70), + SOUND("metal", 70), + SOUND_LINK("chgun", 64, SFX_PISTOL, 150, 0), + SOUND("tink", 60), + SOUND("bdopn", 100), + SOUND("bdcls", 100), + SOUND("itmbk", 100), + SOUND("flame", 32), + SOUND("flamst", 32), + SOUND("getpow", 60), + SOUND("bospit", 70), + SOUND("boscub", 70), + SOUND("bossit", 70), + SOUND("bospn", 70), + SOUND("bosdth", 70), + SOUND("manatk", 70), + SOUND("mandth", 70), + SOUND("sssit", 70), + SOUND("ssdth", 70), + SOUND("keenpn", 70), + SOUND("keendt", 70), + SOUND("skeact", 70), + SOUND("skesit", 70), + SOUND("skeatk", 70), + SOUND("radio", 60), +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/sounds.h b/games/NXDoom/src/doom/sounds.h new file mode 100644 index 00000000000..a2d9b371fe0 --- /dev/null +++ b/games/NXDoom/src/doom/sounds.h @@ -0,0 +1,241 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/sounds.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Created by the sound utility written by Dave Taylor. + * Kept as a sample, DOOM2 sounds. Frozen. + * + ****************************************************************************/ + +#ifndef __SOUNDS__ +#define __SOUNDS__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "i_sound.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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, + MUS_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, + SFX_NUMSFX +} sfxenum_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* the complete set of sound effects */ + +extern sfxinfo_t s_sfx[]; + +/* the complete set of music */ + +extern musicinfo_t s_music[]; + +#endif /* __SOUNDS__ */ diff --git a/games/NXDoom/src/doom/st_lib.c b/games/NXDoom/src/doom/st_lib.c new file mode 100644 index 00000000000..d8aeda710a4 --- /dev/null +++ b/games/NXDoom/src/doom/st_lib.c @@ -0,0 +1,238 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/st_lib.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * The status bar widget code. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_main.h" +#include "doomdef.h" + +#include "v_video.h" +#include "z_zone.h" + +#include "i_swap.h" +#include "i_system.h" + +#include "w_wad.h" + +#include "r_local.h" +#include "st_lib.h" +#include "st_stuff.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Hack display negative frags. Loads and store the stminus lump. */ + +patch_t *sttminus; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stlib_draw_num + * + * Description: + * A fairly efficient way to draw a number based on differences from the old + * number. Note: worth the trouble? + * + ****************************************************************************/ + +static void stlib_draw_num(st_number_t *n, 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("drawNum: n->y - ST_Y < 0"); + + v_copy_rect(x, n->y - ST_Y, st_backing_screen, w * numdigits, h, x, n->y); + + if (num == 1994) return; /* if non-number, do not draw it */ + + x = n->x; + + /* in the special case of 0, you draw 0 */ + + if (!num) v_draw_patch(x - w, n->y, n->p[0]); + + /* draw the new number */ + + while (num && numdigits--) + { + x -= w; + v_draw_patch(x, n->y, n->p[num % 10]); + num /= 10; + } + + /* draw a minus sign if necessary */ + + if (neg && sttminus) v_draw_patch(x - 8, n->y, sttminus); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void stlib_init(void) +{ + if (w_check_num_for_name(("STTMINUS")) >= 0) + sttminus = (patch_t *)w_cache_lump_name(("STTMINUS"), PU_STATIC); + else + sttminus = NULL; +} + +/* ? */ + +void stlib_init_num(st_number_t *n, int x, int y, patch_t **pl, int *num, + 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; +} + +void stlib_update_num(st_number_t *n, boolean refresh) +{ + if (*n->on) stlib_draw_num(n, refresh); +} + +void stlib_init_percent(st_percent_t *p, int x, int y, patch_t **pl, + int *num, boolean *on, patch_t *percent) +{ + stlib_init_num(&p->n, x, y, pl, num, on, 3); + p->p = percent; +} + +void stlib_update_percent(st_percent_t *per, int refresh) +{ + if (refresh && *per->n.on) v_draw_patch(per->n.x, per->n.y, per->p); + + stlib_update_num(&per->n, refresh); +} + +void stlib_init_mutl_icon(st_multicon_t *i, int x, int y, patch_t **il, + int *inum, boolean *on) +{ + i->x = x; + i->y = y; + i->oldinum = -1; + i->inum = inum; + i->on = on; + i->p = il; +} + +void stlib_update_mult_icon(st_multicon_t *mi, 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("updateMultIcon: y - ST_Y < 0"); + + v_copy_rect(x, y - ST_Y, st_backing_screen, w, h, x, y); + } + + v_draw_patch(mi->x, mi->y, mi->p[*mi->inum]); + mi->oldinum = *mi->inum; + } +} + +void stlib_init_bin_icon(st_binicon_t *b, int x, int y, patch_t *i, + boolean *val, boolean *on) +{ + b->x = x; + b->y = y; + b->oldval = false; + b->val = val; + b->on = on; + b->p = i; +} + +void stlib_update_bin_icon(st_binicon_t *bi, 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("updateBinIcon: y - ST_Y < 0"); + + if (*bi->val) + v_draw_patch(bi->x, bi->y, bi->p); + else + v_copy_rect(x, y - ST_Y, st_backing_screen, w, h, x, y); + + bi->oldval = *bi->val; + } +} diff --git a/games/NXDoom/src/doom/st_lib.h b/games/NXDoom/src/doom/st_lib.h new file mode 100644 index 00000000000..b1fa3867bd0 --- /dev/null +++ b/games/NXDoom/src/doom/st_lib.h @@ -0,0 +1,187 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/st_lib.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * The status bar widget code. + * + ****************************************************************************/ + +#ifndef __STLIB__ +#define __STLIB__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* We are referring to patches. */ + +#include "r_defs.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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 boolean stating whether to update number */ + + 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 boolean stating whether to update icon */ + + 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 */ + + boolean oldval; + + /* pointer to current icon status */ + + boolean *val; + + /* pointer to boolean stating whether to update icon */ + + 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. + */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void stlib_init(void); + +/* Number widget routines */ + +void stlib_init_num(st_number_t *n, int x, int y, patch_t **pl, int *num, + boolean *on, int width); + +void stlib_update_num(st_number_t *n, boolean refresh); + +/* Percent widget routines */ + +void stlib_init_percent(st_percent_t *p, int x, int y, patch_t **pl, + int *num, boolean *on, patch_t *percent); + +void stlib_update_percent(st_percent_t *per, int refresh); + +/* Multiple Icon widget routines */ + +void stlib_init_mutl_icon(st_multicon_t *mi, int x, int y, patch_t **il, + int *inum, boolean *on); + +void stlib_update_mult_icon(st_multicon_t *mi, boolean refresh); + +/* Binary Icon widget routines */ + +void stlib_init_bin_icon(st_binicon_t *b, int x, int y, patch_t *i, + boolean *val, boolean *on); + +void stlib_update_bin_icon(st_binicon_t *bi, boolean refresh); + +#endif /* __STLIB__ */ diff --git a/games/NXDoom/src/doom/st_stuff.c b/games/NXDoom/src/doom/st_stuff.c new file mode 100644 index 00000000000..7a133b33ca5 --- /dev/null +++ b/games/NXDoom/src/doom/st_stuff.c @@ -0,0 +1,1416 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/st_stuff.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Status bar code. + * Does the face/direction indicator animating. + * Does palette indicators as well (red pain/berserk, bright pickup) + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "i_system.h" +#include "i_video.h" +#include "m_misc.h" +#include "m_random.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "deh_main.h" +#include "deh_misc.h" +#include "doomdef.h" +#include "doomkeys.h" + +#include "g_game.h" + +#include "r_local.h" +#include "st_lib.h" +#include "st_stuff.h" + +#include "p_inter.h" +#include "p_local.h" + +#include "am_map.h" +#include "m_cheat.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* Needs access to LFB. */ + +#include "v_video.h" + +/* State. */ + +#include "doomstat.h" + +/* Data. */ + +#include "dstrings.h" + +/* STATUS BAR DATA */ + +/* Palette indices. + * For damage/bonus red-/gold-shifts + */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define STARTREDPALS 1 +#define STARTBONUSPALS 9 +#define NUMREDPALS 8 +#define NUMBONUSPALS 4 + +/* Radiation suit, green shift. */ + +#define RADIATIONPAL 13 + +/* Location of status bar */ + +#define ST_X 0 +#define ST_X2 104 + +#define ST_FX 143 +#define ST_FY 169 + +/* 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 + +/* Dimensions given in characters. */ + +#define ST_MSGWIDTH 52 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef void (*load_callback_t)(const char *lumpname, patch_t **variable); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* main player in game */ + +static player_t *plyr; + +/* st_start() has just been called */ + +static boolean st_firsttime; + +/* lump number for PLAYPAL */ + +static int lu_palette; + +/* used for making messages go away */ + +static int st_msgcounter = 0; + +/* whether left-side main status bar is active */ + +static boolean st_statusbaron; + +/* whether status bar chat is active */ + +static boolean st_chat; + +/* value of st_chat before message popped up */ + +static boolean st_oldchat; + +/* !deathmatch */ + +static boolean st_notdeathmatch; + +/* !deathmatch && st_statusbaron */ + +static boolean st_armson; + +/* !deathmatch */ + +static boolean st_fragson; + +/* main bar left */ + +static patch_t *sbar; + +/* main bar right, for doom 1.0 */ + +static patch_t *sbarr; + +/* 0-9, tall numbers */ + +static patch_t *tallnum[10]; + +/* tall % sign */ + +static patch_t *tallpercent; + +/* 0-9, short, yellow (,different!) numbers */ + +static patch_t *shortnum[10]; + +/* 3 key-cards, 3 skulls */ + +static patch_t *keys[NUMCARDS]; + +/* face status patches */ + +static patch_t *faces[ST_NUMFACES]; + +/* face background */ + +static patch_t *faceback; + +/* main bar right */ + +static patch_t *armsbg; + +/* weapon ownership patches */ + +static patch_t *arms[6][2]; + +/* ready-weapon widget */ + +static st_number_t w_ready; + +/* in deathmatch only, summary of frags stats */ + +static st_number_t w_frags; + +/* health widget */ + +static st_percent_t w_health; + +/* arms background */ + +static st_binicon_t w_armsbg; + +/* weapon ownership widgets */ + +static st_multicon_t w_arms[6]; + +/* face status widget */ + +static st_multicon_t w_faces; + +/* keycard widgets */ + +static st_multicon_t w_keyboxes[3]; + +/* armor widget */ + +static st_percent_t w_armor; + +/* ammo widgets */ + +static st_number_t w_ammo[4]; + +/* max ammo widgets */ + +static st_number_t w_maxammo[4]; + +/* number of frags so far in deathmatch */ + +static int st_fragscount; + +/* used to use appropriately pained face */ + +static int st_oldhealth = -1; + +/* used for evil grin */ + +static boolean oldweaponsowned[NUMWEAPONS]; + +/* count until face changes */ + +static int st_facecount = 0; + +/* current face index, used by w_faces */ + +static int st_faceindex = 0; + +/* holds key-type for each key box on bar */ + +static int keyboxes[3]; + +/* a random number per tick */ + +static int st_randomnumber; + +static int st_palette = 0; + +static boolean st_stopped = true; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* graphics are drawn to a backing screen and blitted to the real screen */ + +pixel_t *st_backing_screen; + +cheatseq_t cheat_mus = CHEAT("idmus", 2); +cheatseq_t cheat_god = CHEAT("iddqd", 0); +cheatseq_t cheat_ammo = CHEAT("idkfa", 0); +cheatseq_t cheat_ammonokey = CHEAT("idfa", 0); +cheatseq_t cheat_noclip = CHEAT("idspispopd", 0); +cheatseq_t cheat_commercial_noclip = CHEAT("idclip", 0); + +cheatseq_t cheat_powerup[7] = +{ + CHEAT("idbeholdv", 0), CHEAT("idbeholds", 0), CHEAT("idbeholdi", 0), + CHEAT("idbeholdr", 0), CHEAT("idbeholda", 0), CHEAT("idbeholdl", 0), + CHEAT("idbehold", 0), +}; + +cheatseq_t cheat_choppers = CHEAT("idchoppers", 0); +cheatseq_t cheat_clev = CHEAT("idclev", 2); +cheatseq_t cheat_mypos = CHEAT("idmypos", 0); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void st_stop(void) +{ + if (st_stopped) return; + + i_set_palette(w_cache_lump_num(lu_palette, PU_CACHE)); + st_stopped = true; +} + +static void st_refresh_background(void) +{ + if (st_statusbaron) + { + v_use_buffer(st_backing_screen); + + v_draw_patch(ST_X, 0, sbar); + + /* draw right side of bar if needed (Doom 1.0) */ + + if (sbarr) v_draw_patch(ST_ARMSBGX, 0, sbarr); + + if (netgame) v_draw_patch(ST_FX, 0, faceback); + + v_restore_buffer(); + + v_copy_rect(ST_X, 0, st_backing_screen, ST_WIDTH, ST_HEIGHT, ST_X, + ST_Y); + } +} + +static int st_calc_paint_offset(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 + */ + +static void st_update_face_widget(void) +{ + int i; + angle_t badguyangle; + angle_t diffang; + static int lastattackdown = -1; + static int priority = 0; + 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_calc_paint_offset() + 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_calc_paint_offset() + ST_OUCHOFFSET; + } + else + { + badguyangle = + r_point_to_angle2(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_calc_paint_offset(); + + 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_calc_paint_offset() + ST_OUCHOFFSET; + } + else + { + priority = 6; + st_facecount = ST_TURNCOUNT; + st_faceindex = st_calc_paint_offset() + ST_RAMPAGEOFFSET; + } + } + } + + if (priority < 6) + { + /* rapid firing */ + + if (plyr->attackdown) + { + if (lastattackdown == -1) + lastattackdown = ST_RAMPAGEDELAY; + else if (!--lastattackdown) + { + priority = 5; + st_faceindex = st_calc_paint_offset() + 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_calc_paint_offset() + (st_randomnumber % 3); + st_facecount = ST_STRAIGHTFACECOUNT; + priority = 0; + } + + st_facecount--; +} + +static void st_update_widgets(void) +{ + static int largeammo = 1994; /* means "n/a" */ + int i; + + /* must redirect the pointer if the ready weapon has changed. + * if (w_ready.data != plyr->readyweapon) + * { + */ + + if (weaponinfo[plyr->readyweapon].ammo == am_noammo) + w_ready.num = &largeammo; + else + w_ready.num = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo]; + + /* { + * static int tic=0; + * static int dir=-1; + * if (!(tic&15)) + * plyr->ammo[weaponinfo[plyr->readyweapon].ammo]+=dir; + * if (plyr->ammo[weaponinfo[plyr->readyweapon].ammo] == -100) + * dir = 1; + * tic++; + * } + */ + + w_ready.data = plyr->readyweapon; + + /* if (*w_ready.on) + * stlib_update_num(&w_ready, true); + * refresh weapon change + * } + */ + + /* 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_update_face_widget(); + + /* 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; +} + +static void st_do_palette_stuff(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; + + /* In Chex Quest, the player never sees red. Instead, the + * radiation suit palette is used to tint the screen green, + * as though the player is being covered in goo by an + * attacking flemoid. + */ + + if (gameversion == exe_chex && palette >= STARTREDPALS && + palette < STARTREDPALS + NUMREDPALS) + { + palette = RADIATIONPAL; + } + + if (palette != st_palette) + { + st_palette = palette; + pal = (byte *)w_cache_lump_num(lu_palette, PU_CACHE) + palette * 768; + i_set_palette(pal); + } +} + +static void st_draw_widgets(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_update_num(&w_ready, refresh); + + for (i = 0; i < 4; i++) + { + stlib_update_num(&w_ammo[i], refresh); + stlib_update_num(&w_maxammo[i], refresh); + } + + stlib_update_percent(&w_health, refresh); + stlib_update_percent(&w_armor, refresh); + + stlib_update_bin_icon(&w_armsbg, refresh); + + for (i = 0; i < 6; i++) + stlib_update_mult_icon(&w_arms[i], refresh); + + stlib_update_mult_icon(&w_faces, refresh); + + for (i = 0; i < 3; i++) + stlib_update_mult_icon(&w_keyboxes[i], refresh); + + stlib_update_num(&w_frags, refresh); +} + +static void st_do_refresh(void) +{ + st_firsttime = false; + + /* draw status bar background to off-screen buff */ + + st_refresh_background(); + + /* and refresh all widgets */ + + st_draw_widgets(true); +} + +static void st_diff_draw(void) +{ + /* update all widgets */ + + st_draw_widgets(false); +} + +/* Iterates through all graphics to be loaded or unloaded, along with + * the variable they use, invoking the specified callback function. + */ + +static void st_load_unload_graphics(load_callback_t callback) +{ + int i; + int j; + int facenum; + + char namebuf[9]; + + /* Load the numbers, tall and short */ + + for (i = 0; i < 10; i++) + { + snprintf(namebuf, 9, "STTNUM%d", i); + callback(namebuf, &tallnum[i]); + + snprintf(namebuf, 9, "STYSNUM%d", i); + callback(namebuf, &shortnum[i]); + } + + /* Load percent key. + * Note: why not load STMINUS here, too? + */ + + callback(("STTPRCNT"), &tallpercent); + + /* key cards */ + + for (i = 0; i < NUMCARDS; i++) + { + snprintf(namebuf, 9, "STKEYS%d", i); + callback(namebuf, &keys[i]); + } + + /* arms background */ + + callback(("STARMS"), &armsbg); + + /* arms ownership widgets */ + + for (i = 0; i < 6; i++) + { + snprintf(namebuf, 9, "STGNUM%d", i + 2); + + /* gray # */ + + callback(namebuf, &arms[i][0]); + + /* yellow # */ + + arms[i][1] = shortnum[i + 2]; + } + + /* face backgrounds for different color players */ + + snprintf(namebuf, 9, "STFB%d", consoleplayer); + callback(namebuf, &faceback); + + /* status bar background bits */ + + if (w_check_num_for_name("STBAR") >= 0) + { + callback(("STBAR"), &sbar); + sbarr = NULL; + } + else + { + callback(("STMBARL"), &sbar); + callback(("STMBARR"), &sbarr); + } + + /* face states */ + + facenum = 0; + for (i = 0; i < ST_NUMPAINFACES; i++) + { + for (j = 0; j < ST_NUMSTRAIGHTFACES; j++) + { + snprintf(namebuf, 9, "STFST%d%d", i, j); + callback(namebuf, &faces[facenum]); + ++facenum; + } + + snprintf(namebuf, 9, "STFTR%d0", i); /* turn right */ + callback(namebuf, &faces[facenum]); + ++facenum; + snprintf(namebuf, 9, "STFTL%d0", i); /* turn left */ + callback(namebuf, &faces[facenum]); + ++facenum; + snprintf(namebuf, 9, "STFOUCH%d", i); /* ouch! */ + callback(namebuf, &faces[facenum]); + ++facenum; + snprintf(namebuf, 9, "STFEVL%d", i); /* evil grin ;) */ + callback(namebuf, &faces[facenum]); + ++facenum; + snprintf(namebuf, 9, "STFKILL%d", i); /* pissed off */ + callback(namebuf, &faces[facenum]); + ++facenum; + } + + callback(("STFGOD0"), &faces[facenum]); + ++facenum; + callback(("STFDEAD0"), &faces[facenum]); + ++facenum; +} + +static void st_load_callback(const char *lumpname, patch_t **variable) +{ + *variable = w_cache_lump_name(lumpname, PU_STATIC); +} + +static void st_load_graphics(void) +{ + st_load_unload_graphics(st_load_callback); +} + +static void st_load_data(void) +{ + lu_palette = w_get_num_for_name(("PLAYPAL")); + st_load_graphics(); +} + +static void st_unload_callback(const char *lumpname, patch_t **variable) +{ + w_release_lump_name(lumpname); + *variable = NULL; +} + +static void st_unload_graphics(void) +{ + st_load_unload_graphics(st_unload_callback); +} + +static void st_init_data(void) +{ + int i; + + st_firsttime = true; + plyr = &players[consoleplayer]; + + st_statusbaron = true; + st_oldchat = st_chat = 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(); +} + +static void st_create_widgets(void) +{ + int i; + + /* ready weapon ammo */ + + stlib_init_num(&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_init_percent(&w_health, ST_HEALTHX, ST_HEALTHY, tallnum, + &plyr->health, &st_statusbaron, tallpercent); + + /* arms background */ + + stlib_init_bin_icon(&w_armsbg, ST_ARMSBGX, ST_ARMSBGY, armsbg, + &st_notdeathmatch, &st_statusbaron); + + /* weapons owned */ + + for (i = 0; i < 6; i++) + { + stlib_init_mutl_icon(&w_arms[i], ST_ARMSX + (i % 3) * ST_ARMSXSPACE, + ST_ARMSY + (i / 3) * ST_ARMSYSPACE, arms[i], + &plyr->weaponowned[i + 1], &st_armson); + } + + /* frags sum */ + + stlib_init_num(&w_frags, ST_FRAGSX, ST_FRAGSY, tallnum, &st_fragscount, + &st_fragson, ST_FRAGSWIDTH); + + /* faces */ + + stlib_init_mutl_icon(&w_faces, ST_FACESX, ST_FACESY, faces, &st_faceindex, + &st_statusbaron); + + /* armor percentage - should be colored later */ + + stlib_init_percent(&w_armor, ST_ARMORX, ST_ARMORY, tallnum, + &plyr->armorpoints, &st_statusbaron, tallpercent); + + /* keyboxes 0-2 */ + + stlib_init_mutl_icon(&w_keyboxes[0], ST_KEY0X, ST_KEY0Y, keys, + &keyboxes[0], &st_statusbaron); + + stlib_init_mutl_icon(&w_keyboxes[1], ST_KEY1X, ST_KEY1Y, keys, + &keyboxes[1], &st_statusbaron); + + stlib_init_mutl_icon(&w_keyboxes[2], ST_KEY2X, ST_KEY2Y, keys, + &keyboxes[2], &st_statusbaron); + + /* ammo count (all four kinds) */ + + stlib_init_num(&w_ammo[0], ST_AMMO0X, ST_AMMO0Y, shortnum, &plyr->ammo[0], + &st_statusbaron, ST_AMMO0WIDTH); + + stlib_init_num(&w_ammo[1], ST_AMMO1X, ST_AMMO1Y, shortnum, &plyr->ammo[1], + &st_statusbaron, ST_AMMO1WIDTH); + + stlib_init_num(&w_ammo[2], ST_AMMO2X, ST_AMMO2Y, shortnum, &plyr->ammo[2], + &st_statusbaron, ST_AMMO2WIDTH); + + stlib_init_num(&w_ammo[3], ST_AMMO3X, ST_AMMO3Y, shortnum, &plyr->ammo[3], + &st_statusbaron, ST_AMMO3WIDTH); + + /* max ammo count (all four kinds) */ + + stlib_init_num(&w_maxammo[0], ST_MAXAMMO0X, ST_MAXAMMO0Y, shortnum, + &plyr->maxammo[0], &st_statusbaron, ST_MAXAMMO0WIDTH); + + stlib_init_num(&w_maxammo[1], ST_MAXAMMO1X, ST_MAXAMMO1Y, shortnum, + &plyr->maxammo[1], &st_statusbaron, ST_MAXAMMO1WIDTH); + + stlib_init_num(&w_maxammo[2], ST_MAXAMMO2X, ST_MAXAMMO2Y, shortnum, + &plyr->maxammo[2], &st_statusbaron, ST_MAXAMMO2WIDTH); + + stlib_init_num(&w_maxammo[3], ST_MAXAMMO3X, ST_MAXAMMO3Y, shortnum, + &plyr->maxammo[3], &st_statusbaron, ST_MAXAMMO3WIDTH); +} + +/**************************************************************************** + * Public Function + ****************************************************************************/ + +/* Respond to keyboard input events, intercept cheats. */ + +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_firsttime = true; + break; + + case AM_MSGEXITED: + break; + } + } + + /* if a user keypress... */ + + else if (ev->type == ev_keydown) + { + if (!netgame && gameskill != sk_nightmare) + { + /* 'dqd' cheat for toggleable god mode */ + + if (cht_check_cheat(&cheat_god, ev->data2)) + { + plyr->cheats ^= CF_GODMODE; + if (plyr->cheats & CF_GODMODE) + { + if (plyr->mo) plyr->mo->health = deh_god_mode_health; + + plyr->health = deh_god_mode_health; + plyr->message = (STSTR_DQDON); + } + else + plyr->message = (STSTR_DQDOFF); + } + + /* 'fa' cheat for killer fucking arsenal */ + + else if (cht_check_cheat(&cheat_ammonokey, ev->data2)) + { + plyr->armorpoints = deh_idfa_armor; + plyr->armortype = deh_idfa_armor_class; + + 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_check_cheat(&cheat_ammo, ev->data2)) + { + plyr->armorpoints = deh_idkfa_armor; + plyr->armortype = deh_idkfa_armor_class; + + 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_check_cheat(&cheat_mus, ev->data2)) + { + char buf[3]; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + int musnum; +#endif + + plyr->message = (STSTR_MUS); + ch_get_param(&cheat_mus, buf); + + /* Note: The original v1.9 had a bug that tried to play back + * the Doom II music regardless of gamemode. This was fixed + * in the Ultimate Doom executable so that it would work for + * the Doom 1 music as well. + */ + + if (gamemode == commercial || gameversion < exe_ultimate) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + musnum = + MUS_RUNNIN + (buf[0] - '0') * 10 + buf[1] - '0' - 1; +#endif + + if (((buf[0] - '0') * 10 + buf[1] - '0') > 35 && + gameversion >= exe_doom_1_8) + plyr->message = (STSTR_NOMUS); + else + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_change_music(musnum, 1); +#endif + } + } + else + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + musnum = MUS_E1M1 + (buf[0] - '1') * 9 + (buf[1] - '1'); +#endif + + if (((buf[0] - '1') * 9 + buf[1] - '1') > 31) + plyr->message = (STSTR_NOMUS); + else + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_change_music(musnum, 1); +#endif + } + } + } + else if ((logical_gamemission == doom && + cht_check_cheat(&cheat_noclip, ev->data2)) || + (logical_gamemission != doom && + cht_check_cheat(&cheat_commercial_noclip, ev->data2))) + { + /* Noclip cheat. + * For Doom 1, use the idspipsopd cheat; for all others, use + * idclip + */ + + 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_check_cheat(&cheat_powerup[i], ev->data2)) + { + if (!plyr->powers[i]) + p_give_power(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_check_cheat(&cheat_powerup[6], ev->data2)) + { + plyr->message = (STSTR_BEHOLD); + } + + /* 'choppers' invulnerability & chainsaw */ + + else if (cht_check_cheat(&cheat_choppers, ev->data2)) + { + plyr->weaponowned[wp_chainsaw] = true; + plyr->powers[pw_invulnerability] = true; + plyr->message = (STSTR_CHOPPERS); + } + + /* 'mypos' for player position */ + + else if (cht_check_cheat(&cheat_mypos, ev->data2)) + { + static char buf[ST_MSGWIDTH]; + snprintf(buf, sizeof(buf), "ang=0x%x;x,y=(0x%x,0x%x)", + players[consoleplayer].mo->angle, + players[consoleplayer].mo->x, + players[consoleplayer].mo->y); + plyr->message = buf; + } + } + + /* 'clev' change-level cheat */ + + if (!netgame && cht_check_cheat(&cheat_clev, ev->data2)) + { + char buf[3]; + int epsd; + int map; + + ch_get_param(&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'; + + /* Chex.exe always warps to episode 1. */ + + if (gameversion == exe_chex) + { + if (epsd > 1) + { + epsd = 1; + } + + if (map > 5) + { + map = 5; + } + } + } + + /* Catch invalid maps. */ + + if (gamemode != commercial) + { + if (epsd < 1) + { + return false; + } + + if (epsd > 4) + { + return false; + } + + if (epsd == 4 && gameversion < exe_ultimate) + { + return false; + } + + if (map < 1) + { + return false; + } + + if (map > 9) + { + return false; + } + } + else + { + if (map < 1) + { + return false; + } + + if (map > 40) + { + return false; + } + } + + /* So be it. */ + + plyr->message = (STSTR_CLEV); + g_deferred_init_new(gameskill, epsd, map); + } + } + + return false; +} + +void st_ticker(void) +{ + st_randomnumber = m_random(); + st_update_widgets(); + st_oldhealth = plyr->health; +} + +void st_drawer(boolean p_fullscreen, boolean refresh) +{ + st_statusbaron = (!p_fullscreen) || automapactive; + st_firsttime = st_firsttime || refresh; + + /* Do red-/gold-shifts from damage/items */ + + st_do_palette_stuff(); + + /* If just after st_start(), refresh all */ + + if (st_firsttime) + { + st_do_refresh(); + } + else + { + /* Otherwise, update as little as possible */ + + st_diff_draw(); + } +} + +void st_start(void) +{ + if (!st_stopped) + { + st_stop(); + } + + st_init_data(); + st_create_widgets(); + st_stopped = false; +} + +void st_init(void) +{ + st_load_data(); + st_backing_screen = (pixel_t *)z_malloc( + ST_WIDTH * ST_HEIGHT * sizeof(*st_backing_screen), PU_STATIC, 0); +} diff --git a/games/NXDoom/src/doom/st_stuff.h b/games/NXDoom/src/doom/st_stuff.h new file mode 100644 index 00000000000..f68279e663d --- /dev/null +++ b/games/NXDoom/src/doom/st_stuff.h @@ -0,0 +1,91 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/st_stuff.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Status bar code. + * Does the face/direction indicator animating. + * Does palette indicators as well (red pain/berserk, bright pickup) + * + ****************************************************************************/ + +#ifndef __STSTUFF_H__ +#define __STSTUFF_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "doomtype.h" +#include "m_cheat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Size of statusbar. + * Now sensitive for scaling. + */ + +#define ST_HEIGHT 32 +#define ST_WIDTH SCREENWIDTH +#define ST_Y (SCREENHEIGHT - ST_HEIGHT) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern pixel_t *st_backing_screen; +extern cheatseq_t cheat_mus; +extern cheatseq_t cheat_god; +extern cheatseq_t cheat_ammo; +extern cheatseq_t cheat_ammonokey; +extern cheatseq_t cheat_noclip; +extern cheatseq_t cheat_commercial_noclip; +extern cheatseq_t cheat_powerup[7]; +extern cheatseq_t cheat_choppers; +extern cheatseq_t cheat_clev; +extern cheatseq_t cheat_mypos; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* STATUS BAR */ + +/* Called by main loop. */ + +boolean st_responder(event_t *ev); + +/* Called by main loop. */ + +void st_ticker(void); + +/* Called by main loop. */ + +void st_drawer(boolean fullscreen, boolean refresh); + +/* Called when the console player is spawned on each level. */ + +void st_start(void); + +/* Called by startup code. */ + +void st_init(void); + +#endif /* __STSTUFF_H__ */ diff --git a/games/NXDoom/src/doom/statdump.c b/games/NXDoom/src/doom/statdump.c new file mode 100644 index 00000000000..1db5657ab6c --- /dev/null +++ b/games/NXDoom/src/doom/statdump.c @@ -0,0 +1,388 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/statdump.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Functions for presenting the information captured from the statistics + * buffer to a file. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "d_mode.h" +#include "d_player.h" +#include "m_argv.h" +#include "m_misc.h" + +#include "statdump.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_CAPTURES 32 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Par times for E1M1-E1M9. */ + +static const int doom1_par_times[] = +{ + 30, 75, 120, 90, 165, 180, 180, 30, 165, +}; + +/* Par times for MAP01-MAP09. */ + +static const int doom2_par_times[] = +{ + 30, 90, 120, 120, 90, 150, 120, 120, 270, +}; + +/* Player colors. */ + +static const char *player_colors[] = +{ + "Green", + "Indigo", + "Brown", + "Red", +}; + +/* Array of end-of-level statistics that have been captured. */ + +static wbstartstruct_t captured_stats[MAX_CAPTURES]; +static int num_captured_stats = 0; + +static gamemission_t discovered_gamemission = none; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Try to work out whether this is a Doom 1 or Doom 2 game, by looking + * at the episode and map, and the par times. This is used to decide + * how to format the level name. Unfortunately, in some cases it is + * impossible to determine whether this is Doom 1 or Doom 2. + */ + +static void discover_game_mode(const wbstartstruct_t *stats, int num_stats) +{ + int partime; + int level; + int i; + + if (discovered_gamemission != none) + { + return; + } + + for (i = 0; i < num_stats; ++i) + { + level = stats[i].last; + + /* If episode 2, 3 or 4, this is Doom 1. */ + + if (stats[i].epsd > 0) + { + discovered_gamemission = doom; + return; + } + + /* This is episode 1. If this is level 10 or higher, it must be + * Doom 2. + */ + + if (level >= 9) + { + discovered_gamemission = doom2; + return; + } + + /* Try to work out if this is Doom 1 or Doom 2 by looking at the par + * time. + */ + + partime = stats[i].partime; + + if (partime == doom1_par_times[level] * TICRATE && + partime != doom2_par_times[level] * TICRATE) + { + discovered_gamemission = doom; + return; + } + + if (partime != doom1_par_times[level] * TICRATE && + partime == doom2_par_times[level] * TICRATE) + { + discovered_gamemission = doom2; + return; + } + } +} + +/* Returns the number of players active in the given stats buffer. */ + +static int get_num_players(const wbstartstruct_t *stats) +{ + int i; + int num_players = 0; + + for (i = 0; i < MAXPLAYERS; ++i) + { + if (stats->plyr[i].in) + { + ++num_players; + } + } + + return num_players; +} + +static void print_banner(FILE *stream) +{ + fprintf(stream, "===========================================\n"); +} + +static void print_percentage(FILE *stream, int amount, int total) +{ + if (total == 0) + { + fprintf(stream, "0"); + } + else + { + fprintf(stream, "%i / %i", amount, total); + + /* statdump.exe is a 16-bit program, so very occasionally an + * integer overflow can occur when doing this calculation with + * a large value. Therefore, cast to short to give the same + * output. + */ + + fprintf(stream, " (%i%%)", (short)(amount * 100) / total); + } +} + +/* Display statistics for a single player. */ + +static void print_player_stats(FILE *stream, const wbstartstruct_t *stats, + int player_num) +{ + const wbplayerstruct_t *player = &stats->plyr[player_num]; + + fprintf(stream, "Player %i (%s):\n", player_num + 1, + player_colors[player_num]); + + /* Kills percentage */ + + fprintf(stream, "\tKills: "); + print_percentage(stream, player->skills, stats->maxkills); + fprintf(stream, "\n"); + + /* Items percentage */ + + fprintf(stream, "\tItems: "); + print_percentage(stream, player->sitems, stats->maxitems); + fprintf(stream, "\n"); + + /* Secrets percentage */ + + fprintf(stream, "\tSecrets: "); + print_percentage(stream, player->ssecret, stats->maxsecret); + fprintf(stream, "\n"); +} + +/* Frags table for multiplayer games. */ + +static void print_frags_table(FILE *stream, const wbstartstruct_t *stats) +{ + int x; + int y; + + fprintf(stream, "Frags:\n"); + + /* Print header */ + + fprintf(stream, "\t\t"); + + for (x = 0; x < MAXPLAYERS; ++x) + { + if (!stats->plyr[x].in) + { + continue; + } + + fprintf(stream, "%s\t", player_colors[x]); + } + + fprintf(stream, "\n"); + + fprintf(stream, "\t\t-------------------------------- VICTIMS\n"); + + /* Print table */ + + for (y = 0; y < MAXPLAYERS; ++y) + { + if (!stats->plyr[y].in) + { + continue; + } + + fprintf(stream, "\t%s\t|", player_colors[y]); + + for (x = 0; x < MAXPLAYERS; ++x) + { + if (!stats->plyr[x].in) + { + continue; + } + + fprintf(stream, "%i\t", stats->plyr[y].frags[x]); + } + + fprintf(stream, "\n"); + } + + fprintf(stream, "\t\t|\n"); + fprintf(stream, "\t KILLERS\n"); +} + +/* Displays the level name: MAPxy or ExMy, depending on game mode. */ + +static void print_level_name(FILE *stream, int episode, int level) +{ + print_banner(stream); + + switch (discovered_gamemission) + { + case doom: + fprintf(stream, "E%iM%i\n", episode + 1, level + 1); + break; + case doom2: + fprintf(stream, "MAP%02i\n", level + 1); + break; + default: + case none: + fprintf(stream, "E%iM%i / MAP%02i\n", episode + 1, level + 1, + level + 1); + break; + } + + print_banner(stream); +} + +/* Print details of a statistics buffer to the given file. */ + +static void print_stats(FILE *stream, const wbstartstruct_t *stats) +{ + short leveltime; + short partime; + int i; + + print_level_name(stream, stats->epsd, stats->last); + fprintf(stream, "\n"); + + leveltime = stats->plyr[0].stime / TICRATE; + partime = stats->partime / TICRATE; + fprintf(stream, "Time: %i:%02i", leveltime / 60, leveltime % 60); + fprintf(stream, " (par: %i:%02i)\n", partime / 60, partime % 60); + fprintf(stream, "\n"); + + for (i = 0; i < MAXPLAYERS; ++i) + { + if (stats->plyr[i].in) + { + print_player_stats(stream, stats, i); + } + } + + if (get_num_players(stats) >= 2) + { + print_frags_table(stream, stats); + } + + fprintf(stream, "\n"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void stat_copy(const wbstartstruct_t *stats) +{ + if (m_parm_exists("-statdump") && num_captured_stats < MAX_CAPTURES) + { + memcpy(&captured_stats[num_captured_stats], stats, + sizeof(wbstartstruct_t)); + ++num_captured_stats; + } +} + +void stat_dump(void) +{ + FILE *dumpfile; + int i; + + /* @category compat + * @arg + * + * Dump statistics information to the specified file on the levels + * that were played. The output from this option matches the output + * from statdump.exe (see ctrlapi.zip in the /idgames archive). + */ + + i = m_check_parm_with_args("-statdump", 1); + + if (i > 0) + { + printf("Statistics captured for %i level(s)\n", num_captured_stats); + + /* We actually know what the real gamemission is, but this has + * to match the output from statdump.exe. + */ + + discover_game_mode(captured_stats, num_captured_stats); + + /* Allow "-" as output file, for stdout. */ + + if (strcmp(myargv[i + 1], "-") != 0) + { + dumpfile = fopen(myargv[i + 1], "w"); + } + else + { + dumpfile = stdout; + } + + for (i = 0; i < num_captured_stats; ++i) + { + print_stats(dumpfile, &captured_stats[i]); + } + + if (dumpfile != stdout) + { + fclose(dumpfile); + } + } +} diff --git a/games/NXDoom/src/doom/statdump.h b/games/NXDoom/src/doom/statdump.h new file mode 100644 index 00000000000..57073aa0892 --- /dev/null +++ b/games/NXDoom/src/doom/statdump.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/statdump.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef DOOM_STATDUMP_H +#define DOOM_STATDUMP_H + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void stat_copy(const wbstartstruct_t *stats); +void stat_dump(void); + +#endif /* DOOM_STATDUMP_H */ diff --git a/games/NXDoom/src/doom/wi_stuff.c b/games/NXDoom/src/doom/wi_stuff.c new file mode 100644 index 00000000000..724aa33203b --- /dev/null +++ b/games/NXDoom/src/doom/wi_stuff.c @@ -0,0 +1,1893 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/wi_stuff.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Intermission screens. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "z_zone.h" + +#include "m_misc.h" +#include "m_random.h" + +#include "deh_main.h" +#include "i_swap.h" +#include "i_system.h" + +#include "w_wad.h" + +#include "g_game.h" + +#include "r_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "doomstat.h" + +/* Needs access to LFB. */ + +#include "v_video.h" + +#include "wi_stuff.h" + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +/* 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 between 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 + +/* in tics + * U #define PAUSELEN (TICRATE*2) + * U #define SCORESTEP 100 + * U #define ANIMPERIOD 32 + * pixel distance from "(YOU)" to "PLAYER N" + * U #define STARDIST 10 + * U #define WK 1 + */ + +/* GLOBAL LOCATIONS */ + +#define WI_TITLEY 2 +#define WI_SPACINGY 33 + +/* SINGLE-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 + +/* Animation locations for episode 0 (1). + * Using patches saves a lot of space, + * as they replace 320x200 full screen frames. + */ + +#define ANIM(type, period, nanims, x, y, nexttic) \ + {(type), (period), (nanims), {(x), (y)}, (nexttic), 0, {NULL, NULL, NULL}, \ + 0, 0, 0, 0} + +/* GENERAL DATA */ + +/* Locally used stuff. */ + +/* 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 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + ANIM_ALWAYS, + ANIM_RANDOM, + ANIM_LEVEL +} animenum_t; + +/* States for the intermission */ + +typedef enum +{ + NO_STATE = -1, + STAT_COUNT, + SHOW_NEXT_LOC, +} stateenum_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; + +typedef void (*load_callback_t)(const char *lumpname, patch_t **variable); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +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) */ + }, +}; + +static anim_t epsd0animinfo[] = +{ + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 224, 104, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 184, 160, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 112, 136, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 72, 112, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 88, 96, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 64, 48, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 192, 40, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 136, 16, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 80, 16, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 64, 24, 0), +}; + +static anim_t epsd1animinfo[] = +{ + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 1), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 2), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 3), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 4), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 5), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 6), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 7), + ANIM(ANIM_LEVEL, TICRATE / 3, 3, 192, 144, 8), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 8), +}; + +static anim_t epsd2animinfo[] = +{ + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 104, 168, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 40, 136, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 160, 96, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 104, 80, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 120, 32, 0), + ANIM(ANIM_ALWAYS, TICRATE / 4, 3, 40, 0, 0), +}; + +static int NUMANIMS[NUMEPISODES] = +{ + arrlen(epsd0animinfo), + arrlen(epsd1animinfo), + arrlen(epsd2animinfo), +}; + +static anim_t *anims[NUMEPISODES] = +{ + epsd0animinfo, + epsd1animinfo, + epsd2animinfo, +}; + +/* 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 */ + +/* You Are Here graphic */ + +static patch_t *yah[3] = +{ + NULL, + NULL, + NULL, +}; + +/* splat */ + +static patch_t *splat[2] = +{ + NULL, + NULL, +}; + +/* %, : 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 *timepatch; +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; + +/* Buffer storing the backdrop */ + +static patch_t *background; + +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; + +static boolean snl_pointeron = false; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Common load/unload function. Iterates over all the graphics + * lumps to be loaded/unloaded into memory. + */ + +static void wi_load_unload_data(load_callback_t callback) +{ + int8_t i; + int8_t j; + char name[9]; + anim_t *a; + + if (gamemode == commercial) + { + for (i = 0; i < NUMCMAPS; i++) + { + snprintf(name, sizeof(name), "CWILV%2.2d", i); + callback(name, &lnames[i]); + } + } + else + { + for (i = 0; i < NUMMAPS; i++) + { + snprintf(name, sizeof(name), "WILV%d%d", wbs->epsd, i); + callback(name, &lnames[i]); + } + + /* you are here */ + + callback(("WIURH0"), &yah[0]); + + /* you are here (alt.) */ + + callback(("WIURH1"), &yah[1]); + + /* splat */ + + callback(("WISPLAT"), &splat[0]); + + if (wbs->epsd < 3) + { + for (j = 0; j < NUMANIMS[wbs->epsd]; j++) + { + a = &anims[wbs->epsd][j]; + for (i = 0; i < a->nanims; i++) + { + /* MONDO HACK! */ + + if (wbs->epsd != 1 || j != 8) + { + /* animations */ + + snprintf(name, sizeof(name), "WIA%d%.2d%.2d", + wbs->epsd, j, i); + callback(name, &a->p[i]); + } + else + { + /* HACK ALERT! */ + + a->p[i] = anims[1][4].p[i]; + } + } + } + } + } + + /* More hacks on minus sign. */ + + if (w_check_num_for_name(("WIMINUS")) > 0) + callback(("WIMINUS"), &wiminus); + else + wiminus = NULL; + + for (i = 0; i < 10; i++) + { + /* numbers 0-9 */ + + snprintf(name, sizeof(name), "WINUM%d", i); + callback(name, &num[i]); + } + + /* percent sign */ + + callback(("WIPCNT"), &percent); + + /* "finished" */ + + callback(("WIF"), &finished); + + /* "entering" */ + + callback(("WIENTER"), &entering); + + /* "kills" */ + + callback(("WIOSTK"), &kills); + + /* "scrt" */ + + callback(("WIOSTS"), &secret); + + /* "secret" */ + + callback(("WISCRT2"), &sp_secret); + + /* french wad uses WIOBJ (?) */ + + if (w_check_num_for_name(("WIOBJ")) >= 0) + { + /* "items" */ + + if (netgame && !deathmatch) + callback(("WIOBJ"), &items); + else + callback(("WIOSTI"), &items); + } + else + { + callback(("WIOSTI"), &items); + } + + /* "frgs" */ + + callback(("WIFRGS"), &frags); + + /* ":" */ + + callback(("WICOLON"), &colon); + + /* "time" */ + + callback(("WITIME"), &timepatch); + + /* "sucks" */ + + callback(("WISUCKS"), &sucks); + + /* "par" */ + + callback(("WIPAR"), &par); + + /* "killers" (vertical) */ + + callback(("WIKILRS"), &killers); + + /* "victims" (horiz) */ + + callback(("WIVCTMS"), &victims); + + /* "total" */ + + callback(("WIMSTT"), &total); + + for (i = 0; i < MAXPLAYERS; i++) + { + /* "1,2,3,4" */ + + snprintf(name, sizeof(name), "STPB%d", i); + callback(name, &p[i]); + + /* "1,2,3,4" */ + + snprintf(name, sizeof(name), "WIBP%d", i + 1); + callback(name, &bp[i]); + } + + /* Background image */ + + if (gamemode == commercial) + { + m_str_copy(name, ("INTERPIC"), sizeof(name)); + } + else if (gameversion >= exe_ultimate && wbs->epsd == 3) + { + m_str_copy(name, ("INTERPIC"), sizeof(name)); + } + else + { + snprintf(name, sizeof(name), "WIMAP%d", wbs->epsd); + } + + /* Draw backdrop and save to a temporary buffer */ + + callback(name, &background); +} + +static void wi_load_callback(const char *name, patch_t **variable) +{ + *variable = w_cache_lump_name(name, PU_STATIC); +} + +static void wi_unload_callback(const char *name, patch_t **variable) +{ + w_release_lump_name(name); + *variable = NULL; +} + +static void wi_load_data(void) +{ + if (gamemode == commercial) + { + NUMCMAPS = 32; + lnames = (patch_t **)z_malloc(sizeof(patch_t *) * NUMCMAPS, + PU_STATIC, NULL); + } + else + { + lnames = + (patch_t **)z_malloc(sizeof(patch_t *) * NUMMAPS, PU_STATIC, NULL); + } + + wi_load_unload_data(wi_load_callback); + + /* These two graphics are special cased because we're sharing them with the + * status bar code + */ + + /* your face */ + + star = w_cache_lump_name(("STFST01"), PU_STATIC); + + /* dead face */ + + bstar = w_cache_lump_name(("STFDEAD0"), PU_STATIC); +} + +static void wi_unload_data(void) +{ + wi_load_unload_data(wi_unload_callback); + + /* We do not free these lumps as they are shared with the status + * bar code. + */ + + /* w_release_lump_name("STFST01"); + * w_release_lump_name("STFDEAD0"); + */ +} + +/* slam background */ + +static void wi_slam_background(void) +{ + v_draw_patch(0, 0, background); +} + +/* Draws " Finished!" */ + +static void wi_draw_lf(void) +{ + int y = WI_TITLEY; + + if (gamemode != commercial || wbs->last < NUMCMAPS) + { + /* draw */ + + v_draw_patch((SCREENWIDTH - SHORT(lnames[wbs->last]->width)) / 2, y, + lnames[wbs->last]); + + /* draw "Finished!" */ + + y += (5 * SHORT(lnames[wbs->last]->height)) / 4; + + v_draw_patch((SCREENWIDTH - SHORT(finished->width)) / 2, y, finished); + } + else if (wbs->last == NUMCMAPS) + { + /* MAP33 - draw "Finished!" only */ + + v_draw_patch((SCREENWIDTH - SHORT(finished->width)) / 2, y, finished); + } + else if (wbs->last > NUMCMAPS) + { + /* > MAP33. Doom bombs out here with a Bad v_draw_patch error. + * I'm pretty sure that doom2.exe is just reading into random + * bits of memory at this point, but let's try to be accurate + * anyway. This deliberately triggers a v_draw_patch error. + */ + + patch_t tmp = + { + SCREENWIDTH, + SCREENHEIGHT, + 1, + 1, + { + 0, 0, 0, 0, 0, 0, 0, 0 + }, + }; + + v_draw_patch(0, y, &tmp); + } +} + +/* Draws "Entering " */ + +static void wi_draw_e(void) +{ + int y = WI_TITLEY; + + /* draw "Entering" */ + + v_draw_patch((SCREENWIDTH - SHORT(entering->width)) / 2, y, entering); + + /* draw level */ + + y += (5 * SHORT(lnames[wbs->next]->height)) / 4; + + v_draw_patch((SCREENWIDTH - SHORT(lnames[wbs->next]->width)) / 2, y, + lnames[wbs->next]); +} + +static void wi_draw_on_lnode(int n, patch_t *c[]) +{ + int i; + int left; + int top; + int right; + int bottom; + 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 && c[i] != NULL); + + if (fits && i < 2) + { + v_draw_patch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y, c[i]); + } + else + { + printf("Could not place patch on level %d", n + 1); /* DEBUG */ + } +} + +static void wi_init_animate_back(void) +{ + int i; + anim_t *a; + + if (gamemode == commercial) return; + + if (wbs->epsd > 2) return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims[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; + } +} + +static void wi_update_animated_block(void) +{ + int i; + anim_t *a; + + if (gamemode == commercial) return; + + if (wbs->epsd > 2) return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims[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 == STAT_COUNT && i == 7) && wbs->next == a->data1) + { + a->ctr++; + if (a->ctr == a->nanims) a->ctr--; + a->nexttic = bcnt + a->period; + } + break; + } + } + } +} + +static void wi_draw_animated_back(void) +{ + int i; + anim_t *a; + + if (gamemode == commercial) return; + + if (wbs->epsd > 2) return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims[wbs->epsd][i]; + + if (a->ctr >= 0) v_draw_patch(a->loc.x, a->loc.y, 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. + */ + +static int wi_draw_num(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_draw_patch(x, y, num[n % 10]); + n /= 10; + } + + /* draw a minus sign if necessary */ + + if (neg && wiminus) v_draw_patch(x -= 8, y, wiminus); + + return x; +} + +static void wi_draw_percent(int x, int y, int p_p) +{ + if (p_p < 0) return; + + v_draw_patch(x, y, percent); + wi_draw_num(x, y, p_p, -1); +} + +/* Display level completion time and par, or "sucks" message if overflow. + */ + +static void wi_draw_time(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_draw_num(x, y, n, 2) - SHORT(colon->width); + div *= 60; + + /* draw */ + + if (div == 60 || t / div) v_draw_patch(x, y, colon); + } + while (t / div); + } + else + { + /* "sucks" */ + + v_draw_patch(x - SHORT(sucks->width), y, sucks); + } +} + +static void wi_init_nostate(void) +{ + state = NO_STATE; + acceleratestage = 0; + cnt = 10; +} + +static void wi_update_no_state(void) +{ + wi_update_animated_block(); + + if (!--cnt) + { + /* Don't call wi_end yet. g_world_done doesn't immediately + * change gamestate, so wi_drawer is still going to get + * run until that happens. If we do that after wi_end + * (which unloads all the graphics), we're in trouble. + * wi_end(); + */ + + g_world_done(); + } +} + +static void wi_init_show_next_loc(void) +{ + state = SHOW_NEXT_LOC; + acceleratestage = 0; + cnt = SHOWNEXTLOCDELAY * TICRATE; + + wi_init_animate_back(); +} + +static void wi_update_show_next_loc(void) +{ + wi_update_animated_block(); + + if (!--cnt || acceleratestage) + wi_init_nostate(); + else + snl_pointeron = (cnt & 31) < 20; +} + +static void wi_draw_show_next_loc(void) +{ + int i; + int last; + + wi_slam_background(); + + /* draw animated background */ + + wi_draw_animated_back(); + + if (gamemode != commercial) + { + if (wbs->epsd > 2) + { + wi_draw_e(); + return; + } + + last = (wbs->last == 8) ? wbs->next - 1 : wbs->last; + + /* draw a splat on taken cities. */ + + for (i = 0; i <= last; i++) + wi_draw_on_lnode(i, splat); + + /* splat the secret level? */ + + if (wbs->didsecret) wi_draw_on_lnode(8, splat); + + /* draw flashing ptr */ + + if (snl_pointeron) wi_draw_on_lnode(wbs->next, yah); + } + + /* draws which level you are entering.. */ + + if ((gamemode != commercial) || wbs->next != 30) wi_draw_e(); +} + +static void wi_draw_no_state(void) +{ + snl_pointeron = true; + wi_draw_show_next_loc(); +} + +static int wi_frag_sum(int playernum) +{ + int i; + int l_frags = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && i != playernum) + { + l_frags += plrs[playernum].frags[i]; + } + } + + /* JDC hack - negative frags. */ + + l_frags -= plrs[playernum].frags[playernum]; + + /* UNUSED if (frags < 0) + * frags = 0; + */ + + return l_frags; +} + +static void wi_init_deathmatch_stats(void) +{ + int i; + int j; + + state = STAT_COUNT; + 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_init_animate_back(); +} + +static void wi_update_deathmatch_stats(void) +{ + int i; + int j; + + boolean stillticking; + + wi_update_animated_block(); + + 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_frag_sum(i); + } + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + dm_state = 4; + } + + if (dm_state == 2) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + 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_frag_sum(i); + + if (dm_totals[i] > 99) dm_totals[i] = 99; + + if (dm_totals[i] < -99) dm_totals[i] = -99; + } + } + + if (!stillticking) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + dm_state++; + } + } + else if (dm_state == 4) + { + if (acceleratestage) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_SLOP); +#endif + + if (gamemode == commercial) + wi_init_nostate(); + else + wi_init_show_next_loc(); + } + } + else if (dm_state & 1) + { + if (!--cnt_pause) + { + dm_state++; + cnt_pause = TICRATE; + } + } +} + +static void wi_draw_deathmatch_stats(void) +{ + int i; + int j; + int x; + int y; + int w; + + wi_slam_background(); + + /* draw animated background */ + + wi_draw_animated_back(); + wi_draw_lf(); + + /* draw stat titles (top line) */ + + v_draw_patch(DM_TOTALSX - SHORT(total->width) / 2, + DM_MATRIXY - WI_SPACINGY + 10, total); + + v_draw_patch(DM_KILLERSX, DM_KILLERSY, killers); + v_draw_patch(DM_VICTIMSX, DM_VICTIMSY, victims); + + /* draw P? */ + + x = DM_MATRIXX + DM_SPACINGX; + y = DM_MATRIXY; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + v_draw_patch(x - SHORT(p[i]->width) / 2, DM_MATRIXY - WI_SPACINGY, + p[i]); + + v_draw_patch(DM_MATRIXX - SHORT(p[i]->width) / 2, y, p[i]); + + if (i == me) + { + v_draw_patch(x - SHORT(p[i]->width) / 2, + DM_MATRIXY - WI_SPACINGY, bstar); + + v_draw_patch(DM_MATRIXX - SHORT(p[i]->width) / 2, y, star); + } + } + else + { + /* v_draw_patch(x-SHORT(bp[i]->width)/2, + * DM_MATRIXY - WI_SPACINGY, bp[i]); + * v_draw_patch(DM_MATRIXX-SHORT(bp[i]->width)/2, + * y, 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_draw_num(x + w, y, dm_frags[i][j], 2); + + x += DM_SPACINGX; + } + + wi_draw_num(DM_TOTALSX + w, y, dm_totals[i], 2); + } + + y += WI_SPACINGY; + } +} + +static void wi_init_netgame_stats(void) +{ + int i; + + state = STAT_COUNT; + 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_frag_sum(i); + } + + dofrags = !!dofrags; + + wi_init_animate_back(); +} + +static void wi_update_netgame_stats(void) +{ + int i; + int fsum; + + boolean stillticking; + + wi_update_animated_block(); + + 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_frag_sum(i); + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + ng_state = 10; + } + + if (ng_state == 2) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + 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) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + ng_state++; + } + } + else if (ng_state == 4) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + 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) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + ng_state++; + } + } + else if (ng_state == 6) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + 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) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + ng_state += 1 + 2 * !dofrags; + } + } + else if (ng_state == 8) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + cnt_frags[i] += 1; + + if (cnt_frags[i] >= (fsum = wi_frag_sum(i))) + cnt_frags[i] = fsum; + else + stillticking = true; + } + + if (!stillticking) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_PLDETH); +#endif + ng_state++; + } + } + else if (ng_state == 10) + { + if (acceleratestage) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_SGCOCK); +#endif + if (gamemode == commercial) + wi_init_nostate(); + else + wi_init_show_next_loc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = TICRATE; + } + } +} + +static void wi_init_stats(void) +{ + state = STAT_COUNT; + acceleratestage = 0; + sp_state = 1; + cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; + cnt_time = cnt_par = -1; + cnt_pause = TICRATE; + + wi_init_animate_back(); +} + +static void wi_update_stats(void) +{ + wi_update_animated_block(); + + 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; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + sp_state = 10; + } + + if (sp_state == 2) + { + cnt_kills[0] += 2; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) + { + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + sp_state++; + } + } + else if (sp_state == 4) + { + cnt_items[0] += 2; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems) + { + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + sp_state++; + } + } + else if (sp_state == 6) + { + cnt_secret[0] += 2; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs->maxsecret) + { + cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + sp_state++; + } + } + + else if (sp_state == 8) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + 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) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + sp_state++; + } + } + } + else if (sp_state == 10) + { + if (acceleratestage) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_SGCOCK); +#endif + + if (gamemode == commercial) + wi_init_nostate(); + else + wi_init_show_next_loc(); + } + } + else if (sp_state & 1) + { + if (!--cnt_pause) + { + sp_state++; + cnt_pause = TICRATE; + } + } +} + +static void wi_draw_netgame_stats(void) +{ + int i; + int x; + int y; + int pwidth = SHORT(percent->width); + + wi_slam_background(); + + /* draw animated background */ + + wi_draw_animated_back(); + + wi_draw_lf(); + + /* draw stat titles (top line) */ + + v_draw_patch(NG_STATSX + NG_SPACINGX - SHORT(kills->width), NG_STATSY, + kills); + + v_draw_patch(NG_STATSX + 2 * NG_SPACINGX - SHORT(items->width), NG_STATSY, + items); + + v_draw_patch(NG_STATSX + 3 * NG_SPACINGX - SHORT(secret->width), NG_STATSY, + secret); + + if (dofrags) + { + v_draw_patch(NG_STATSX + 4 * NG_SPACINGX - SHORT(frags->width), + NG_STATSY, frags); + } + + /* draw stats */ + + y = NG_STATSY + SHORT(kills->height); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + x = NG_STATSX; + v_draw_patch(x - SHORT(p[i]->width), y, p[i]); + + if (i == me) v_draw_patch(x - SHORT(p[i]->width), y, star); + + x += NG_SPACINGX; + wi_draw_percent(x - pwidth, y + 10, cnt_kills[i]); + x += NG_SPACINGX; + wi_draw_percent(x - pwidth, y + 10, cnt_items[i]); + x += NG_SPACINGX; + wi_draw_percent(x - pwidth, y + 10, cnt_secret[i]); + x += NG_SPACINGX; + + if (dofrags) wi_draw_num(x, y + 10, cnt_frags[i], -1); + + y += WI_SPACINGY; + } +} + +static void wi_draw_stats(void) +{ + int lh; /* line height */ + + lh = (3 * SHORT(num[0]->height)) / 2; + + wi_slam_background(); + + /* draw animated background */ + + wi_draw_animated_back(); + + wi_draw_lf(); + + v_draw_patch(SP_STATSX, SP_STATSY, kills); + wi_draw_percent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]); + + v_draw_patch(SP_STATSX, SP_STATSY + lh, items); + wi_draw_percent(SCREENWIDTH - SP_STATSX, SP_STATSY + lh, cnt_items[0]); + + v_draw_patch(SP_STATSX, SP_STATSY + 2 * lh, sp_secret); + wi_draw_percent(SCREENWIDTH - SP_STATSX, SP_STATSY + 2 * lh, + cnt_secret[0]); + + v_draw_patch(SP_TIMEX, SP_TIMEY, timepatch); + wi_draw_time(SCREENWIDTH / 2 - SP_TIMEX, SP_TIMEY, cnt_time); + + if (wbs->epsd < 3) + { + v_draw_patch(SCREENWIDTH / 2 + SP_TIMEX, SP_TIMEY, par); + wi_draw_time(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par); + } +} + +static void wi_check_for_accelerate(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; + } + } +} + +static void wi_init_variables(wbstartstruct_t *wbstartstruct) +{ + wbs = wbstartstruct; + + 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 (gameversion < exe_ultimate) + { + if (wbs->epsd > 2) wbs->epsd -= 3; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void wi_end(void) +{ + wi_unload_data(); +} + +/* Updates stuff each tick */ + +void wi_ticker(void) +{ + /* counter for general background animation */ + + bcnt++; + + if (bcnt == 1) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + /* intermission music */ + + if (gamemode == commercial) + s_change_music(MUS_DM2INT, true); + else + s_change_music(MUS_INTER, true); +#endif + } + + wi_check_for_accelerate(); + + switch (state) + { + case STAT_COUNT: + if (deathmatch) + wi_update_deathmatch_stats(); + else if (netgame) + wi_update_netgame_stats(); + else + wi_update_stats(); + break; + + case SHOW_NEXT_LOC: + wi_update_show_next_loc(); + break; + + case NO_STATE: + wi_update_no_state(); + break; + } +} + +void wi_drawer(void) +{ + switch (state) + { + case STAT_COUNT: + if (deathmatch) + wi_draw_deathmatch_stats(); + else if (netgame) + wi_draw_netgame_stats(); + else + wi_draw_stats(); + break; + + case SHOW_NEXT_LOC: + wi_draw_show_next_loc(); + break; + + case NO_STATE: + wi_draw_no_state(); + break; + } +} + +void wi_start(wbstartstruct_t *wbstartstruct) +{ + wi_init_variables(wbstartstruct); + wi_load_data(); + + if (deathmatch) + wi_init_deathmatch_stats(); + else if (netgame) + wi_init_netgame_stats(); + else + wi_init_stats(); +} diff --git a/games/NXDoom/src/doom/wi_stuff.h b/games/NXDoom/src/doom/wi_stuff.h new file mode 100644 index 00000000000..78c689d78fc --- /dev/null +++ b/games/NXDoom/src/doom/wi_stuff.h @@ -0,0 +1,78 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/wi_stuff.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Intermission. + * + ****************************************************************************/ + +#ifndef __WI_STUFF__ +#define __WI_STUFF__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: wi_ticker + * + * Description: + * Called by main loop, animate the intermission. + * + ****************************************************************************/ + +void wi_ticker(void); + +/**************************************************************************** + * Name: wi_drawer + * + * Description: + * Called by main loop, draws the intermission directly into the screen + * buffer. + * + ****************************************************************************/ + +void wi_drawer(void); + +/**************************************************************************** + * Name: wi_start + * + * Description: + * Setup for an intermission screen. + * + ****************************************************************************/ + +void wi_start(wbstartstruct_t *wbstartstruct); + +/**************************************************************************** + * Name: wi_end + * + * Description: + * Shut down the intermission screen + * + ****************************************************************************/ + +void wi_end(void); + +#endif /* __WI_STUFF__ */ diff --git a/games/NXDoom/src/doomkeys.h b/games/NXDoom/src/doomkeys.h new file mode 100644 index 00000000000..f29a2b78221 --- /dev/null +++ b/games/NXDoom/src/doomkeys.h @@ -0,0 +1,302 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doomkeys.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Key definitions + * + * + ****************************************************************************/ + +#ifndef __DOOMKEYS__ +#define __DOOMKEYS__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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 0x7f +#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 + +/* new keys: */ + +#define KEY_CAPSLOCK (0x80 + 0x3a) +#define KEY_NUMLOCK (0x80 + 0x45) +#define KEY_SCRLCK (0x80 + 0x46) +#define KEY_PRTSCR (0x80 + 0x59) + +#define KEY_HOME (0x80 + 0x47) +#define KEY_END (0x80 + 0x4f) +#define KEY_PGUP (0x80 + 0x49) +#define KEY_PGDN (0x80 + 0x51) +#define KEY_INS (0x80 + 0x52) +#define KEY_DEL (0x80 + 0x53) + +#define KEYP_0 KEY_INS +#define KEYP_1 KEY_END +#define KEYP_2 KEY_DOWNARROW +#define KEYP_3 KEY_PGDN +#define KEYP_4 KEY_LEFTARROW +#define KEYP_5 (0x80 + 0x4c) +#define KEYP_6 KEY_RIGHTARROW +#define KEYP_7 KEY_HOME +#define KEYP_8 KEY_UPARROW +#define KEYP_9 KEY_PGUP + +#define KEYP_DIVIDE '/' +#define KEYP_PLUS '+' +#define KEYP_MINUS '-' +#define KEYP_MULTIPLY '*' +#define KEYP_PERIOD 0 +#define KEYP_EQUALS KEY_EQUALS +#define KEYP_ENTER KEY_ENTER + +#define KEY_NONUSBACKSLASH '\\' + +#define SCANCODE_TO_KEYS_ARRAY \ + { \ + 0, \ + 0, \ + 0, \ + 0, \ + 'a', /* 0-9 */ \ + 'b', \ + 'c', \ + 'd', \ + 'e', \ + 'f', \ + 'g', \ + 'h', \ + 'i', \ + 'j', \ + 'k', /* 10-19 */ \ + 'l', \ + 'm', \ + 'n', \ + 'o', \ + 'p', \ + 'q', \ + 'r', \ + 's', \ + 't', \ + 'u', /* 20-29 */ \ + 'v', \ + 'w', \ + 'x', \ + 'y', \ + 'z', \ + '1', \ + '2', \ + '3', \ + '4', \ + '5', /* 30-39 */ \ + '6', \ + '7', \ + '8', \ + '9', \ + '0', \ + KEY_ENTER, \ + KEY_ESCAPE, \ + KEY_BACKSPACE, \ + KEY_TAB, \ + ' ', /* 40-49 */ \ + KEY_MINUS, \ + KEY_EQUALS, \ + '[', \ + ']', \ + '\\', \ + 0, \ + ';', \ + '\'', \ + '`', \ + ',', /* 50-59 */ \ + '.', \ + '/', \ + KEY_CAPSLOCK, \ + KEY_F1, \ + KEY_F2, \ + KEY_F3, \ + KEY_F4, \ + KEY_F5, \ + KEY_F6, \ + KEY_F7, /* 60-69 */ \ + KEY_F8, \ + KEY_F9, \ + KEY_F10, \ + KEY_F11, \ + KEY_F12, \ + KEY_PRTSCR, \ + KEY_SCRLCK, \ + KEY_PAUSE, \ + KEY_INS, \ + KEY_HOME, /* 70-79 */ \ + KEY_PGUP, \ + KEY_DEL, \ + KEY_END, \ + KEY_PGDN, \ + KEY_RIGHTARROW, \ + KEY_LEFTARROW, \ + KEY_DOWNARROW, \ + KEY_UPARROW, /* 80-89 */ \ + KEY_NUMLOCK, \ + KEYP_DIVIDE, \ + KEYP_MULTIPLY, \ + KEYP_MINUS, \ + KEYP_PLUS, \ + KEYP_ENTER, \ + KEYP_1, \ + KEYP_2, \ + KEYP_3, \ + KEYP_4, \ + KEYP_5, \ + KEYP_6, /* 90-99 */ \ + KEYP_7, \ + KEYP_8, \ + KEYP_9, \ + KEYP_0, \ + KEYP_PERIOD, \ + KEY_NONUSBACKSLASH, \ + 0, \ + 0, \ + KEYP_EQUALS, /* 100-103 */ \ + } + +/* Default names for keys, to use in English or as fallback. */ + +#define KEY_NAMES_ARRAY \ + { \ + {KEY_BACKSPACE, "BACKSP"}, \ + {KEY_TAB, "TAB"}, \ + {KEY_INS, "INS"}, \ + {KEY_DEL, "DEL"}, \ + {KEY_PGUP, "PGUP"}, \ + {KEY_PGDN, "PGDN"}, \ + {KEY_ENTER, "ENTER"}, \ + {KEY_ESCAPE, "ESC"}, \ + {KEY_F1, "F1"}, \ + {KEY_F2, "F2"}, \ + {KEY_F3, "F3"}, \ + {KEY_F4, "F4"}, \ + {KEY_F5, "F5"}, \ + {KEY_F6, "F6"}, \ + {KEY_F7, "F7"}, \ + {KEY_F8, "F8"}, \ + {KEY_F9, "F9"}, \ + {KEY_F10, "F10"}, \ + {KEY_F11, "F11"}, \ + {KEY_F12, "F12"}, \ + {KEY_HOME, "HOME"}, \ + {KEY_END, "END"}, \ + {KEY_MINUS, "-"}, \ + {KEY_EQUALS, "="}, \ + {KEY_NUMLOCK, "NUMLCK"}, \ + {KEY_SCRLCK, "SCRLCK"}, \ + {KEY_PAUSE, "PAUSE"}, \ + {KEY_PRTSCR, "PRTSC"}, \ + {KEY_UPARROW, "UP"}, \ + {KEY_DOWNARROW, "DOWN"}, \ + {KEY_LEFTARROW, "LEFT"}, \ + {KEY_RIGHTARROW, "RIGHT"}, \ + {KEY_RALT, "ALT"}, \ + {KEY_LALT, "ALT"}, \ + {KEY_RSHIFT, "SHIFT"}, \ + {KEY_CAPSLOCK, "CAPS"}, \ + {KEY_RCTRL, "CTRL"}, \ + {KEYP_5, "NUM5"}, \ + {' ', "SPACE"}, \ + {'a', "A"}, \ + {'b', "B"}, \ + {'c', "C"}, \ + {'d', "D"}, \ + {'e', "E"}, \ + {'f', "F"}, \ + {'g', "G"}, \ + {'h', "H"}, \ + {'i', "I"}, \ + {'j', "J"}, \ + {'k', "K"}, \ + {'l', "L"}, \ + {'m', "M"}, \ + {'n', "N"}, \ + {'o', "O"}, \ + {'p', "P"}, \ + {'q', "Q"}, \ + {'r', "R"}, \ + {'s', "S"}, \ + {'t', "T"}, \ + {'u', "U"}, \ + {'v', "V"}, \ + {'w', "W"}, \ + {'x', "X"}, \ + {'y', "Y"}, \ + {'z', "Z"}, \ + {'0', "0"}, \ + {'1', "1"}, \ + {'2', "2"}, \ + {'3', "3"}, \ + {'4', "4"}, \ + {'5', "5"}, \ + {'6', "6"}, \ + {'7', "7"}, \ + {'8', "8"}, \ + {'9', "9"}, \ + {'[', "["}, \ + {']', "]"}, \ + {';', ";"}, \ + {'`', "`"}, \ + {',', ","}, \ + {'.', "."}, \ + {'/', "/"}, \ + {'\\', "\\"}, \ + {'\'', "\'"}, \ + } + +#endif /* __DOOMKEYS__ */ diff --git a/games/NXDoom/src/doomtype.h b/games/NXDoom/src/doomtype.h new file mode 100644 index 00000000000..2029723ad3f --- /dev/null +++ b/games/NXDoom/src/doomtype.h @@ -0,0 +1,119 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doomtype.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Simple basic typedefs, isolated here to make it easier + * separating modules. + * + ****************************************************************************/ + +#ifndef __DOOMTYPE__ +#define __DOOMTYPE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/* What is really wanted here is stdint.h; however, some old versions + * of Solaris don't have stdint.h and only have inttypes.h (the + * pre-standardisation version). inttypes.h is also in the C99 + * standard and defined to include stdint.h, so include this. + */ + +#include +#include + +#include + +#include "config.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DIR_SEPARATOR '/' +#define DIR_SEPARATOR_S "/" +#define PATH_SEPARATOR ':' + +#define arrlen(array) (sizeof(array) / sizeof(*array)) + +/* The packed attribute forces structures to be packed into the minimum + * space necessary. If this is not done, the compiler may align structure + * fields differently to optimize memory access, inflating the overall + * structure size. It is important to use the packed attribute on certain + * structures where alignment is important, particularly data read/written + * to disk. + */ + +#ifdef __GNUC__ + +#define PACKEDATTR __attribute__((packed)) + +#define PRINTF_ATTR(fmt, first) __attribute__((format(printf, fmt, first))) +#define PRINTF_ARG_ATTR(x) __attribute__((format_arg(x))) +#define NORETURN __attribute__((noreturn)) + +#else +#if defined(_MSC_VER) +#define PACKEDATTR __pragma(pack(pop)) +#else +#define PACKEDATTR +#endif +#define PRINTF_ATTR(fmt, first) +#define PRINTF_ARG_ATTR(x) +#define NORETURN +#endif /* __GNUC__ */ + +#define PACKEDPREFIX + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* C99 integer types; with gcc we just use this. Other compilers + * should add conditional statements that define the C99 types. + */ + +#if defined(__cplusplus) || defined(__bool_true_false_are_defined) + +/* The C++/C99 bool type (or _Bool that is) can only have two values: + * 0 or 1. However, the Doom source code assumes any non-zero value + * to evaluate to true, so we have to use an int type here. + */ + +typedef int boolean; + +#else + +typedef enum +{ + false, + true +} boolean; + +#endif + +typedef uint8_t byte; +typedef uint8_t pixel_t; +typedef int16_t dpixel_t; + +#endif /* __DOOMTYPE__ */ diff --git a/games/NXDoom/src/gusconf.c b/games/NXDoom/src/gusconf.c new file mode 100644 index 00000000000..01f6606c5e0 --- /dev/null +++ b/games/NXDoom/src/gusconf.c @@ -0,0 +1,337 @@ +/**************************************************************************** + * apps/games/NXDoom/src/gusconf.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * GUS emulation code. + * + * Actually emulating a GUS is far too much work; fortunately + * GUS "emulation" already exists in the form of Timidity, which + * supports GUS patch files. This code therefore converts Doom's + * DMXGUS lump into an equivalent Timidity configuration file. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "m_misc.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_INSTRUMENTS 256 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + char *patch_names[MAX_INSTRUMENTS]; + int used[MAX_INSTRUMENTS]; + int mapping[MAX_INSTRUMENTS]; + unsigned int count; +} gus_config_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +char *gus_patch_path = ""; +int gus_ram_kb = 1024; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static unsigned int mapping_index(void) +{ + unsigned int result = gus_ram_kb / 256; + + if (result < 1) + { + return 1; + } + else if (result > 4) + { + return 4; + } + else + { + return result; + } +} + +static int split_line(char *line, char **fields, unsigned int max_fields) +{ + unsigned int num_fields; + char *p; + + fields[0] = line; + num_fields = 1; + + for (p = line; *p != '\0'; ++p) + { + if (*p == ',') + { + *p = '\0'; + + /* Skip spaces following the comma. */ + + do + { + ++p; + } + while (*p != '\0' && isspace(*p)); + + fields[num_fields] = p; + ++num_fields; + --p; + + if (num_fields >= max_fields) + { + break; + } + } + else if (*p == '#') + { + *p = '\0'; + break; + } + } + + /* Strip off trailing whitespace from the end of the line. */ + + p = fields[num_fields - 1] + strlen(fields[num_fields - 1]); + while (p > fields[num_fields - 1] && isspace(*(p - 1))) + { + --p; + *p = '\0'; + } + + return num_fields; +} + +static void parse_line(gus_config_t *config, char *line) +{ + char *fields[6]; + unsigned int i; + unsigned int num_fields; + unsigned int instr_id; + unsigned int mapped_id; + + num_fields = split_line(line, fields, 6); + + if (num_fields < 6) + { + return; + } + + instr_id = atoi(fields[0]); + + /* Skip non GM percussions. */ + + if ((instr_id >= 128 && instr_id < 128 + 35) || instr_id > 128 + 81) + { + return; + } + + mapped_id = atoi(fields[mapping_index()]); + + for (i = 0; i < config->count; i++) + { + if (config->used[i] == mapped_id) + { + break; + } + } + + if (i == config->count) + { + /* DMX uses wrong patch name (we should use name of 'mapped_id' + * instrument, but DMX uses name of 'instr_id' instead). + */ + + free(config->patch_names[i]); + config->patch_names[i] = m_string_duplicate(fields[5]); + config->used[i] = mapped_id; + config->count++; + } + + config->mapping[instr_id] = i; +} + +static void parse_dmx_config(char *dmxconf, gus_config_t *config) +{ + char *p; + char *newline; + unsigned int i; + + memset(config, 0, sizeof(gus_config_t)); + + for (i = 0; i < MAX_INSTRUMENTS; ++i) + { + config->mapping[i] = -1; + config->used[i] = -1; + } + + config->count = 0; + + p = dmxconf; + + for (; ; ) + { + newline = strchr(p, '\n'); + + if (newline != NULL) + { + *newline = '\0'; + } + + parse_line(config, p); + + if (newline == NULL) + { + break; + } + else + { + p = newline + 1; + } + } +} + +static void free_dmx_config(gus_config_t *config) +{ + unsigned int i; + + for (i = 0; i < MAX_INSTRUMENTS; ++i) + { + free(config->patch_names[i]); + } +} + +static char *read_dmx_config(void) +{ + int lumpnum; + unsigned int len; + char *data; + + /* TODO: This should be chosen based on gamemode == commercial: */ + + lumpnum = w_check_num_for_name("DMXGUS"); + + if (lumpnum < 0) + { + lumpnum = w_get_num_for_name("DMXGUSC"); + } + + len = w_lump_length(lumpnum); + data = z_malloc(len + 1, PU_STATIC, NULL); + w_read_lump(lumpnum, data); + + data[len] = '\0'; + return data; +} + +static boolean write_timidity_config(char *path, gus_config_t *config) +{ + FILE *fstream; + unsigned int i; + + fstream = fopen(path, "w"); + + if (fstream == NULL) + { + return false; + } + + fprintf(fstream, "# Autogenerated Timidity config.\n\n"); + + fprintf(fstream, "dir \"%s\"\n", gus_patch_path); + + fprintf(fstream, "\nbank 0\n\n"); + + for (i = 0; i < 128; ++i) + { + if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS && + config->patch_names[config->mapping[i]] != NULL) + { + fprintf(fstream, "%u %s\n", i, + config->patch_names[config->mapping[i]]); + } + } + + fprintf(fstream, "\ndrumset 0\n\n"); + + for (i = 128 + 35; i <= 128 + 81; ++i) + { + if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS && + config->patch_names[config->mapping[i]] != NULL) + { + fprintf(fstream, "%u %s\n", i - 128, + config->patch_names[config->mapping[i]]); + } + } + + fprintf(fstream, "\n"); + + fclose(fstream); + + return true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +boolean gus_write_config(char *path) +{ + boolean result; + char *dmxconf; + gus_config_t config; + + if (!strcmp(gus_patch_path, "")) + { + printf("You haven't configured gus_patch_path.\n"); + printf("gus_patch_path needs to point to the location of " + "your GUS patch set.\n" + "To get a copy of the \"standard\" GUS patches, " + "download a copy of dgguspat.zip.\n"); + + return false; + } + + dmxconf = read_dmx_config(); + parse_dmx_config(dmxconf, &config); + + result = write_timidity_config(path, &config); + + free_dmx_config(&config); + z_free(dmxconf); + + return result; +} diff --git a/games/NXDoom/src/gusconf.h b/games/NXDoom/src/gusconf.h new file mode 100644 index 00000000000..aef259d5e15 --- /dev/null +++ b/games/NXDoom/src/gusconf.h @@ -0,0 +1,45 @@ +/**************************************************************************** + * apps/games/NXDoom/src/gusconf.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * GUS emulation code. + * + ****************************************************************************/ + +#ifndef __GUSCONF_H__ +#define __GUSCONF_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern char *gus_patch_path; +extern int gus_ram_kb; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean gus_write_config(char *path); + +#endif /* __GUSCONF_H__ */ diff --git a/games/NXDoom/src/i_endoom.c b/games/NXDoom/src/i_endoom.c new file mode 100644 index 00000000000..97a797ee8fa --- /dev/null +++ b/games/NXDoom/src/i_endoom.c @@ -0,0 +1,99 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_endoom.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Exit text-mode ENDOOM screen. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "config.h" +#include "doomtype.h" +#include "i_video.h" + +#include "txt_main.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ENDOOM_W 80 +#define ENDOOM_H 25 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: i_endoom + * + * Description: + * Displays the text mode ending screen after the game quits + * + ****************************************************************************/ + +void i_endoom(byte *endoom_data) +{ + unsigned char *screendata; + int y; + int indent; + + /* Set up text mode screen */ + + txt_init(); + + txt_set_window_title(PACKAGE_STRING); + + /* SDL2-TODO i_init_window_title(); + */ + + /* Write the data to the screen memory */ + + screendata = txt_get_screen_data(); + + indent = (ENDOOM_W - TXT_SCREEN_W) / 2; + + for (y = 0; y < TXT_SCREEN_H; ++y) + { + memcpy(screendata + (y * TXT_SCREEN_W * 2), + endoom_data + (y * ENDOOM_W + indent) * 2, TXT_SCREEN_W * 2); + } + + /* Wait for a keypress */ + + while (true) + { + txt_update_screen(); + + if (txt_getchar() > 0) + { + break; + } + + txt_sleep(0); + } + + /* Shut down text mode screen */ + + txt_shutdown(); +} diff --git a/games/NXDoom/src/i_endoom.h b/games/NXDoom/src/i_endoom.h new file mode 100644 index 00000000000..22a773dc45d --- /dev/null +++ b/games/NXDoom/src/i_endoom.h @@ -0,0 +1,40 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_endoom.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Exit text-mode ENDOOM screen. + * + ****************************************************************************/ + +#ifndef __I_ENDOOM__ +#define __I_ENDOOM__ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_GAMES_NXDOOM_ENDOOM + +/* Display the Endoom screen on shutdown. Pass a pointer to the + * ENDOOM lump. + */ + +void i_endoom(byte *data); +#endif + +#endif /* __I_ENDOOM__ */ diff --git a/games/NXDoom/src/i_glob.c b/games/NXDoom/src/i_glob.c new file mode 100644 index 00000000000..6f2df49e094 --- /dev/null +++ b/games/NXDoom/src/i_glob.c @@ -0,0 +1,431 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_glob.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2018 Simon Howard + * + * 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. + * + * + * File globbing API. This allows the contents of the filesystem + * to be interrogated. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "config.h" +#include "i_glob.h" +#include "m_misc.h" + +#if defined(HAVE_DIRENT_H) +#include +#include +#elif defined(__WATCOMC__) +/* Watcom has the same API in a different header. */ +#include +#else +#define NO_DIRENT_IMPLEMENTATION +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct glob_s +{ + char **globs; + int num_globs; + int flags; + DIR *dir; + char *directory; + char *last_filename; + + /* These fields are only used when the GLOB_FLAG_SORTED flag is set: + */ + + char **filenames; + int filenames_len; + int next_index; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifndef NO_DIRENT_IMPLEMENTATION + +/* Only the fields d_name and (as an XSI extension) d_ino are specified + * in POSIX.1. Other than Linux, the d_type field is available mainly + * only on BSD systems. The remaining fields are available on many, but + * not all systems. + */ + +static boolean is_directory(char *dir, struct dirent *de) +{ +#if defined(_DIRENT_HAVE_D_TYPE) + if (de->d_type != DT_UNKNOWN && de->d_type != DT_LNK) + { + return de->d_type == DT_DIR; + } + else +#endif + { + char *filename; + struct stat sb; + int result; + + filename = m_string_join(dir, DIR_SEPARATOR_S, de->d_name, NULL); + result = m_stat(filename, &sb); + free(filename); + + if (result != 0) + { + return false; + } + + return S_ISDIR(sb.st_mode); + } +} + +static void free_string_list(char **globs, int num_globs) +{ + int i; + for (i = 0; i < num_globs; ++i) + { + free(globs[i]); + } + + free(globs); +} + +static boolean matches_glob(const char *name, const char *glob, int flags) +{ + int n; + int g; + + while (*glob != '\0') + { + n = *name; + g = *glob; + + if ((flags & GLOB_FLAG_NOCASE) != 0) + { + n = tolower(n); + g = tolower(g); + } + + if (g == '*') + { + /* To handle *-matching we skip past the * and recurse + * to check each subsequent character in turn. If none + * match then the whole match is a failure. + */ + + while (*name != '\0') + { + if (matches_glob(name, glob + 1, flags)) + { + return true; + } + + ++name; + } + + return glob[1] == '\0'; + } + else if (g != '?' && n != g) + { + /* For normal characters the name must match the glob, + * but for ? we don't care what the character is. + */ + + return false; + } + + ++name; + ++glob; + } + + /* Match successful when glob and name end at the same time. */ + + return *name == '\0'; +} + +static boolean matches_any_glob(const char *name, glob_t *glob) +{ + int i; + + for (i = 0; i < glob->num_globs; ++i) + { + if (matches_glob(name, glob->globs[i], glob->flags)) + { + return true; + } + } + + return false; +} + +static char *next_glob(glob_t *glob) +{ + struct dirent *de; + + do + { + de = readdir(glob->dir); + if (de == NULL) + { + return NULL; + } + } + while (is_directory(glob->directory, de) || + !matches_any_glob(de->d_name, glob)); + + /* Return the fully-qualified path, not just the bare filename. */ + + return m_string_join(glob->directory, DIR_SEPARATOR_S, de->d_name, NULL); +} + +static void read_all_filenames(glob_t *glob) +{ + char *name; + + glob->filenames = NULL; + glob->filenames_len = 0; + glob->next_index = 0; + + for (; ; ) + { + name = next_glob(glob); + if (name == NULL) + { + break; + } + + glob->filenames = realloc(glob->filenames, + (glob->filenames_len + 1) * sizeof(char *)); + glob->filenames[glob->filenames_len] = name; + ++glob->filenames_len; + } +} + +static void sort_filenames(char **filenames, int len, int flags) +{ + char *pivot; + char *tmp; + int i; + int left_len; + int cmp; + + if (len <= 1) + { + return; + } + + pivot = filenames[len - 1]; + left_len = 0; + for (i = 0; i < len - 1; ++i) + { + if ((flags & GLOB_FLAG_NOCASE) != 0) + { + cmp = strcasecmp(filenames[i], pivot); + } + else + { + cmp = strcmp(filenames[i], pivot); + } + + if (cmp < 0) + { + tmp = filenames[i]; + filenames[i] = filenames[left_len]; + filenames[left_len] = tmp; + ++left_len; + } + } + + filenames[len - 1] = filenames[left_len]; + filenames[left_len] = pivot; + + sort_filenames(filenames, left_len, flags); + sort_filenames(&filenames[left_len + 1], len - left_len - 1, flags); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +glob_t *i_start_multi_glob(const char *directory, int flags, + const char *glob, ...) +{ + char **globs; + int num_globs; + glob_t *result; + va_list args; + + globs = malloc(sizeof(char *)); + if (globs == NULL) + { + return NULL; + } + + globs[0] = m_string_duplicate(glob); + num_globs = 1; + + va_start(args, glob); + for (; ; ) + { + const char *arg = va_arg(args, const char *); + char **new_globs; + + if (arg == NULL) + { + break; + } + + new_globs = realloc(globs, sizeof(char *) * (num_globs + 1)); + if (new_globs == NULL) + { + free_string_list(globs, num_globs); + } + + globs = new_globs; + globs[num_globs] = m_string_duplicate(arg); + ++num_globs; + } + + va_end(args); + + result = malloc(sizeof(glob_t)); + if (result == NULL) + { + free_string_list(globs, num_globs); + return NULL; + } + + result->dir = opendir(directory); + if (result->dir == NULL) + { + free_string_list(globs, num_globs); + free(result); + return NULL; + } + + result->directory = m_string_duplicate(directory); + result->globs = globs; + result->num_globs = num_globs; + result->flags = flags; + result->last_filename = NULL; + result->filenames = NULL; + result->filenames_len = 0; + result->next_index = -1; + return result; +} + +glob_t *i_start_glob(const char *directory, const char *glob, int flags) +{ + return i_start_multi_glob(directory, flags, glob, NULL); +} + +void i_end_glob(glob_t *glob) +{ + if (glob == NULL) + { + return; + } + + free_string_list(glob->globs, glob->num_globs); + free_string_list(glob->filenames, glob->filenames_len); + + free(glob->directory); + free(glob->last_filename); + (void)closedir(glob->dir); + free(glob); +} + +const char *i_next_glob(glob_t *glob) +{ + const char *result; + + if (glob == NULL) + { + return NULL; + } + + /* In unsorted mode we just return the filenames as we read + * them back from the system API. + */ + + if ((glob->flags & GLOB_FLAG_SORTED) == 0) + { + free(glob->last_filename); + glob->last_filename = next_glob(glob); + return glob->last_filename; + } + + /* In sorted mode we read the whole list of filenames into memory, + * sort them and return them one at a time. + */ + + if (glob->next_index < 0) + { + read_all_filenames(glob); + sort_filenames(glob->filenames, glob->filenames_len, glob->flags); + } + + if (glob->next_index >= glob->filenames_len) + { + return NULL; + } + + result = glob->filenames[glob->next_index]; + ++glob->next_index; + return result; +} + +#else /* #ifdef NO_DIRENT_IMPLEMENTATION */ + +#warning "No native implementation of file globbing." + +glob_t *i_start_glob(const char *directory, const char *glob, int flags) +{ + return NULL; +} + +void i_end_glob(glob_t *glob) +{ + return; +} + +const char *i_next_glob(glob_t *glob) +{ + return ""; +} + +glob_t *i_start_multi_glob(const char *directory, int flags, + const char *glob, ...) +{ + return NULL; +} + +#endif /* NO_DIRENT_IMPLEMENTATION */ diff --git a/games/NXDoom/src/i_glob.h b/games/NXDoom/src/i_glob.h new file mode 100644 index 00000000000..a151795adfc --- /dev/null +++ b/games/NXDoom/src/i_glob.h @@ -0,0 +1,87 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_glob.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2018 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System specific file globbing interface. + * + ****************************************************************************/ + +#ifndef __I_GLOB__ +#define __I_GLOB__ + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#define GLOB_FLAG_NOCASE 0x01 +#define GLOB_FLAG_SORTED 0x02 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct glob_s glob_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: i_start_glob + * + * Description: + * Start reading a list of file paths from the given directory which match + * the given glob pattern. i_end_glob() must be called on completion. + * + ****************************************************************************/ + +glob_t *i_start_glob(const char *directory, const char *glob, int flags); + +/**************************************************************************** + * Name: i_start_multi_glob + * + * Description: + * Same as i_start_glob but multiple glob patterns can be provided. The list + * of patterns must be terminated with NULL. + * + ****************************************************************************/ + +glob_t *i_start_multi_glob(const char *directory, int flags, + const char *glob, ...); + +/**************************************************************************** + * Name: i_end_glob + * + * Description: + * Finish reading file list. + * + ****************************************************************************/ + +void i_end_glob(glob_t *glob); + +/**************************************************************************** + * Name: i_next_glob + * + * Description: + * Read the name of the next globbed filename. NULL is returned if there + * are no more found. + * + ****************************************************************************/ + +const char *i_next_glob(glob_t *glob); + +#endif /* __I_GLOB__ */ diff --git a/games/NXDoom/src/i_input.c b/games/NXDoom/src/i_input.c new file mode 100644 index 00000000000..8cb3a863019 --- /dev/null +++ b/games/NXDoom/src/i_input.c @@ -0,0 +1,402 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_input.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * SDL implementation of system-specific input interface. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#ifdef CONFIG_GAMES_NXDOOM_KEYBOARD +#include +#endif + +#include "d_event.h" +#include "doomkeys.h" +#include "doomtype.h" +#include "i_input.h" +#include "m_argv.h" +#include "m_config.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct keyboard_dev +{ + int fd; + bool inited; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_GAMES_NXDOOM_KEYBOARD +struct keyboard_dev g_kbd_dev = +{ + .fd = -1, + .inited = false, +}; +#endif + +#if 0 +static const int g_scancode_translate_table[] = SCANCODE_TO_KEYS_ARRAY; + +/* Lookup table for mapping ASCII characters to their equivalent when + * shift is pressed on a US layout keyboard. This is the original table + * as found in the Doom sources, comments and all. + */ + +static const char 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 +}; + +/* If true, i_start_text_input() has been called, and we are populating + * the data3 field of ev_keydown events. + */ + +static boolean text_input_enabled = true; + +/* Bit mask of mouse button state. */ + +static unsigned int mouse_button_state = 0; +#endif + +/* Disallow mouse and joystick movement to cause forward/backward + * motion. Specified with the '-novert' command line parameter. + * This is an int to allow saving to config file + */ + +int novert = 0; + +/* If true, keyboard mapping is ignored, like in Vanilla Doom. + * The sensible thing to do is to disable this if you have a non-US + * keyboard. + */ + +int vanilla_keyboard_mapping = true; + +/* Mouse acceleration + * + * This emulates some of the behavior of DOS mouse drivers by increasing + * the speed when the mouse is moved fast. + * + * The mouse input values are input directly to the game, but when + * the values exceed the value of mouse_threshold, they are multiplied + * by mouse_acceleration to increase the speed. + */ + +float mouse_acceleration = 2.0; +int mouse_threshold = 10; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: init_kbd_dev + * + * Description: + * Set up the keyboard device for getting keyboard events. + * + * Return: + * 0 on success, error code otherwise. + * + ****************************************************************************/ + +static int init_kbd_dev(struct keyboard_dev *dev) +{ + if (dev->inited) + { + return 0; + } + + dev->fd = open(CONFIG_GAMES_NXDOOM_KBDPATH, O_RDONLY | O_NONBLOCK); + if (dev->fd < 0) + { + return errno; + } + + dev->inited = true; + return 0; +} + +/**************************************************************************** + * Name: translate_key + * + * Description: + * Translates a NuttX key code into one from doomkeys.h. + * + * Return: + * The translated key code. + * + ****************************************************************************/ + +static int translate_key(uint32_t keycode) +{ + switch (keycode) + { + case KEYCODE_LEFT: + return KEY_LEFTARROW; + case KEYCODE_RIGHT: + return KEY_RIGHTARROW; + case KEYCODE_UP: + return KEY_UPARROW; + case KEYCODE_DOWN: + return KEY_DOWNARROW; + case KEYCODE_ENTER: + return KEY_ENTER; + default: + return keycode; + } +} + +/**************************************************************************** + * Name: get_localized_key + * + * Description: + * Get the localized version of the key press. This takes into account the + * keyboard layout, but does not apply any changes due to modifiers, (eg. + * shift-, alt-, etc.) + * + ****************************************************************************/ + +static int get_localized_key(uint32_t sym) +{ + /* NOTE: Argument was SDL_Keysym *sym */ + + /* When using Vanilla mapping, we just base everything off the scancode + * and always pretend the user is using a US layout keyboard. + */ + + if (vanilla_keyboard_mapping) + { + return translate_key(sym); + } + else + { + int result = sym; + + if (result < 0 || result >= 128) + { + result = 0; + } + + return result; + } +} + +/**************************************************************************** + * Name: get_typed_char + * + * Description: + * Get the equivalent ASCII (Unicode?) character for a keypress. + * + ****************************************************************************/ + +static int get_typed_char(uint32_t sym) +{ + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef CONFIG_GAMES_NXDOOM_KEYBOARD + +/**************************************************************************** + * Name: get_kbd_event + * + * Description: + * Read a single keyboard event from the keyboard device. + * + * Return: + * 0 on success, error code otherwise. + * + ****************************************************************************/ + +int get_kbd_event(struct keyboard_event_s *sample) +{ + int err; + ssize_t nbytes; + + /* Initialize the keyboard device if it isn't already */ + + err = init_kbd_dev(&g_kbd_dev); + if (err) + { + return err; + } + + /* Read events until we're out of them */ + + nbytes = read(g_kbd_dev.fd, sample, sizeof(*sample)); + if (nbytes < 0) + { + err = errno; + if (err != EINTR) + { + return err; + } + } + else if (nbytes != sizeof(*sample)) + { + return EIO; + } + else if (nbytes == 0) + { + return EAGAIN; /* No event */ + } + + return 0; +} +#endif + +void i_handle_keyboard_event(struct keyboard_event_s *kevent) +{ + /* XXX: passing pointers to event for access after this function + * has terminated is undefined behaviour + */ + + event_t event; + + switch (kevent->type) + { + case KEYBOARD_PRESS: + event.type = ev_keydown; + event.data1 = translate_key(kevent->code); + event.data2 = get_localized_key(kevent->code); + event.data3 = get_typed_char(kevent->code); + + if (event.data1 != 0) + { + d_post_event(&event); + } + break; + + case KEYBOARD_RELEASE: + event.type = ev_keyup; + event.data1 = translate_key(kevent->code); + + /* data2/data3 are initialized to zero for ev_keyup. + * For ev_keydown it's the shifted Unicode character + * that was typed, but if something wants to detect + * key releases it should do so based on data1 + * (key ID), not the printable char. + */ + + event.data2 = 0; + event.data3 = 0; + + if (event.data1 != 0) + { + d_post_event(&event); + } + break; + + default: + break; + } +} + +void i_start_text_input(int x1, int y1, int x2, int y2) +{ +} + +void i_stop_text_input(void) +{ +} + +void i_handle_mouse_event(void) +{ + /* Argument was SDL_Event *sdlevent */ +} + +/**************************************************************************** + * Name: i_read_mouse + * + * Description: + * Read the change in mouse state to generate mouse motion events. This is + * to combine all mouse movement for a tic into one mouse motion event. + * + ****************************************************************************/ + +void i_read_mouse(void) +{ +} + +/**************************************************************************** + * Name: i_bind_input_variables + * + * Description: + * Bind all variables controlling input options. + * + ****************************************************************************/ + +void i_bind_input_variables(void) +{ + m_bind_float_variable("mouse_acceleration", &mouse_acceleration); + m_bind_int_variable("mouse_threshold", &mouse_threshold); + m_bind_int_variable("vanilla_keyboard_mapping", &vanilla_keyboard_mapping); + m_bind_int_variable("novert", &novert); +} diff --git a/games/NXDoom/src/i_input.h b/games/NXDoom/src/i_input.h new file mode 100644 index 00000000000..97fc81bf7fc --- /dev/null +++ b/games/NXDoom/src/i_input.h @@ -0,0 +1,77 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_input.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System-specific keyboard/mouse input. + * + ****************************************************************************/ + +#ifndef __I_INPUT__ +#define __I_INPUT__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +#ifdef CONFIG_GAMES_NXDOOM_KEYBOARD +#include +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_MOUSE_BUTTONS 8 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern float mouse_acceleration; +extern int mouse_threshold; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void i_bind_input_variables(void); +void i_read_mouse(void); + +/* i_start_text_input begins text input, activating the on-screen keyboard + * (if one is used). The caller indicates that any entered text will be + * displayed in the rectangle given by the provided set of coordinates. + */ + +void i_start_text_input(int x1, int y1, int x2, int y2); + +/* i_stop_text_input finishes text input, deactivating the on-screen keyboard + * (if one is used). + */ + +void i_stop_text_input(void); + +void i_handle_keyboard_event(struct keyboard_event_s *kevent); +void i_handle_mouse_event(void); + +#ifdef CONFIG_GAMES_NXDOOM_KEYBOARD +int get_kbd_event(struct keyboard_event_s *sample); +#endif + +#endif /* __I_INPUT__ */ diff --git a/games/NXDoom/src/i_joystick.c b/games/NXDoom/src/i_joystick.c new file mode 100644 index 00000000000..4ae0b3a32a7 --- /dev/null +++ b/games/NXDoom/src/i_joystick.c @@ -0,0 +1,181 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_joystick.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * SDL Joystick code. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "d_event.h" +#include "doomtype.h" +#include "i_joystick.h" +#include "i_system.h" + +#include "m_config.h" +#include "m_fixed.h" +#include "m_misc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DIRECTION_DEADZONE (50 * 32768 / 100) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Configuration variables: */ + +/* Standard default.cfg Joystick enable/disable */ + +static int usejoystick = 0; + +/* Use SDL_gamecontroller interface for the selected device */ + +static int use_gamepad = 0; + +/* SDL_GameControllerType of gamepad */ + +static int gamepad_type = 0; + +/* SDL GUID and index of the joystick to use. */ + +static char *joystick_guid = ""; +static int joystick_index = -1; + +/* Which joystick axis to use for horizontal movement, and whether to + * invert the direction: + */ + +static int joystick_x_axis = 0; +static int joystick_x_invert = 0; + +/* Which joystick axis to use for vertical movement, and whether to + * invert the direction: + */ + +static int joystick_y_axis = 1; +static int joystick_y_invert = 0; + +/* Which joystick axis to use for strafing? */ + +static int joystick_strafe_axis = -1; +static int joystick_strafe_invert = 0; + +/* Which joystick axis to use for looking? */ + +static int joystick_look_axis = -1; +static int joystick_look_invert = 0; + +/* Configurable dead zone for each axis, specified as a percentage of the + * axis max value. + */ + +static int joystick_x_dead_zone = 33; +static int joystick_y_dead_zone = 33; +static int joystick_strafe_dead_zone = 33; +static int joystick_look_dead_zone = 33; + +/* Virtual to physical button joystick button mapping. By default this + * is a straight mapping. + */ + +static int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int use_analog = 0; + +int joystick_turn_sensitivity = 10; +int joystick_move_sensitivity = 10; +int joystick_look_sensitivity = 10; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void i_init_joystick(void) +{ +} + +void i_update_gamepad(void) +{ +} + +void i_shutdown_joystick(void) +{ +} + +void i_update_joystick(void) +{ +} + +void i_bind_joystick_variables(void) +{ + int i; + + m_bind_int_variable("use_joystick", &usejoystick); + m_bind_int_variable("use_gamepad", &use_gamepad); + m_bind_int_variable("gamepad_type", &gamepad_type); + m_bind_string_variable("joystick_guid", &joystick_guid); + m_bind_int_variable("joystick_index", &joystick_index); + m_bind_int_variable("joystick_x_axis", &joystick_x_axis); + m_bind_int_variable("joystick_y_axis", &joystick_y_axis); + m_bind_int_variable("joystick_strafe_axis", &joystick_strafe_axis); + m_bind_int_variable("joystick_x_invert", &joystick_x_invert); + m_bind_int_variable("joystick_y_invert", &joystick_y_invert); + m_bind_int_variable("joystick_strafe_invert", &joystick_strafe_invert); + m_bind_int_variable("joystick_look_axis", &joystick_look_axis); + m_bind_int_variable("joystick_look_invert", &joystick_look_invert); + m_bind_int_variable("joystick_x_dead_zone", &joystick_x_dead_zone); + m_bind_int_variable("joystick_y_dead_zone", &joystick_y_dead_zone); + m_bind_int_variable("joystick_strafe_dead_zone", + &joystick_strafe_dead_zone); + m_bind_int_variable("joystick_look_dead_zone", &joystick_look_dead_zone); + m_bind_int_variable("use_analog", &use_analog); + m_bind_int_variable("joystick_turn_sensitivity", + &joystick_turn_sensitivity); + m_bind_int_variable("joystick_move_sensitivity", + &joystick_move_sensitivity); + m_bind_int_variable("joystick_look_sensitivity", + &joystick_look_sensitivity); + + for (i = 0; i < NUM_VIRTUAL_BUTTONS; ++i) + { + char name[32]; + snprintf(name, sizeof(name), "joystick_physical_button%i", i); + m_bind_int_variable(name, &joystick_physical_buttons[i]); + } +} diff --git a/games/NXDoom/src/i_joystick.h b/games/NXDoom/src/i_joystick.h new file mode 100644 index 00000000000..6c1ff3df593 --- /dev/null +++ b/games/NXDoom/src/i_joystick.h @@ -0,0 +1,152 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_joystick.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System-specific joystick interface. + * + ****************************************************************************/ + +#ifndef __I_JOYSTICK__ +#define __I_JOYSTICK__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Number of "virtual" joystick buttons defined in configuration files. + * This needs to be at least as large as the number of different key + * bindings supported by the higher-level game code (joyb* variables). + */ + +#define NUM_VIRTUAL_BUTTONS 17 + +/* Max allowed number of virtual mappings. Chosen to be less than joybspeed + * autorun value. + */ + +#define MAX_VIRTUAL_BUTTONS 20 + +/* If this bit is set in a configuration file axis value, the axis is + * not actually a joystick axis, but instead is a "button axis". This + * means that instead of reading an SDL joystick axis, we read the + * state of two buttons to get the axis value. This is needed for eg. + * the PS3 SIXAXIS controller, where the D-pad buttons register as + * buttons, not as two axes. + */ + +#define BUTTON_AXIS 0x10000 + +/* Query whether a given axis value describes a button axis. */ + +#define IS_BUTTON_AXIS(axis) ((axis) >= 0 && ((axis) & BUTTON_AXIS) != 0) + +/* Get the individual buttons from a button axis value. */ + +#define BUTTON_AXIS_NEG(axis) ((axis) & 0xff) +#define BUTTON_AXIS_POS(axis) (((axis) >> 8) & 0xff) + +/* Create a button axis value from two button values. */ + +#define CREATE_BUTTON_AXIS(neg, pos) (BUTTON_AXIS | (neg) | ((pos) << 8)) + +/* If this bit is set in an axis value, the axis is not actually a + * joystick axis, but is a "hat" axis. This means that we read (one of) + * the hats on the joystick. + */ + +#define HAT_AXIS 0x20000 + +#define IS_HAT_AXIS(axis) ((axis) >= 0 && ((axis) & HAT_AXIS) != 0) + +/* Get the hat number from a hat axis value. */ + +#define HAT_AXIS_HAT(axis) ((axis) & 0xff) + +/* Which axis of the hat? (horizontal or vertical) */ + +#define HAT_AXIS_DIRECTION(axis) (((axis) >> 8) & 0xff) + +#define CREATE_HAT_AXIS(hat, direction) \ + (HAT_AXIS | (hat) | ((direction) << 8)) + +#define HAT_AXIS_HORIZONTAL 1 +#define HAT_AXIS_VERTICAL 2 + +/* When a trigger reads greater than this, consider it to be pressed. 30 + * comes from XINPUT_GAMEPAD_TRIGGER_THRESHOLD in xinput.h, and is scaled + * here for the SDL_GameController trigger max value. + */ + +#define TRIGGER_THRESHOLD (30 * 32767 / 255) + +/* To be used with SDL_JoystickGetGUIDString; see SDL_joystick.h */ + +#define GUID_STRING_BUF_SIZE 33 + +/* Helper macros for bitpacked directional data from gamepad inputs. */ + +#define DPAD_SHIFT 0 +#define LSTICK_SHIFT 4 +#define RSTICK_SHIFT 8 +#define JOY_GET_DPAD(x) (((x) >> DPAD_SHIFT) & 0xf) +#define JOY_GET_LSTICK(x) (((x) >> LSTICK_SHIFT) & 0xf) +#define JOY_GET_RSTICK(x) (((x) >> RSTICK_SHIFT) & 0xf) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 4-way direction data for gamepad directional inputs. */ + +enum +{ + JOY_DIR_NONE = 0x0, + JOY_DIR_UP = 0x1, + JOY_DIR_DOWN = 0x2, + JOY_DIR_LEFT = 0x4, + JOY_DIR_RIGHT = 0x8 +}; + +/* Extend the SDL_GameControllerButton enum to include the triggers. */ + +enum +{ + GAMEPAD_BUTTON_TRIGGERLEFT, /* = SDL_CONTROLLER_BUTTON_MAX, */ + GAMEPAD_BUTTON_TRIGGERRIGHT, + GAMEPAD_BUTTON_MAX +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int use_analog; +extern int joystick_turn_sensitivity; +extern int joystick_move_sensitivity; +extern int joystick_look_sensitivity; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void i_init_joystick(void); +void i_shutdown_joystick(void); +void i_update_joystick(void); + +void i_bind_joystick_variables(void); + +#endif /* __I_JOYSTICK__ */ diff --git a/games/NXDoom/src/i_main.c b/games/NXDoom/src/i_main.c new file mode 100644 index 00000000000..bd9dab60910 --- /dev/null +++ b/games/NXDoom/src/i_main.c @@ -0,0 +1,87 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_main.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Main program, simply calls d_doom_main high level loop. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "config.h" + +#include +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_misc.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: d_doom_main() + * + * Description: + * Not a globally visible function, just included for source reference, + * calls all startup code, parses command line options. + * + ****************************************************************************/ + +void d_doom_main(void); + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char **argv) +{ + /* save arguments */ + + myargc = argc; + myargv = malloc(argc * sizeof(char *)); + assert(myargv != NULL); + + for (int i = 0; i < argc; i++) + { + myargv[i] = m_string_duplicate(argv[i]); + } + + /* Print the program version and exit. */ + + if (m_parm_exists("-version") || m_parm_exists("--version")) + { + puts(PACKAGE_STRING); + exit(0); + } + + m_find_response_file(); + m_set_exe_dir(); + + /* start doom */ + + d_doom_main(); + + return 0; +} diff --git a/games/NXDoom/src/i_musicpack.c b/games/NXDoom/src/i_musicpack.c new file mode 100644 index 00000000000..733a45c7c4e --- /dev/null +++ b/games/NXDoom/src/i_musicpack.c @@ -0,0 +1,152 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_musicpack.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System interface for music. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "i_glob.h" + +#include "config.h" +#include "doomtype.h" +#include "memio.h" +#include "mus2mid.h" + +#include "deh_str.h" +#include "gusconf.h" +#include "i_sound.h" +#include "i_swap.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" +#include "sha1.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static boolean i_null_init_music(void); +static void i_null_shutdown_music(void); +static void i_null_set_music_volume(int volume); +static void i_null_pause_song(void); +static void i_null_resume_song(void); +static void *i_null_register_song(void *data, int len); +static void i_null_unregister_song(void *handle); +static void i_null_play_song(void *handle, boolean looping); +static void i_null_stop_song(void); +static boolean i_null_musicisplaying(void); +static void i_null_pollmusic(void); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +char *music_pack_path = ""; + +const music_module_t music_pack_module = +{ + NULL, + 0, + i_null_init_music, + i_null_shutdown_music, + i_null_set_music_volume, + i_null_pause_song, + i_null_resume_song, + i_null_register_song, + i_null_unregister_song, + i_null_play_song, + i_null_stop_song, + i_null_musicisplaying, + i_null_pollmusic, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static boolean i_null_init_music(void) +{ + return false; +} + +static void i_null_shutdown_music(void) +{ + return; +} + +static void i_null_set_music_volume(int volume) +{ + return; +} + +static void i_null_pause_song(void) +{ + return; +} + +static void i_null_resume_song(void) +{ + return; +} + +static void *i_null_register_song(void *data, int len) +{ + return NULL; +} + +static void i_null_unregister_song(void *handle) +{ + return; +} + +static void i_null_play_song(void *handle, boolean looping) +{ + return; +} + +static void i_null_stop_song(void) +{ + return; +} + +static boolean i_null_musicisplaying(void) +{ + return false; +} + +static void i_null_pollmusic(void) +{ + return; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/i_pcsound.c b/games/NXDoom/src/i_pcsound.c new file mode 100644 index 00000000000..2548fbe967b --- /dev/null +++ b/games/NXDoom/src/i_pcsound.c @@ -0,0 +1,236 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_pcsound.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System interface for PC speaker sound. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" + +#include "deh_str.h" +#include "i_sound.h" +#include "m_misc.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "pcsound.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TIMER_FREQ 1193181 /* Hz */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static boolean g_pcs_initialized = false; + +static gamemission_t g_gamemission; + +static uint8_t *g_current_sound_lump = NULL; +static uint8_t *g_current_sound_pos = NULL; +static unsigned int g_current_sound_remaining = 0; +static int g_current_sound_handle = 0; +static int g_current_sound_lump_num = -1; + +static const uint16_t g_divisors[] = +{ + 0, 6818, 6628, 6449, 6279, 6087, 5906, 5736, 5575, 5423, 5279, 5120, + 4971, 4830, 4697, 4554, 4435, 4307, 4186, 4058, 3950, 3836, 3728, 3615, + 3519, 3418, 3323, 3224, 3131, 3043, 2960, 2875, 2794, 2711, 2633, 2560, + 2485, 2415, 2348, 2281, 2213, 2153, 2089, 2032, 1975, 1918, 1864, 1810, + 1757, 1709, 1659, 1612, 1565, 1521, 1478, 1435, 1395, 1355, 1316, 1280, + 1242, 1207, 1173, 1140, 1107, 1075, 1045, 1015, 986, 959, 931, 905, + 879, 854, 829, 806, 783, 760, 739, 718, 697, 677, 658, 640, + 621, 604, 586, 570, 553, 538, 522, 507, 493, 479, 465, 452, + 439, 427, 415, 403, 391, 380, 369, 359, 348, 339, 329, 319, + 310, 302, 293, 285, 276, 269, 261, 253, 246, 239, 232, 226, + 219, 213, 207, 201, 195, 190, 184, 179, +}; + +static const snddevice_t g_sound_pcsound_devices[] = +{ + SNDDEVICE_PCSPEAKER, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const sound_module_t sound_pcsound_module = +{ + g_sound_pcsound_devices, + arrlen(g_sound_pcsound_devices), +#if 0 + i_pcs_init_sound, + i_pcs_shutdown_sound, + i_pcs_get_sfx_lumpnum, + i_pcs_update_sound, + i_pcs_update_sound_params, + i_pcs_start_sound, + i_pcs_stop_sound, + i_pcs_sound_is_playing, +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void pcs_callback_func(int *duration, int *freq) +{ +} + +static boolean cache_pcs_lump(sfxinfo_t *sfxinfo) +{ + int lumplen; + int headerlen; + + /* Free the current sound lump back to the cache */ + + if (g_current_sound_lump != NULL) + { + w_release_lump_num(g_current_sound_lump_num); + g_current_sound_lump = NULL; + } + + /* Load from WAD */ + + g_current_sound_lump = w_cache_lump_num(sfxinfo->lumpnum, PU_STATIC); + lumplen = w_lump_length(sfxinfo->lumpnum); + + /* Read header */ + + if (g_current_sound_lump[0] != 0x00 || g_current_sound_lump[1] != 0x00) + { + return false; + } + + headerlen = (g_current_sound_lump[3] << 8) | g_current_sound_lump[2]; + + if (headerlen > lumplen - 4) + { + return false; + } + + /* Header checks out ok */ + + g_current_sound_remaining = headerlen; + g_current_sound_pos = g_current_sound_lump + 4; + g_current_sound_lump_num = sfxinfo->lumpnum; + + return true; +} + +/* These Doom PC speaker sounds are not played - this can be seen in the + * Heretic source code, where there are remnants of this left over + * from Doom. + */ + +static boolean is_disabled_sound(sfxinfo_t *sfxinfo) +{ + int i; + const char *disabled_sounds[] = { + "posact", "bgact", "dmact", "dmpain", "popain", "sawidl", "rifle", + }; + + for (i = 0; i < arrlen(disabled_sounds); ++i) + { + if (!strcmp(sfxinfo->name, disabled_sounds[i])) + { + return true; + } + } + + return false; +} + +static int i_pcs_start_sound(sfxinfo_t *sfxinfo, int channel, int vol, + int sep, int pitch) +{ +} + +static void i_pcs_stop_sound(int handle) +{ +} + +/* Retrieve the raw data lump index for a given SFX name. */ + +static int i_pcs_get_sfx_lumpnum(sfxinfo_t *sfx) +{ + char namebuf[9]; + + if (g_gamemission == doom || g_gamemission == strife) + { + snprintf(namebuf, sizeof(namebuf), "dp%s", (sfx->name)); + + if (g_gamemission == strife && w_check_num_for_name(namebuf) == -1) + { + /* Missing sounds replaced with DPRIFLE. */ + + snprintf(namebuf, sizeof(namebuf), "dp%s", ("rifle")); + } + } + else + { + m_str_copy(namebuf, (sfx->name), sizeof(namebuf)); + } + + return w_get_num_for_name(namebuf); +} + +static boolean i_pcs_sound_is_playing(int handle) +{ + if (!g_pcs_initialized) + { + return false; + } + + if (handle != g_current_sound_handle) + { + return false; + } + + return g_current_sound_lump != NULL && g_current_sound_remaining > 0; +} + +static boolean i_pcs_init_sound(gamemission_t mission) +{ +} + +static void i_pcs_shutdown_sound(void) +{ +} + +static void i_pcs_update_sound(void) +{ + /* no-op. */ +} + +static void i_pcs_update_sound_params(int channel, int vol, int sep) +{ + /* no-op. */ +} diff --git a/games/NXDoom/src/i_sdlmusic.c b/games/NXDoom/src/i_sdlmusic.c new file mode 100644 index 00000000000..29af65a4e62 --- /dev/null +++ b/games/NXDoom/src/i_sdlmusic.c @@ -0,0 +1,149 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_sdlmusic.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System interface for music. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "config.h" +#include "doomtype.h" +#include "memio.h" +#include "mus2mid.h" + +#include "deh_str.h" +#include "gusconf.h" +#include "i_sound.h" +#include "i_swap.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" +#include "sha1.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static char *temp_timidity_cfg = NULL; + +/* putenv requires a non-const string whose lifetime is the whole program + * so can't use a string directly, have to do this silliness + */ + +static char sdl_mixer_disable_fluidsynth[] = + "SDL_MIXER_DISABLE_FLUIDSYNTH=1"; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +char *timidity_cfg_path = ""; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* If the temp_timidity_cfg config variable is set, generate a "wrapper" + * config file for Timidity to point to the actual config file. This + * is needed to inject a "dir" command so that the patches are read + * relative to the actual config file. + */ + +static boolean write_wrapper_timidity_config(char *write_path) +{ + char *path; + FILE *fstream; + + if (!strcmp(timidity_cfg_path, "")) + { + return false; + } + + fstream = fopen(write_path, "w"); + + if (fstream == NULL) + { + return false; + } + + path = m_dir_name(timidity_cfg_path); + fprintf(fstream, "dir %s\n", path); + free(path); + + fprintf(fstream, "source %s\n", timidity_cfg_path); + fclose(fstream); + + return true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void i_init_timidity_config(void) +{ + char *env_string; + boolean success; + + temp_timidity_cfg = m_temp_file("timidity.cfg"); + + if (snd_musicdevice == SNDDEVICE_GUS) + { + success = gus_write_config(temp_timidity_cfg); + } + else + { + success = write_wrapper_timidity_config(temp_timidity_cfg); + } + + /* Set the TIMIDITY_CFG environment variable to point to the temporary + * config file. + */ + + if (success) + { + env_string = m_string_join("TIMIDITY_CFG=", temp_timidity_cfg, NULL); + putenv(env_string); + + /* env_string deliberately not freed; see putenv manpage + * If we're explicitly configured to use Timidity (either through + * timidity_cfg_path or GUS mode), then disable Fluidsynth, because + * SDL_mixer considers Fluidsynth a higher priority than Timidity and + * therefore can end up circumventing Timidity entirely. + */ + + putenv(sdl_mixer_disable_fluidsynth); + } + else + { + free(temp_timidity_cfg); + temp_timidity_cfg = NULL; + } +} diff --git a/games/NXDoom/src/i_sound.c b/games/NXDoom/src/i_sound.c new file mode 100644 index 00000000000..e86a7e65bce --- /dev/null +++ b/games/NXDoom/src/i_sound.c @@ -0,0 +1,517 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_sound.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "config.h" +#include "doomtype.h" + +#include "gusconf.h" +#include "i_sound.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_config.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Sound sample rate to use for digital output (Hz) */ + +int snd_samplerate = 44100; + +/* Maximum number of bytes to dedicate to allocated sound effects. + * (Default: 64MB) + */ + +int snd_cachesize = 64 * 1024 * 1024; + +/* Config variable that controls the sound buffer size. + * We default to 28ms (1000 / 35fps = 1 buffer per tic). + */ + +int snd_maxslicetime_ms = 28; + +/* External command to invoke to play back music. */ + +char *snd_musiccmd = ""; + +/* Whether to vary the pitch of sound effects + * Each game will set the default differently + */ + +int snd_pitchshift = -1; + +int snd_musicdevice = SNDDEVICE_SB; +int snd_sfxdevice = SNDDEVICE_SB; + +/* Scale factor used when converting libsamplerate floating point numbers + * to integers. Too high means the sounds can clip; too low means they + * will be too quiet. This is an amount that should avoid clipping most + * of the time: with all the Doom IWAD sound effects, at least. If a PWAD + * is used, clipping might occur. + * + * NOTE: originally from i_sdlsound.c + */ + +float libsamplerate_scale = 0.65f; +int use_libsamplerate = 0; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Low-level sound and music modules we are using */ + +static const sound_module_t *sound_module; +static const music_module_t *music_module; + +/* If true, the music pack module was successfully initialized. */ + +static boolean music_packs_active = false; + +/* This is either equal to music_module or &music_pack_module, + * depending on whether the current track is substituted. + */ + +static const music_module_t *active_music_module; + +/* DOS-specific options: These are unused but should be maintained + * so that the config file can be shared between chocolate + * doom and doom.exe + */ + +static int snd_sbport = 0; +static int snd_sbirq = 0; +static int snd_sbdma = 0; +static int snd_mport = 0; + +/* Compiled-in sound modules: */ + +static const sound_module_t *sound_modules[] = +{ + &sound_pcsound_module, + NULL, +}; + +/* Compiled-in music modules: */ + +static const music_module_t *music_modules[] = +{ +#if 0 + &music_opl_module, +#endif + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Check if a sound device is in the given list of devices */ + +static boolean snd_device_in_list(snddevice_t device, + const snddevice_t *list, int len) +{ + int i; + + for (i = 0; i < len; ++i) + { + if (device == list[i]) + { + return true; + } + } + + return false; +} + +/* Find and initialize a sound_module_t appropriate for the setting + * in snd_sfxdevice. + */ + +static void init_sfx_module(gamemission_t mission) +{ + int i; + + sound_module = NULL; + + for (i = 0; sound_modules[i] != NULL; ++i) + { + /* Is the sfx device in the list of devices supported by + * this module? + */ + + if (snd_device_in_list(snd_sfxdevice, sound_modules[i]->sound_devices, + sound_modules[i]->num_sound_devices)) + { + /* initialize the module */ + + if (sound_modules[i]->init(mission)) + { + sound_module = sound_modules[i]; + return; + } + } + } +} + +/* initialize music according to snd_musicdevice. */ + +static void init_music_module(void) +{ + int i; + + music_module = NULL; + + for (i = 0; music_modules[i] != NULL; ++i) + { + /* Is the music device in the list of devices supported + * by this module? + */ + + if (snd_device_in_list(snd_musicdevice, + music_modules[i]->sound_devices, + music_modules[i]->num_sound_devices)) + { + /* initialize the module */ + + if (music_modules[i]->init()) + { + music_module = music_modules[i]; + return; + } + } + } +} + +static void check_volume_separation(int *vol, int *sep) +{ + if (*sep < 0) + { + *sep = 0; + } + else if (*sep > 254) + { + *sep = 254; + } + + if (*vol < 0) + { + *vol = 0; + } + else if (*vol > 127) + { + *vol = 127; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* initializes sound stuff, including volume + * Sets channels, SFX and music volume, allocates channel buffer, sets s_sfx + * lookup. + */ + +void i_init_sound(gamemission_t mission) +{ + boolean nosound, nosfx, nomusic, nomusicpacks; + + /* @vanilla + * + * Disable all sound output. + */ + + nosound = m_check_parm("-nosound") > 0; + + /* @vanilla + * + * Disable sound effects. + */ + + nosfx = m_check_parm("-nosfx") > 0; + + /* @vanilla + * + * Disable music. + */ + + nomusic = m_check_parm("-nomusic") > 0; + + /* Disable substitution music packs. */ + + nomusicpacks = m_parm_exists("-nomusicpacks"); + + /* Auto configure the music pack directory. */ + + m_set_music_pack_dir(); + + /* initialize the sound and music subsystems. */ + + if (!nosound && !screensaver_mode) + { + /* This is kind of a hack. If native MIDI is enabled, set up + * the TIMIDITY_CFG environment variable here before SDL_mixer + * is opened. + */ + + if (!nomusic && (snd_musicdevice == SNDDEVICE_GENMIDI || + snd_musicdevice == SNDDEVICE_GUS)) + { + i_init_timidity_config(); + } + + if (!nosfx) + { + init_sfx_module(mission); + } + + if (!nomusic) + { + init_music_module(); + active_music_module = music_module; + } + + /* We may also have substitute MIDIs we can load. */ + + if (!nomusicpacks && music_module != NULL) + { + music_packs_active = music_pack_module.init(); + } + } +} + +void i_shutdown_sound(void) +{ + if (sound_module != NULL) + { + sound_module->shutdown(); + } + + if (music_packs_active) + { + music_pack_module.shutdown(); + } + + if (music_module != NULL) + { + music_module->shutdown(); + } +} + +int i_get_sfx_lumpnum(sfxinfo_t *sfxinfo) +{ + if (sound_module != NULL) + { + return sound_module->get_sfx_lumpnum(sfxinfo); + } + else + { + return 0; + } +} + +void i_update_sound(void) +{ + if (sound_module != NULL) + { + sound_module->update(); + } + + if (active_music_module != NULL && active_music_module->poll != NULL) + { + active_music_module->poll(); + } +} + +void i_update_sound_params(int channel, int vol, int sep) +{ + if (sound_module != NULL) + { + check_volume_separation(&vol, &sep); + sound_module->update_sound_params(channel, vol, sep); + } +} + +int i_start_sound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, + int pitch) +{ + if (sound_module != NULL) + { + check_volume_separation(&vol, &sep); + return sound_module->start_sound(sfxinfo, channel, vol, sep, pitch); + } + else + { + return 0; + } +} + +void i_stop_sound(int channel) +{ + if (sound_module != NULL) + { + sound_module->stop_sound(channel); + } +} + +boolean i_sound_playing(int channel) +{ + if (sound_module != NULL) + { + return sound_module->sound_is_playing(channel); + } + else + { + return false; + } +} + +void i_precache_sounds(sfxinfo_t *sounds, int num_sounds) +{ + if (sound_module != NULL && sound_module->cache_sounds != NULL) + { + sound_module->cache_sounds(sounds, num_sounds); + } +} + +void i_init_music(void) +{ +} + +void i_shutdown_music(void) +{ +} + +void i_set_music_volume(int volume) +{ + if (music_module != NULL) + { + music_module->set_music_volume(volume); + + if (music_packs_active && music_module != &music_pack_module) + { + music_pack_module.set_music_volume(volume); + } + } +} + +void i_pause_song(void) +{ + if (active_music_module != NULL) + { + active_music_module->pause_music(); + } +} + +void i_resume_song(void) +{ + if (active_music_module != NULL) + { + active_music_module->resume_music(); + } +} + +void *i_register_song(void *data, int len) +{ + /* If the music pack module is active, check to see if there is a + * valid substitution for this track. If there is, we set the + * active_music_module pointer to the music pack module for the + * duration of this particular track. + */ + + if (music_packs_active) + { + void *handle; + + handle = music_pack_module.register_song(data, len); + if (handle != NULL) + { + active_music_module = &music_pack_module; + return handle; + } + } + + /* No substitution for this track, so use the main module. */ + + active_music_module = music_module; + if (active_music_module != NULL) + { + return active_music_module->register_song(data, len); + } + else + { + return NULL; + } +} + +void i_unregister_song(void *handle) +{ + if (active_music_module != NULL) + { + active_music_module->unregister_song(handle); + } +} + +void i_play_song(void *handle, boolean looping) +{ + if (active_music_module != NULL) + { + active_music_module->play_song(handle, looping); + } +} + +void i_stop_song(void) +{ + if (active_music_module != NULL) + { + active_music_module->stop_song(); + } +} + +void i_bind_sound_variables(void) +{ + m_bind_int_variable("snd_musicdevice", &snd_musicdevice); + m_bind_int_variable("snd_sfxdevice", &snd_sfxdevice); + m_bind_int_variable("snd_sbport", &snd_sbport); + m_bind_int_variable("snd_sbirq", &snd_sbirq); + m_bind_int_variable("snd_sbdma", &snd_sbdma); + m_bind_int_variable("snd_mport", &snd_mport); + m_bind_int_variable("snd_maxslicetime_ms", &snd_maxslicetime_ms); + m_bind_string_variable("snd_musiccmd", &snd_musiccmd); + m_bind_int_variable("snd_samplerate", &snd_samplerate); + m_bind_int_variable("snd_cachesize", &snd_cachesize); + m_bind_int_variable("snd_pitchshift", &snd_pitchshift); + + m_bind_string_variable("music_pack_path", &music_pack_path); + m_bind_string_variable("timidity_cfg_path", &timidity_cfg_path); + m_bind_string_variable("gus_patch_path", &gus_patch_path); + m_bind_int_variable("gus_ram_kb", &gus_ram_kb); + + m_bind_int_variable("use_libsamplerate", &use_libsamplerate); + m_bind_float_variable("libsamplerate_scale", &libsamplerate_scale); +} diff --git a/games/NXDoom/src/i_sound.h b/games/NXDoom/src/i_sound.h new file mode 100644 index 00000000000..d1ad8d8ebbd --- /dev/null +++ b/games/NXDoom/src/i_sound.h @@ -0,0 +1,316 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_sound.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * The not so system specific sound interface. + * + ****************************************************************************/ + +#ifndef __I_SOUND__ +#define __I_SOUND__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_mode.h" +#include "doomtype.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* so that the individual game logic and sound driver code agree */ + +#define NORM_PITCH 127 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* SoundFX struct. */ + +typedef struct sfxinfo_struct sfxinfo_t; + +struct sfxinfo_struct +{ + /* tag name, used for hexen. */ + + const char *tagname; + + /* lump name. If we are running with use_sfx_prefix=true, a + * 'DS' (or 'DP' for PC speaker sounds) is prepended to this. + */ + + char name[9]; + + /* Sfx priority */ + + int priority; + + /* referenced sound if a link */ + + sfxinfo_t *link; + + /* pitch if a link (Doom), whether to pitch-shift (Hexen) */ + + int pitch; + + /* volume if a link */ + + int volume; + + /* 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; + + /* Maximum number of channels that the sound can be played on + * (Heretic) + */ + + int numchannels; + + /* data used by the low level code */ + + void *driver_data; +}; + +/* MusicInfo struct. */ + +typedef struct +{ + /* up to 6-character name */ + + const char *name; + + /* lump number of music */ + + int lumpnum; + + /* music data */ + + void *data; + + /* music handle once registered */ + + void *handle; +} musicinfo_t; + +typedef enum +{ + SNDDEVICE_NONE = 0, + SNDDEVICE_PCSPEAKER = 1, + SNDDEVICE_ADLIB = 2, + SNDDEVICE_SB = 3, + SNDDEVICE_PAS = 4, + SNDDEVICE_GUS = 5, + SNDDEVICE_WAVEBLASTER = 6, + SNDDEVICE_SOUNDCANVAS = 7, + SNDDEVICE_GENMIDI = 8, + SNDDEVICE_AWE32 = 9, + SNDDEVICE_CD = 10, + SNDDEVICE_FSYNTH = 11, +} snddevice_t; + +/* Interface for sound modules */ + +typedef struct +{ + /* List of sound devices that this sound module is used for. */ + + const snddevice_t *sound_devices; + int num_sound_devices; + + /* initialise sound module + * Returns true if successfully initialised + */ + + boolean (*init)(gamemission_t mission); + + /* shutdown sound module */ + + void (*shutdown)(void); + + /* Returns the lump index of the given sound. */ + + int (*get_sfx_lumpnum)(sfxinfo_t *sfxinfo); + + /* Called periodically to update the subsystem. */ + + void (*update)(void); + + /* Update the sound settings on the given channel. */ + + void (*update_sound_params)(int channel, int vol, int sep); + + /* Start a sound on a given channel. Returns the channel id + * or -1 on failure. + */ + + int (*start_sound)(sfxinfo_t *sfxinfo, int channel, int vol, int sep, + int pitch); + + /* Stop the sound playing on the given channel. */ + + void (*stop_sound)(int channel); + + /* Query if a sound is playing on the given channel */ + + boolean (*sound_is_playing)(int channel); + + /* Called on startup to precache sound effects (if necessary) */ + + void (*cache_sounds)(sfxinfo_t *sounds, int num_sounds); +} sound_module_t; + +/* Interface for music modules */ + +typedef struct +{ + /* List of sound devices that this music module is used for. */ + + const snddevice_t *sound_devices; + int num_sound_devices; + + /* initialise the music subsystem */ + + boolean (*init)(void); + + /* shutdown the music subsystem */ + + void (*shutdown)(void); + + /* Set music volume - range 0-127 */ + + void (*set_music_volume)(int volume); + + /* Pause music */ + + void (*pause_music)(void); + + /* Un-pause music */ + + void (*resume_music)(void); + + /* Register a song handle from data + * Returns a handle that can be used to play the song + */ + + void *(*register_song)(void *data, int len); + + /* Un-register (free) song data */ + + void (*unregister_song)(void *handle); + + /* Play the song */ + + void (*play_song)(void *handle, boolean looping); + + /* Stop playing the current song. */ + + void (*stop_song)(void); + + /* Query if music is playing. */ + + boolean (*music_is_playing)(void); + + /* Invoked periodically to poll. */ + + void (*poll)(void); +} music_module_t; + +#if 0 +/* DMX version to emulate for OPL emulation: */ + +typedef enum +{ + opl_doom1_1_666, /* Doom 1 v1.666 */ + opl_doom2_1_666, /* Doom 2 v1.666, Hexen, Heretic */ + opl_doom_1_9 /* Doom v1.9, Strife */ +} opl_driver_ver_t; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int snd_sfxdevice; +extern int snd_musicdevice; +extern int snd_samplerate; +extern int snd_cachesize; +extern int snd_maxslicetime_ms; +extern char *snd_musiccmd; +extern int snd_pitchshift; +extern int use_libsamplerate; +extern float libsamplerate_scale; + +extern const sound_module_t sound_sdl_module; +extern const sound_module_t sound_pcsound_module; +extern const music_module_t music_sdl_module; +extern const music_module_t music_pack_module; +extern const music_module_t music_fl_module; + +/* For native music module: */ + +extern char *music_pack_path; +extern char *timidity_cfg_path; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void i_init_sound(gamemission_t mission); +void i_shutdown_sound(void); +int i_get_sfx_lumpnum(sfxinfo_t *sfxinfo); +void i_update_sound(void); +void i_update_sound_params(int channel, int vol, int sep); +int i_start_sound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, + int pitch); +void i_stop_sound(int channel); +boolean i_sound_playing(int channel); +void i_precache_sounds(sfxinfo_t *sounds, int num_sounds); + +void i_init_music(void); +void i_shutdown_music(void); +void i_set_music_volume(int volume); +void i_pause_song(void); +void i_resume_song(void); +void *i_register_song(void *data, int len); +void i_unregister_song(void *handle); +void i_play_song(void *handle, boolean looping); +void i_stop_song(void); + +void i_bind_sound_variables(void); + +#if 0 +void i_set_opl_driver_ver(opl_driver_ver_t ver); +void i_opl_dev_messages(char *, size_t); +#endif + +/* Sound modules */ + +void i_init_timidity_config(void); + +#endif /* __I_SOUND__ */ diff --git a/games/NXDoom/src/i_swap.h b/games/NXDoom/src/i_swap.h new file mode 100644 index 00000000000..72b64236f4d --- /dev/null +++ b/games/NXDoom/src/i_swap.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_swap.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Endianness handling, swapping 16bit and 32bit. + * + ****************************************************************************/ + +#ifndef __I_SWAP__ +#define __I_SWAP__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Endianness handling. + * WAD files are stored little endian. + */ + +/* These are deliberately cast to signed values; this is the behaviour + * of the macros in the original source and some code relies on it. + */ + +#define SHORT(x) ((signed short)le16toh(x)) +#define LONG(x) ((signed int)le32toh(x)) + +/* Defines for checking the endianness of the system. */ + +#if BYTE_ORDER == BIG_ENDIAN +#define SYS_BIG_ENDIAN +#endif + +#endif /* __I_SWAP__ */ diff --git a/games/NXDoom/src/i_system.c b/games/NXDoom/src/i_system.c new file mode 100644 index 00000000000..b3867e0add9 --- /dev/null +++ b/games/NXDoom/src/i_system.c @@ -0,0 +1,482 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_system.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "config.h" + +#include "deh_str.h" +#include "doomtype.h" +#include "i_joystick.h" +#include "i_sound.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" + +#include "i_system.h" + +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DEFAULT_RAM 16 /* MiB */ +#define MIN_RAM 4 /* MiB */ + +#define DOS_MEM_DUMP_SIZE 10 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct atexit_listentry_s atexit_listentry_t; + +struct atexit_listentry_s +{ + atexit_func_t func; + boolean run_on_error; + atexit_listentry_t *next; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static atexit_listentry_t *exit_funcs = NULL; + +static boolean already_quitting = false; + +/* Read Access Violation emulation. + * + * From PrBoom+, by entryway. + */ + +/* C:\>debug + * -d 0:0 + * + * DOS 6.22: + * 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) + * DOS 7.1: + * 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) + * Win98: + * 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) + * DOSBox under XP: + * 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) + */ + +static const unsigned char mem_dump_dos622[DOS_MEM_DUMP_SIZE] = +{ + 0x57, 0x92, 0x19, 0x00, 0xf4, 0x06, 0x70, 0x00, 0x16, 0x00, +}; + +static const unsigned char mem_dump_win98[DOS_MEM_DUMP_SIZE] = +{ + 0x9e, 0x0f, 0xc9, 0x00, 0x65, 0x04, 0x70, 0x00, 0x16, 0x00, +}; + +static const unsigned char mem_dump_dosbox[DOS_MEM_DUMP_SIZE] = +{ + 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, +}; + +static unsigned char mem_dump_custom[DOS_MEM_DUMP_SIZE]; + +static const unsigned char *dos_mem_dump = mem_dump_dos622; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Zone memory auto-allocation function that allocates the zone size + * by trying progressively smaller zone sizes until one is found that + * works. + */ + +static byte *auto_alloc_memory(int *size, int default_ram, int min_ram) +{ + byte *zonemem; + + /* Allocate the zone memory. This loop tries progressively smaller + * zone sizes until a size is found that can be allocated. + * If we used the -mb command line parameter, only the parameter + * provided is accepted. + */ + + zonemem = NULL; + + while (zonemem == NULL) + { + /* We need a reasonable minimum amount of RAM to start. */ + + if (default_ram < min_ram) + { + i_error("Unable to allocate %i MiB of RAM for zone", default_ram); + } + + /* Try to allocate the zone memory. */ + + *size = default_ram * 1024 * 1024; + + zonemem = malloc(*size); + + /* Failed to allocate? Reduce zone size until we reach a size + * that is acceptable. + */ + + if (zonemem == NULL) + { + default_ram -= 1; + } + } + + return zonemem; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void i_at_exit(atexit_func_t func, boolean run_on_error) +{ + atexit_listentry_t *entry; + + entry = malloc(sizeof(*entry)); + + entry->func = func; + entry->run_on_error = run_on_error; + entry->next = exit_funcs; + exit_funcs = entry; +} + +/* Tactile feedback function, probably used for the Logitech Cyberman */ + +void i_tactile(int on, int off, int total) +{ +} + +byte *i_zone_base(int *size) +{ + byte *zonemem; + int min_ram; + int default_ram; + int p; + + /* @category obscure + * @arg + * + * Specify the heap size, in MiB. + */ + + p = m_check_parm_with_args("-mb", 1); + + if (p > 0) + { + default_ram = atoi(myargv[p + 1]); + min_ram = default_ram; + } + else + { + /* Because of the 8-byte pointer size in a 64-bit build, the default + * heap size (16 MiB) is insufficient compared to a 32-bit build. For + * example, the Alien Vendetta avm62402.lmp demo completes successfully + * on a 32-bit build, but terminates with an out of memory error on a + * 64-bit build. Therefore, to maintain consistency with a 32-bit + * build, the heap size should be increased. + */ + + if (sizeof(void *) == 8) + { + default_ram = DEFAULT_RAM * 2; + } + else + { + default_ram = DEFAULT_RAM; + } + + min_ram = MIN_RAM; + } + + zonemem = auto_alloc_memory(size, default_ram, min_ram); + + printf("zone memory: %p, %x allocated for zone\n", zonemem, *size); + + return zonemem; +} + +void i_print_banner(const char *msg) +{ + int i; + int spaces = 35 - (strlen(msg) / 2); + + for (i = 0; i < spaces; ++i) + putchar(' '); + + puts(msg); +} + +void i_print_divider(void) +{ + int i; + + for (i = 0; i < 75; ++i) + { + putchar('='); + } + + putchar('\n'); +} + +void i_print_startup_banner(const char *gamedescription) +{ + i_print_divider(); + i_print_banner(gamedescription); + i_print_divider(); + + printf( + " " PACKAGE_NAME + " is free software, covered by the GNU General Public\n" + " License. There is NO warranty; not even for MERCHANTABILITY or " + "FITNESS\n" + " FOR A PARTICULAR PURPOSE. You are welcome to change and distribute\n" + " copies under certain conditions. See the source for more " + "information.\n"); + + i_print_divider(); +} + +/* i_console_stdout + * + * Returns true if stdout is a real console, false if it is a file + */ + +boolean i_console_stdout(void) +{ + return isatty(fileno(stdout)); +} + +/* i_init */ + +#if 0 +void i_init(void) +{ + i_check_is_screensaver(); + i_init_timer(); + i_init_joystick(); +} + +void i_bind_variables(void) +{ + i_bind_video_variables(); + i_bind_joystick_variables(); + i_bind_sound_variables(); +} +#endif + +/* i_quit */ + +void i_quit(void) +{ + atexit_listentry_t *entry; + + /* Run through all exit functions */ + + entry = exit_funcs; + + while (entry != NULL) + { + entry->func(); + entry = entry->next; + } + + exit(0); +} + +void i_error(const char *error, ...) +{ + char msgbuf[512]; + va_list argptr; + atexit_listentry_t *entry; + boolean exit_gui_popup; + + if (already_quitting) + { + fprintf(stderr, "Warning: recursive call to i_error detected.\n"); + exit(-1); + } + else + { + already_quitting = true; + } + + /* Message first. */ + + va_start(argptr, error); + + vfprintf(stderr, error, argptr); + fprintf(stderr, "\n\n"); + va_end(argptr); + fflush(stderr); + + /* Write a copy of the message into buffer. */ + + va_start(argptr, error); + memset(msgbuf, 0, sizeof(msgbuf)); + vsnprintf(msgbuf, sizeof(msgbuf), error, argptr); + va_end(argptr); + + /* Shutdown. Here might be other errors. */ + + entry = exit_funcs; + + while (entry != NULL) + { + if (entry->run_on_error) + { + entry->func(); + } + + entry = entry->next; + } + + /* @category obscure + * + * If specified, don't show a GUI window for error messages when the + * game exits with an error. + */ + + exit_gui_popup = !m_parm_exists("-nogui"); + + /* Pop up a GUI dialog box to show the error message, if the + * game was not run from the console (and the user will + * therefore be unable to otherwise see the message). + */ + + if (exit_gui_popup && !i_console_stdout()) + { + } + + /* abort(); */ + + exit(-1); +} + +void *i_realloc(void *ptr, size_t size) +{ + void *new_ptr; + + new_ptr = realloc(ptr, size); + + if (size != 0 && new_ptr == NULL) + { + i_error("i_realloc: failed on reallocation of %zu bytes", size); + } + + return new_ptr; +} + +boolean i_get_memory_value(unsigned int offset, void *value, int size) +{ + static boolean firsttime = true; + + if (firsttime) + { + int p; + int i; + int val; + + firsttime = false; + i = 0; + + /* @category compat + * @arg + * + * Specify DOS version to emulate for NULL pointer dereference + * emulation. Supported versions are: dos622, dos71, dosbox. + * The default is to emulate DOS 7.1 (Windows 98). + */ + + p = m_check_parm_with_args("-setmem", 1); + + if (p > 0) + { + if (!strcasecmp(myargv[p + 1], "dos622")) + { + dos_mem_dump = mem_dump_dos622; + } + + if (!strcasecmp(myargv[p + 1], "dos71")) + { + dos_mem_dump = mem_dump_win98; + } + else if (!strcasecmp(myargv[p + 1], "dosbox")) + { + dos_mem_dump = mem_dump_dosbox; + } + else + { + for (i = 0; i < DOS_MEM_DUMP_SIZE; ++i) + { + ++p; + + if (p >= myargc || myargv[p][0] == '-') + { + break; + } + + m_str_to_int(myargv[p], &val); + mem_dump_custom[i++] = (unsigned char)val; + } + + dos_mem_dump = mem_dump_custom; + } + } + } + + switch (size) + { + case 1: + *((unsigned char *)value) = dos_mem_dump[offset]; + return true; + case 2: + *((unsigned short *)value) = + dos_mem_dump[offset] | (dos_mem_dump[offset + 1] << 8); + return true; + case 4: + *((unsigned int *)value) = + dos_mem_dump[offset] | (dos_mem_dump[offset + 1] << 8) | + (dos_mem_dump[offset + 2] << 16) | + (dos_mem_dump[offset + 3] << 24); + return true; + } + + return false; +} diff --git a/games/NXDoom/src/i_system.h b/games/NXDoom/src/i_system.h new file mode 100644 index 00000000000..ae1c568068b --- /dev/null +++ b/games/NXDoom/src/i_system.h @@ -0,0 +1,107 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_system.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __I_SYSTEM__ +#define __I_SYSTEM__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "d_ticcmd.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*atexit_func_t)(void); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Called by DoomMain. */ + +void i_init(void); + +/* Called by startup code to get the amount of memory to malloc for the zone + * management. + */ + +byte *i_zone_base(int *size); + +boolean i_console_stdout(void); + +/* Asynchronous interrupt functions should maintain private queues + * that are read by the synchronous functions + * to be converted into events. + */ + +#if 0 /* Unused */ + +/* 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_base_ticcmd(void); +#endif + +/* Called by m_responder when quit is selected. + * Clean exit, displays sell blurb. + */ + +void i_quit(void) NORETURN; + +void i_error(const char *error, ...) NORETURN PRINTF_ATTR(1, 2); + +void i_tactile(int on, int off, int total); + +void *i_realloc(void *ptr, size_t size); + +boolean i_get_memory_value(unsigned int offset, void *value, int size); + +/* Schedule a function to be called when the program exits. + * If run_if_error is true, the function is called if the exit + * is due to an error (i_error) + */ + +void i_at_exit(atexit_func_t func, boolean run_if_error); + +/* Add all system-specific config file variable bindings. */ + +void i_bind_variables(void); + +/* Print startup banner copyright message. */ + +void i_print_startup_banner(const char *gamedescription); + +/* Print a centered text banner displaying the given string. */ + +void i_print_banner(const char *text); + +/* Print a dividing line for startup banners. */ + +void i_print_divider(void); + +#endif /* __I_SYSTEM__ */ diff --git a/games/NXDoom/src/i_timer.c b/games/NXDoom/src/i_timer.c new file mode 100644 index 00000000000..abde6d4c9fc --- /dev/null +++ b/games/NXDoom/src/i_timer.c @@ -0,0 +1,107 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_timer.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Timer functions. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" +#include "i_timer.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The start time of the game used as the base for tic calculations. */ + +static struct timespec basetime = +{ + .tv_nsec = 0, .tv_sec = 0 +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: i_get_time + * + * Description: + * Called by D_DoomLoop. + * + * Returns: + * The current time in 1/35th second tics. + ****************************************************************************/ + +int i_get_time(void) +{ + return (i_get_time_ms() * TICRATE) / 1000; +} + +/**************************************************************************** + * Name: i_get_time_ms + * + * Returns: + * The current time in ms. + ****************************************************************************/ + +int i_get_time_ms(void) +{ + struct timespec curtime; + + /* NOTE: we ignore any possible error here */ + + clock_gettime(CLOCK_MONOTONIC, &curtime); + if (basetime.tv_sec == 0 && basetime.tv_nsec == 0) + { + clock_gettime(CLOCK_MONOTONIC, &basetime); + } + + return (clock_time2usec(&curtime) - clock_time2usec(&basetime)) / 1000; +} + +/**************************************************************************** + * Name: i_init_timer + * + * Description: + * Initialize timer. + ****************************************************************************/ + +void i_init_timer(void) +{ +} + +/**************************************************************************** + * Name: i_wait_vbl + * + * Description: + * Wait for vertical retrace or pause a bit. + ****************************************************************************/ + +void i_wait_vbl(int count) +{ + usleep((count * 1000000) / 70); +} diff --git a/games/NXDoom/src/i_timer.h b/games/NXDoom/src/i_timer.h new file mode 100644 index 00000000000..eb9d7756c37 --- /dev/null +++ b/games/NXDoom/src/i_timer.h @@ -0,0 +1,76 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_timer.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System-specific timer interface + * + ****************************************************************************/ + +#ifndef __I_TIMER__ +#define __I_TIMER__ + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#define TICRATE 35 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: i_get_time + * + * Description: + * Called by d_doomloop. + * + * Returns: + * The current time in tics. + ****************************************************************************/ + +int i_get_time(void); + +/**************************************************************************** + * Name: i_get_time_ms + * + * Returns: + * The current time in ms. + ****************************************************************************/ + +int i_get_time_ms(void); + +/**************************************************************************** + * Name: i_init_timer + * + * Description: + * Initialize timer. + ****************************************************************************/ + +void i_init_timer(void); + +/**************************************************************************** + * Name: i_wait_vbl + * + * Description: + * Wait for vertical retrace or pause a bit. + ****************************************************************************/ + +void i_wait_vbl(int count); + +#endif /* __I_TIMER__ */ diff --git a/games/NXDoom/src/i_video.c b/games/NXDoom/src/i_video.c new file mode 100644 index 00000000000..0d3b41af30e --- /dev/null +++ b/games/NXDoom/src/i_video.c @@ -0,0 +1,832 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_video.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * DOOM graphics stuff for SDL. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "config.h" +#include "d_loop.h" +#include "deh_str.h" +#include "doomtype.h" +#include "i_input.h" +#include "i_joystick.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" +#include "tables.h" +#include "v_diskicon.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#define RESIZE_DELAY 500 + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct graphics_state_s +{ + int fd; /* File descriptor handle to frame buffer */ + + /* The 320x200x32 RGBA intermediate buffer is what we blit the former + * buffer to. On NuttX, this is the frame buffer memory `fbmem`. It may not + * have 32-bit depth, but if it doesn't, the code is adjusted accordingly. + */ + + FAR void *fbmem; + + /* 8-bit depth screen buffer (320x200x8) that we draw to (i.e. the one that + * holds i_video_buffer) + */ + + pixel_t *scrnbuf; + + /* Information about the frame buffer needed for rendering. */ + + struct fb_videoinfo_s vinfo; + struct fb_planeinfo_s pinfo; + + /* Scale multiplier for rendering large image */ + + uint8_t scale; + + bool inited; /* Track initialization */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* NuttX graphics state */ + +static struct graphics_state_s g_graphics_state = +{ + 0 +}; + +/* Window title */ + +static const char *g_window_title = ""; + +/* Colour palette map from 8-bit colour to 32-bit */ + +static struct argbcolor_s g_palette[256]; + +static boolean palette_to_set; + +/* disable mouse? */ + +static boolean nomouse = false; + +/* Maximum number of pixels to use for intermediate scale buffer. */ + +static int max_scaling_buffer_pixels = 16000000; + +/* Time to wait for the screen to settle on startup before starting the game + * (ms) + */ + +static int startup_delay = 1000; + +/* Grab the mouse? (int type for config code). nograbmouse_override allows + * this to be temporarily disabled via the command line. + */ + +static int grabmouse = true; +static boolean nograbmouse_override = false; + +/* If true, we display dots at the bottom of the screen to + * indicate FPS. + */ + +static boolean display_fps_dots; + +/* If this is true, the screen is rendered but not blitted to the + * video buffer. + */ + +static boolean noblit; + +/* Callback function to invoke to determine whether to grab the + * mouse pointer. + */ + +static grabmouse_callback_t grabmouse_callback = NULL; + +/* Does the window currently have focus? */ + +static boolean window_focused = true; + +/* Window resize state. */ + +#if 0 +static boolean need_resize = false; +static unsigned int last_resize_time; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int usemouse = 1; + +/* Save screenshots in PNG format. */ + +int png_screenshots = 0; + +/* SDL video driver name */ + +char *video_driver = ""; + +/* Window position: */ + +char *window_position = "center"; + +/* SDL display number on which to run. */ + +int video_display = 0; + +/* Screen width and height, from configuration file. */ + +int window_width = 320; +int window_height = 200; + +/* Fullscreen mode, 0x0 for SDL_WINDOW_FULLSCREEN_DESKTOP. */ + +int fullscreen_width = 0; +int fullscreen_height = 0; + +/* Run in full screen mode? (int type for config code) */ + +int fullscreen = true; + +/* Smooth pixel scaling */ + +int smooth_pixel_scaling = true; + +/* Force integer scales for resolution-independent rendering */ + +int integer_scaling = false; + +/* VGA Porch palette change emulation */ + +int vga_porch_flash = false; + +/* Force software rendering, for systems which lack effective hardware + * acceleration + */ + +int force_software_renderer = false; + +/* The screen buffer; this is modified to draw things to the screen */ + +pixel_t *i_video_buffer = NULL; + +/* If true, game is running as a screensaver */ + +boolean screensaver_mode = false; + +/* Flag indicating whether the screen is currently visible: + * when the screen isn't visible, don't render the screen + */ + +boolean screenvisible = true; + +/* Gamma correction level to use */ + +int usegamma = 0; + +/* Joystick/gamepad hysteresis */ + +unsigned int joywait = 0; + +/* TODO: I'm sure more of the variables above can be private */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: blit_screen + * + * Description: + * Blit the 8-bit depth buffer that DOOM renders to onto the frame buffer + * in a higher colour depth. + * + ****************************************************************************/ + +static void blit_screen(void) +{ + uint8_t p_idx; + void *fbptr; + + /* TODO: It would be best to do this more efficiently/with less memory. + * It also would be good if we could handle the palette translation here + * such that DOOM can be played on frame buffers with differing bit depths + * and pixel formats. + */ + + fbptr = g_graphics_state.fbmem; + for (unsigned y = 0; y < SCREENHEIGHT * g_graphics_state.scale; y++) + { + for (unsigned x = 0; x < SCREENWIDTH * g_graphics_state.scale; x++) + { + p_idx = g_graphics_state + .scrnbuf[(y / g_graphics_state.scale) * SCREENWIDTH + + (x / g_graphics_state.scale)]; + + ((uint32_t *)(fbptr))[x] = + ARGBTO32(g_palette[p_idx].a, g_palette[p_idx].r, + g_palette[p_idx].g, g_palette[p_idx].b); + } + + fbptr += g_graphics_state.pinfo.stride; + } +} + +static void update_grab(void) +{ +} + +static void set_video_mode(void) +{ +} + +static void i_get_event(void) +{ + int err; +#if CONFIG_GAMES_NXDOOM_KEYBOARD + struct keyboard_event_s kbdevent; + + while ((err = get_kbd_event(&kbdevent)) == 0) + { + switch (kbdevent.type) + { + case KEYBOARD_PRESS: + + /* deliberate fall-though */ + + case KEYBOARD_RELEASE: + i_handle_keyboard_event(&kbdevent); + break; + + default: + break; + } + } +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void i_set_grab_mouse_callback(grabmouse_callback_t func) +{ + grabmouse_callback = func; +} + +/* Set the variable controlling FPS dots. */ + +void i_display_fps_dots(boolean dots_on) +{ + display_fps_dots = dots_on; +} + +void i_shutdown_graphics(void) +{ + if (!g_graphics_state.inited) + { + return; + } + + close(g_graphics_state.fd); + munmap(g_graphics_state.fbmem, g_graphics_state.pinfo.fblen); + free(g_graphics_state.scrnbuf); + g_graphics_state.inited = false; +} + +void i_start_frame(void) +{ + /* er? */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void i_start_tic(void) +{ + if (!g_graphics_state.inited) + { + return; + } + + i_get_event(); + + if (usemouse && !nomouse && window_focused) + { + i_read_mouse(); + } + + if (joywait < i_get_time()) + { + i_update_joystick(); + } +} + +void i_update_no_blit(void) +{ + /* what is this? */ +} + +void i_finish_update(void) +{ + static int lasttic; + int tics; + int i; + + if (!g_graphics_state.inited) return; + + if (noblit) return; + + /* draws little dots on the bottom of the screen */ + + if (display_fps_dots) + { + i = i_get_time(); + tics = i - lasttic; + lasttic = i; + if (tics > 20) tics = 20; + + for (i = 0; i < tics * 4; i += 4) + i_video_buffer[(SCREENHEIGHT - 1) * SCREENWIDTH + i] = 0xff; + for (; i < 20 * 4; i += 4) + i_video_buffer[(SCREENHEIGHT - 1) * SCREENWIDTH + i] = 0x0; + } + + /* Draw disk icon before blit, if necessary. */ + + v_draw_disk_icon(); + + if (palette_to_set) + { + palette_to_set = false; + } + + blit_screen(); + + /* Draw! */ + + /* Restore background and undo the disk indicator, if it was drawn. */ + + v_restore_disk_background(); +} + +void i_read_screen(pixel_t *scr) +{ + memcpy(scr, i_video_buffer, SCREENWIDTH * SCREENHEIGHT * sizeof(*scr)); +} + +/**************************************************************************** + * Name: i_set_palette + ****************************************************************************/ + +void i_set_palette(byte *doompalette) +{ + for (int i = 0; i < 256; ++i) + { + /* Zero out the bottom two bits of each channel - the PC VGA + * controller only supports 6 bits of accuracy. + */ + + g_palette[i].a = 0xffu; + g_palette[i].r = gammatable[usegamma][*doompalette++] & ~3; + g_palette[i].g = gammatable[usegamma][*doompalette++] & ~3; + g_palette[i].b = gammatable[usegamma][*doompalette++] & ~3; + } + + palette_to_set = true; +} + +/**************************************************************************** + * Name: i_get_palette_index + * + * Description: + * Given an RGB value, find the closest matching palette index. + * + * Return: + * An index into the palette lookup table for the best match. + * + ****************************************************************************/ + +int i_get_palette_index(int r, int g, int b) +{ + int best = 0; + int best_diff = INT_MAX; + int diff; + + for (int i = 0; i < 256; ++i) + { + diff = (r - g_palette[i].r) * (r - g_palette[i].r) + + (g - g_palette[i].g) * (g - g_palette[i].g) + + (b - g_palette[i].b) * (b - g_palette[i].b); + + if (diff < best_diff) + { + best = i; + best_diff = diff; + } + + if (diff == 0) + { + break; + } + } + + return best; +} + +/**************************************************************************** + * Name: i_set_window_title + * + * Description: + * Set the window title internally. + * + ****************************************************************************/ + +void i_set_window_title(const char *title) +{ + g_window_title = title; +} + +/**************************************************************************** + * Name: i_init_window_title + * + * Description: + * Actually cause the window title to update with whatever window title was + * last set via i_set_window_title. + * + ****************************************************************************/ + +void i_init_window_title(void) +{ +} + +/**************************************************************************** + * Name: i_set_window_title + ****************************************************************************/ + +void i_graphics_check_commandline(void) +{ + int i; + + /* @category video + * @vanilla + * + * Disable blitting the screen. + */ + + noblit = m_check_parm("-noblit"); + + /* @category video + * + * Don't grab the mouse when running in windowed mode. + */ + + nograbmouse_override = m_parm_exists("-nograbmouse"); + + /* default to fullscreen mode, allow override with command line + * nofullscreen because we love prboom + */ + + /* @category video + * + * Run in a window. + */ + + if (m_check_parm("-window") || m_check_parm("-nofullscreen")) + { + fullscreen = false; + } + + /* @category video + * + * Run in fullscreen mode. + */ + + if (m_check_parm("-fullscreen")) + { + fullscreen = true; + } + + /* @category video + * + * Disable the mouse. + */ + + nomouse = m_check_parm("-nomouse") > 0; + + /* @category video + * @arg + * + * Specify the screen width, in pixels. Implies -window. + */ + + i = m_check_parm_with_args("-width", 1); + + if (i > 0) + { + window_width = atoi(myargv[i + 1]); + fullscreen = false; + } + + /* @category video + * @arg + * + * Specify the screen height, in pixels. Implies -window. + */ + + i = m_check_parm_with_args("-height", 1); + + if (i > 0) + { + window_height = atoi(myargv[i + 1]); + fullscreen = false; + } + + /* @category video + * @arg + * + * Specify the dimensions of the window. Implies -window. + */ + + i = m_check_parm_with_args("-geometry", 1); + + if (i > 0) + { + int w; + int h; + int s; + + s = sscanf(myargv[i + 1], "%ix%i", &w, &h); + if (s == 2) + { + window_width = w; + window_height = h; + fullscreen = false; + } + } + + /* @category video + * @arg + * + * Specify the display number on which to show the screen. + */ + + i = m_check_parm_with_args("-display", 1); + + if (i > 0) + { + int display = atoi(myargv[i + 1]); + if (display >= 0) + { + video_display = display; + } + } +} + +/* Check if we have been invoked as a screensaver by xscreensaver. */ + +void i_check_is_screensaver(void) +{ + char *env; + + env = getenv("XSCREENSAVER_WINDOW"); + + if (env != NULL) + { + screensaver_mode = true; + } +} + +/* Check the display bounds of the display referred to by 'video_display' and + * set x and y to a location that places the window in the center of that + * display. + */ + +static void center_window(int *x, int *y, int w, int h) +{ + *x = MAX((g_graphics_state.vinfo.xres - w) / 2, 0); + *y = MAX((g_graphics_state.vinfo.yres - h) / 2, 0); +} + +void i_get_window_position(int *x, int *y, int w, int h) +{ + /* in fullscreen mode, the window "position" still matters, because + * we use it to control which display we run fullscreen on. + */ + + if (fullscreen) + { + center_window(x, y, w, h); + return; + } +} + +void i_init_graphics(void) +{ + uint8_t xscale; + uint8_t yscale; + int err; + byte *doompal; + + /* Open frame buffer */ + + g_graphics_state.fd = open(CONFIG_GAMES_NXDOOM_FBPATH, O_RDWR); + if (g_graphics_state.fd < 0) + { + i_error("Failed to open frame buffer: %d", errno); + } + + /* Get frame buffer characteristics */ + + err = ioctl(g_graphics_state.fd, FBIOGET_VIDEOINFO, + (unsigned long)(uintptr_t)&g_graphics_state.vinfo); + if (err < 0) + { + close(g_graphics_state.fd); + i_error("Failed to get video info: %d", errno); + } + + /* Here, we check the dimensions of the frame buffer. If we have enough + * space to scale up the rendered image in both width and height, record + * that so we can make use of it elsewhere. + * + * If we don't have enough frame buffer space for the game, quit! + */ + + if (g_graphics_state.vinfo.xres < SCREENWIDTH) + { + i_error("Resolution width of %u px < minimum of %u px\n", + g_graphics_state.vinfo.xres, SCREENWIDTH); + } + + if (g_graphics_state.vinfo.yres < SCREENHEIGHT) + { + i_error("Resolution height of %u px < minimum of %u px\n", + g_graphics_state.vinfo.yres, SCREENHEIGHT); + } + + xscale = g_graphics_state.vinfo.xres / SCREENWIDTH; + yscale = g_graphics_state.vinfo.yres / SCREENHEIGHT; + g_graphics_state.scale = xscale > yscale ? yscale : xscale; + + /* Get frame buffer plane info */ + + if (ioctl(g_graphics_state.fd, FBIOGET_PLANEINFO, + (unsigned long)((uintptr_t)&g_graphics_state.pinfo)) < 0) + { + i_error("ioctl(FBIOGET_PLANEINFO) failed: %d\n", errno); + } + + /* Initialize frame buffer memory for actual rendering */ + + g_graphics_state.fbmem = + mmap(NULL, g_graphics_state.pinfo.fblen, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FILE, g_graphics_state.fd, 0); + if (g_graphics_state.fbmem == MAP_FAILED) + { + i_error("mmap() of frame buffer failed: %d\n", errno); + } + + /* Create an 8-bit depth screen buffer for DOOM to render to */ + + g_graphics_state.scrnbuf = malloc(SCREENWIDTH * SCREENHEIGHT); + if (g_graphics_state.scrnbuf == NULL) + { + i_error("Couldn't allocate screen buffer: %d\n", errno); + } + + /* Create the game window; this may switch graphic modes depending + * on configuration. + * AdjustWindowSize(); + */ + + set_video_mode(); + + /* Start with a clear black screen + * (screen will be flipped after we set the palette) + */ + + memset(g_graphics_state.scrnbuf, 0, SCREENHEIGHT * SCREENWIDTH); + + /* Set the palette */ + + doompal = w_cache_lump_name(("PLAYPAL"), PU_CACHE); + i_set_palette(doompal); + + update_grab(); + + /* On some systems, it takes a second or so for the screen to settle + * after changing modes. We include the option to add a delay when + * setting the screen mode, so that the game doesn't start immediately + * with the player unable to see anything. + */ + + if (fullscreen && !screensaver_mode) + { + usleep(startup_delay * 1000); + } + + /* The actual 320x200 canvas that we draw to. This is the pixel buffer of + * the 8-bit paletted screen buffer that gets blit on an intermediate + * 32-bit RGBA screen buffer that gets loaded into a texture that gets + * finally rendered into our window or full screen in i_finish_update(). + */ + + i_video_buffer = g_graphics_state.scrnbuf; + v_restore_buffer(); + + /* Clear the screen to black. */ + + memset(i_video_buffer, 0, + SCREENWIDTH * SCREENHEIGHT * sizeof(*i_video_buffer)); + + /* clear out any events waiting at the start and center the mouse */ + + g_graphics_state.inited = true; + + /* Call i_shutdown_graphics on quit */ + + i_at_exit(i_shutdown_graphics, true); +} + +/* Bind all variables controlling video options into the configuration + * file system. + */ + +void i_bind_video_variables(void) +{ + m_bind_int_variable("use_mouse", &usemouse); + m_bind_int_variable("fullscreen", &fullscreen); + m_bind_int_variable("video_display", &video_display); + m_bind_int_variable("integer_scaling", &integer_scaling); + m_bind_int_variable("smooth_pixel_scaling", &smooth_pixel_scaling); + m_bind_int_variable("vga_porch_flash", &vga_porch_flash); + m_bind_int_variable("startup_delay", &startup_delay); + m_bind_int_variable("fullscreen_width", &fullscreen_width); + m_bind_int_variable("fullscreen_height", &fullscreen_height); + m_bind_int_variable("force_software_renderer", &force_software_renderer); + m_bind_int_variable("max_scaling_buffer_pixels", + &max_scaling_buffer_pixels); + m_bind_int_variable("window_width", &window_width); + m_bind_int_variable("window_height", &window_height); + m_bind_int_variable("grabmouse", &grabmouse); + m_bind_string_variable("video_driver", &video_driver); + m_bind_string_variable("window_position", &window_position); + m_bind_int_variable("usegamma", &usegamma); + m_bind_int_variable("png_screenshots", &png_screenshots); +} diff --git a/games/NXDoom/src/i_video.h b/games/NXDoom/src/i_video.h new file mode 100644 index 00000000000..322160f9bff --- /dev/null +++ b/games/NXDoom/src/i_video.h @@ -0,0 +1,154 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_video.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __I_VIDEO__ +#define __I_VIDEO__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Screen width and height. */ + +#define SCREENWIDTH 320 +#define SCREENHEIGHT 200 + +/* Screen height used when aspect_ratio_correct=true. */ + +#define SCREENHEIGHT_4_3 240 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef boolean (*grabmouse_callback_t)(void); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern char *video_driver; +extern boolean screenvisible; + +extern int vanilla_keyboard_mapping; +extern boolean screensaver_mode; +extern int usegamma; +extern pixel_t *i_video_buffer; + +extern int screen_width; +extern int screen_height; +extern int fullscreen; +extern int aspect_ratio_correct; +extern int integer_scaling; +extern int smooth_pixel_scaling; +extern int vga_porch_flash; +extern int force_software_renderer; + +extern int png_screenshots; + +extern char *window_position; + +/* Joystic/gamepad hysteresis */ + +extern unsigned int joywait; + +extern int usemouse; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: i_init_graphics + * + * Description: + * Called by d_doom_main, determines the hardware configuration and sets up + * the video mode. + * + ****************************************************************************/ + +void i_init_graphics(void); + +void i_graphics_check_commandline(void); + +void i_shutdown_graphics(void); + +/**************************************************************************** + * Name: i_set_palette + * + * Description: + * Takes full 8 bit values. + * + ****************************************************************************/ + +void i_set_palette(byte *palette); + +int i_get_palette_index(int r, int g, int b); + +void i_update_no_blit(void); +void i_finish_update(void); + +void i_read_screen(pixel_t *scr); + +void i_set_window_title(const char *title); + +void i_check_is_screensaver(void); +void i_set_grab_mouse_callback(grabmouse_callback_t func); + +void i_display_fps_dots(boolean dots_on); +void i_bind_video_variables(void); + +void i_init_window_title(void); + +/**************************************************************************** + * Name: i_start_frame + * + * Description: + * Called before processing any tics in a frame (just after displaying a + * frame). Time consuming synchronous operations are performed here + * (joystick reading). + * + ****************************************************************************/ + +void i_start_frame(void); + +/**************************************************************************** + * Name: i_start_tic + * + * Description: + * Called before processing each tic in a frame. Quick synchronous + * operations are performed here. + * + ****************************************************************************/ + +void i_start_tic(void); + +void i_get_window_position(int *x, int *y, int w, int h); + +#endif /* __I_VIDEO__ */ diff --git a/games/NXDoom/src/m_argv.c b/games/NXDoom/src/m_argv.c new file mode 100644 index 00000000000..a9b0ccd148b --- /dev/null +++ b/games/NXDoom/src/m_argv.c @@ -0,0 +1,349 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_argv.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "d_iwad.h" +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_misc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAXARGVS 100 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int myargc; +char **myargv; + +char *exedir = NULL; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void load_response_file(int argv_index, const char *filename) +{ + FILE *handle; + int size; + char *infile; + char *file; + char **newargv; + int newargc; + int i; + int k; + + /* Read the response file into memory */ + + handle = fopen(filename, "rb"); + + if (handle == NULL) + { + printf("\nNo such response file!"); + exit(1); + } + + printf("Found response file %s!\n", filename); + + size = m_file_length(handle); + + /* Read in the entire file + * Allocate one byte extra - this is in case there is an argument + * at the end of the response file, in which case a '\0' will be + * needed. + */ + + file = malloc(size + 1); + + i = 0; + + while (i < size) + { + k = fread(file + i, 1, size - i, handle); + + if (k < 0) + { + i_error("Failed to read full contents of '%s'", filename); + } + + i += k; + } + + fclose(handle); + + /* Create new arguments list array */ + + newargv = malloc(sizeof(char *) * MAXARGVS); + newargc = 0; + memset(newargv, 0, sizeof(char *) * MAXARGVS); + + /* Copy all the arguments in the list up to the response file */ + + if (argv_index >= MAXARGVS) + { + i_error("Too many arguments up to the response file!"); + } + + for (i = 0; i < argv_index; ++i) + { + newargv[i] = myargv[i]; + myargv[i] = NULL; + ++newargc; + } + + infile = file; + k = 0; + + while (k < size) + { + /* Skip past space characters to the next argument */ + + while (k < size && isspace(infile[k])) + { + ++k; + } + + if (k >= size) + { + break; + } + + /* If the next argument is enclosed in quote marks, treat + * the contents as a single argument. This allows long filenames + * to be specified. + */ + + if (infile[k] == '\"') + { + char *argstart; + + /* Skip the first character(") */ + + ++k; + + argstart = &infile[k]; + + /* Read all characters between quotes */ + + while (k < size && infile[k] != '\"' && infile[k] != '\n') + { + ++k; + } + + if (k >= size || infile[k] == '\n') + { + i_error("Quotes unclosed in response file '%s'", filename); + } + + /* Cut off the string at the closing quote */ + + infile[k] = '\0'; + ++k; + + if (newargc >= MAXARGVS) + { + i_error("Too many arguments in the response file!"); + } + + newargv[newargc++] = m_string_duplicate(argstart); + } + else + { + char *argstart; + + /* Read in the next argument until a space is reached */ + + argstart = &infile[k]; + + while (k < size && !isspace(infile[k])) + { + ++k; + } + + /* Cut off the end of the argument at the first space */ + + infile[k] = '\0'; + ++k; + + if (newargc >= MAXARGVS) + { + i_error("Too many arguments in the response file!"); + } + + newargv[newargc++] = m_string_duplicate(argstart); + } + } + + /* Add arguments following the response file argument */ + + if (newargc + myargc - (argv_index + 1) >= MAXARGVS) + { + i_error("Too many arguments following the response file!"); + } + + for (i = argv_index + 1; i < myargc; ++i) + { + newargv[newargc] = myargv[i]; + myargv[i] = NULL; + ++newargc; + } + + /* Free any old strings in myargv which were not moved to newargv */ + + for (i = 0; i < myargc; ++i) + { + if (myargv[i] != NULL) + { + free(myargv[i]); + myargv[i] = NULL; + } + } + + free(myargv); + myargv = newargv; + myargc = newargc; + + free(file); + +#if 0 + /* Disabled - Vanilla Doom does not do this. + * Display arguments + */ + + printf("%d command-line args:\n", myargc); + + for (k = 1; k < myargc; k++) + { + printf("'%s'\n", myargv[k]); + } +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* m_check_parm + * 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_check_parm_with_args(const char *check, int num_args) +{ + int i; + + /* Check if myargv[i] has been set to NULL in load_response_file(), + * which may call i_error(), which in turn calls m_parm_exists("-nogui"). + */ + + for (i = 1; i < myargc - num_args && myargv[i]; i++) + { + if (!strcasecmp(check, myargv[i])) return i; + } + + return 0; +} + +/* m_parm_exists + * + * Returns true if the given parameter exists in the program's command + * line arguments, false if not. + */ + +boolean m_parm_exists(const char *check) +{ + return m_check_parm(check) != 0; +} + +int m_check_parm(const char *check) +{ + return m_check_parm_with_args(check, 0); +} + +/* Find a Response File */ + +void m_find_response_file(void) +{ + int i; + + for (i = 1; i < myargc; i++) + { + if (myargv[i][0] == '@') + { + load_response_file(i, myargv[i] + 1); + } + } + + for (; ; ) + { + /* @arg + * + * Load extra command-line arguments from the given response + * file. Arguments read from the file are inserted into the + * command line, replacing this argument. A response file can + * also be loaded using the abbreviated syntax '@file.rsp'. + */ + + i = m_check_parm_with_args("-response", 1); + if (i <= 0) + { + break; + } + + /* Replace the -response argument so that the next time through + * the loop we'll ignore it. Since some parameters stop reading when + * an argument beginning with a '-' is encountered, we keep something + * that starts with a '-'. + */ + + free(myargv[i]); + myargv[i] = m_string_duplicate("-_"); + load_response_file(i + 1, myargv[i + 1]); + } +} + +/* Return the name of the executable used to start the program: */ + +const char *m_get_executable_name(void) +{ + return m_base_name(myargv[0]); +} + +void m_set_exe_dir(void) +{ + char *dirname; + + dirname = m_dir_name(myargv[0]); + exedir = m_string_join(dirname, DIR_SEPARATOR_S, NULL); + free(dirname); +} diff --git a/games/NXDoom/src/m_argv.h b/games/NXDoom/src/m_argv.h new file mode 100644 index 00000000000..bb7f6a8cbe4 --- /dev/null +++ b/games/NXDoom/src/m_argv.h @@ -0,0 +1,92 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_argv.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef __M_ARGV__ +#define __M_ARGV__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int myargc; +extern char **myargv; + +extern char *exedir; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void m_set_exe_dir(void); + +/**************************************************************************** + * Name: m_check_parm + * + * Description: + * Returns the position of the given parameter in the arg list. + * + * Returns: + * The position of the given parameter in the arg list (0 if not found). + * + ****************************************************************************/ + +int m_check_parm(const char *check); + +/**************************************************************************** + * Name: m_check_parm_with_args + * + * Description: + * Same as m_check_parm, but checks that num_args arguments are available + * following the specified argument. + * + ****************************************************************************/ + +int m_check_parm_with_args(const char *check, int num_args); + +void m_find_response_file(void); +void m_add_loose_files(void); + +/**************************************************************************** + * Name: m_parm_exists + * + * Description: + * Parameter has been specified? + * + ****************************************************************************/ + +boolean m_parm_exists(const char *check); + +/**************************************************************************** + * Name: m_get_executable_name + * + * Description: + * Get name of executable used to run this program. + * + ****************************************************************************/ + +const char *m_get_executable_name(void); + +#endif diff --git a/games/NXDoom/src/m_bbox.c b/games/NXDoom/src/m_bbox.c new file mode 100644 index 00000000000..226237fbbec --- /dev/null +++ b/games/NXDoom/src/m_bbox.c @@ -0,0 +1,53 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_bbox.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Main loop menu stuff. + * Random number LUT. + * Default Config File. + * PCX Screenshots. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "m_bbox.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void m_clear_box(fixed_t *box) +{ + box[BOXTOP] = box[BOXRIGHT] = INT_MIN; + box[BOXBOTTOM] = box[BOXLEFT] = INT_MAX; +} + +void m_add_to_box(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/games/NXDoom/src/m_bbox.h b/games/NXDoom/src/m_bbox.h new file mode 100644 index 00000000000..a36d545d557 --- /dev/null +++ b/games/NXDoom/src/m_bbox.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_bbox.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Nil. + * + ****************************************************************************/ + +#ifndef __M_BBOX__ +#define __M_BBOX__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "m_fixed.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Bounding box coordinate storage. */ + +enum +{ + BOXTOP, + BOXBOTTOM, + BOXLEFT, + BOXRIGHT +}; /* bbox coordinates */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Bounding box functions. */ + +void m_clear_box(fixed_t *box); + +void m_add_to_box(fixed_t *box, fixed_t x, fixed_t y); + +#endif /* __M_BBOX__ */ diff --git a/games/NXDoom/src/m_cheat.c b/games/NXDoom/src/m_cheat.c new file mode 100644 index 00000000000..038d771077d --- /dev/null +++ b/games/NXDoom/src/m_cheat.c @@ -0,0 +1,95 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_cheat.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Cheat sequence checking. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" +#include "m_cheat.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* CHEAT SEQUENCE PACKAGE */ + +/* Called in st_stuff module, which handles the input. + * Returns a 1 if the cheat was successful, 0 if failed. + */ + +int cht_check_cheat(cheatseq_t *cht, char key) +{ + /* if we make a short sequence on a cheat with parameters, this + * will not work in vanilla doom. behave the same. + */ + + if (cht->parameter_chars > 0 && strlen(cht->sequence) < cht->sequence_len) + { + return false; + } + + if (cht->chars_read < strlen(cht->sequence)) + { + /* still reading characters from the cheat code + * and verifying. reset back to the beginning + * if a key is wrong + */ + + if (key == cht->sequence[cht->chars_read]) + ++cht->chars_read; + else + cht->chars_read = 0; + + cht->param_chars_read = 0; + } + else if (cht->param_chars_read < cht->parameter_chars) + { + /* we have passed the end of the cheat sequence and are + * entering parameters now + */ + + cht->parameter_buf[cht->param_chars_read] = key; + + ++cht->param_chars_read; + } + + if (cht->chars_read >= strlen(cht->sequence) && + cht->param_chars_read >= cht->parameter_chars) + { + cht->chars_read = cht->param_chars_read = 0; + + return true; + } + + /* cheat not matched yet */ + + return false; +} + +void ch_get_param(cheatseq_t *cht, char *buffer) +{ + memcpy(buffer, cht->parameter_buf, cht->parameter_chars); +} diff --git a/games/NXDoom/src/m_cheat.h b/games/NXDoom/src/m_cheat.h new file mode 100644 index 00000000000..8fa745615f9 --- /dev/null +++ b/games/NXDoom/src/m_cheat.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_cheat.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Cheat code checking. + * + ****************************************************************************/ + +#ifndef __M_CHEAT__ +#define __M_CHEAT__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* CHEAT SEQUENCE PACKAGE */ + +/* declaring a cheat */ + +#define CHEAT(value, parameters) \ + {value, sizeof(value) - 1, parameters, 0, 0, ""} + +#define MAX_CHEAT_LEN 25 +#define MAX_CHEAT_PARAMS 5 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct +{ + /* settings for this cheat */ + + char sequence[MAX_CHEAT_LEN]; + size_t sequence_len; + int parameter_chars; + + /* state used during the game */ + + size_t chars_read; + int param_chars_read; + char parameter_buf[MAX_CHEAT_PARAMS]; +} cheatseq_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int cht_check_cheat(cheatseq_t *cht, char key); + +void ch_get_param(cheatseq_t *cht, char *buffer); + +#endif /* __M_CHEAT__ */ diff --git a/games/NXDoom/src/m_config.c b/games/NXDoom/src/m_config.c new file mode 100644 index 00000000000..d4441f5eaae --- /dev/null +++ b/games/NXDoom/src/m_config.c @@ -0,0 +1,2494 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_config.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Configuration file interface. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "doomkeys.h" +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" + +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CONFIG_VARIABLE_GENERIC(name, type) {#name, {NULL}, type, 0, 0, false} + +#define CONFIG_VARIABLE_KEY(name) CONFIG_VARIABLE_GENERIC(name, DEFAULT_KEY) +#define CONFIG_VARIABLE_INT(name) CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT) +#define CONFIG_VARIABLE_INT_HEX(name) \ + CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT_HEX) +#define CONFIG_VARIABLE_FLOAT(name) \ + CONFIG_VARIABLE_GENERIC(name, DEFAULT_FLOAT) +#define CONFIG_VARIABLE_STRING(name) \ + CONFIG_VARIABLE_GENERIC(name, DEFAULT_STRING) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + DEFAULT_INT, + DEFAULT_INT_HEX, + DEFAULT_STRING, + DEFAULT_FLOAT, + DEFAULT_KEY, +} default_type_t; + +typedef struct +{ + /* Name of the variable */ + + const char *name; + + /* Pointer to the location in memory of the variable */ + + union + { + int *i; + char **s; + float *f; + } location; + + /* Type of the variable */ + + default_type_t type; + + /* If this is a key value, the original integer scancode we read from + * the config file before translating it to the internal key value. + * If zero, we didn't read this value from a config file. + */ + + int untranslated; + + /* The value we translated the scancode into when we read the + * config file on startup. If the variable value is different from + * this, it has been changed and needs to be converted; otherwise, + * use the 'untranslated' value. + */ + + int original_translated; + + /* If true, this config variable has been bound to a variable + * and is being used. + */ + + boolean bound; +} default_t; + +typedef struct +{ + default_t *defaults; + int numdefaults; + const char *filename; +} default_collection_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static char *autoload_path = ""; + +/* Default filenames for configuration files. */ + +static const char *default_main_config; +static const char *default_extra_config; + +/* @begin_config_file default */ + +static default_t doom_defaults_list[] = +{ + /* Mouse sensitivity. This value is used to multiply input mouse + * movement to control the effect of moving the mouse. + * + * The "normal" maximum value available for this through the + * in-game options menu is 9. A value of 31 or greater will cause + * the game to crash when entering the options menu. + * + */ + + CONFIG_VARIABLE_INT(mouse_sensitivity), + + /* Volume of sound effects, range 0-15. */ + + CONFIG_VARIABLE_INT(sfx_volume), + + /* Volume of in-game music, range 0-15. */ + + CONFIG_VARIABLE_INT(music_volume), + + /* @game strife + * + * If non-zero, dialogue text is displayed over characters' pictures + * when engaging actors who have voices. + */ + + CONFIG_VARIABLE_INT(show_talk), + + /* @game strife + * + * Volume of voice sound effects, range 0-15. + */ + + CONFIG_VARIABLE_INT(voice_volume), + + /* @game doom + * + * If non-zero, messages are displayed on the heads-up display + * in the game ("picked up a clip", etc). If zero, these messages + * are not displayed. + */ + + CONFIG_VARIABLE_INT(show_messages), + + /* Keyboard key to turn right. */ + + CONFIG_VARIABLE_KEY(key_right), + + /* Keyboard key to turn left. */ + + CONFIG_VARIABLE_KEY(key_left), + + /* Keyboard key to move forward. */ + + CONFIG_VARIABLE_KEY(key_up), + + /* Keyboard key to move backward. */ + + CONFIG_VARIABLE_KEY(key_down), + + /* Keyboard key to strafe left. */ + + CONFIG_VARIABLE_KEY(key_strafeleft), + + /* Keyboard key to strafe right. */ + + CONFIG_VARIABLE_KEY(key_straferight), + + /* @game strife + * + * Keyboard key to use health. + */ + + CONFIG_VARIABLE_KEY(key_use_health), + + /* @game hexen + * + * Keyboard key to jump. + */ + + CONFIG_VARIABLE_KEY(key_jump), + + /* @game heretic hexen + * + * Keyboard key to fly upward. + */ + + CONFIG_VARIABLE_KEY(key_flyup), + + /* @game heretic hexen + * + * Keyboard key to fly downwards. + */ + + CONFIG_VARIABLE_KEY(key_flydown), + + /* @game heretic hexen + * + * Keyboard key to center flying. + */ + + CONFIG_VARIABLE_KEY(key_flycenter), + + /* @game heretic hexen + * + * Keyboard key to look up. + */ + + CONFIG_VARIABLE_KEY(key_lookup), + + /* @game heretic hexen + * + * Keyboard key to look down. + */ + + CONFIG_VARIABLE_KEY(key_lookdown), + + /* @game heretic hexen + * + * Keyboard key to center the view. + */ + + CONFIG_VARIABLE_KEY(key_lookcenter), + + /* @game strife + * + * Keyboard key to query inventory. + */ + + CONFIG_VARIABLE_KEY(key_invquery), + + /* @game strife + * + * Keyboard key to display mission objective. + */ + + CONFIG_VARIABLE_KEY(key_mission), + + /* @game strife + * + * Keyboard key to display inventory popup. + */ + + CONFIG_VARIABLE_KEY(key_inv_pop), + + /* @game strife + * + * Keyboard key to display keys popup. + */ + + CONFIG_VARIABLE_KEY(key_inv_key), + + /* @game strife + * + * Keyboard key to jump to start of inventory. + */ + + CONFIG_VARIABLE_KEY(key_inv_home), + + /* @game strife + * + * Keyboard key to jump to end of inventory. + */ + + CONFIG_VARIABLE_KEY(key_inv_end), + + /* @game heretic hexen + * + * Keyboard key to scroll left in the inventory. + */ + + CONFIG_VARIABLE_KEY(key_invleft), + + /* @game heretic hexen + * + * Keyboard key to scroll right in the inventory. + */ + + CONFIG_VARIABLE_KEY(key_invright), + + /* @game strife + * + * Keyboard key to scroll left in the inventory. + */ + + CONFIG_VARIABLE_KEY(key_inv_left), + + /* @game strife + * + * Keyboard key to scroll right in the inventory. + */ + + CONFIG_VARIABLE_KEY(key_inv_right), + + /* @game heretic hexen + * + * Keyboard key to use the current item in the inventory. + */ + + CONFIG_VARIABLE_KEY(key_useartifact), + + /* @game strife + * + * Keyboard key to use inventory item. + */ + + CONFIG_VARIABLE_KEY(key_inv_use), + + /* @game strife + * + * Keyboard key to drop an inventory item. + */ + + CONFIG_VARIABLE_KEY(key_inv_drop), + + /* @game strife + * + * Keyboard key to look up. + */ + + CONFIG_VARIABLE_KEY(key_look_up), + + /* @game strife + * + * Keyboard key to look down. + */ + + CONFIG_VARIABLE_KEY(key_look_down), + + /* Keyboard key to fire the currently selected weapon. */ + + CONFIG_VARIABLE_KEY(key_fire), + + /* Keyboard key to "use" an object, eg. a door or switch. */ + + CONFIG_VARIABLE_KEY(key_use), + + /* Keyboard key to turn on strafing. When held down, pressing the + * key to turn left or right causes the player to strafe left or + * right instead. + */ + + CONFIG_VARIABLE_KEY(key_strafe), + + /* Keyboard key to make the player run. */ + + CONFIG_VARIABLE_KEY(key_speed), + + /* If non-zero, mouse input is enabled. If zero, mouse input is + * disabled. + */ + + CONFIG_VARIABLE_INT(use_mouse), + + /* Mouse button to fire the currently selected weapon. */ + + CONFIG_VARIABLE_INT(mouseb_fire), + + /* Mouse button to turn on strafing. When held down, the player + * will strafe left and right instead of turning left and right. + */ + + CONFIG_VARIABLE_INT(mouseb_strafe), + + /* Mouse button to move forward. */ + + CONFIG_VARIABLE_INT(mouseb_forward), + + /* Mouse button to turn on running. When held down, the player + * will run while moving. + */ + + CONFIG_VARIABLE_INT(mouseb_speed), + + /* @game hexen strife + * + * Mouse button to jump. + */ + + CONFIG_VARIABLE_INT(mouseb_jump), + + /* If non-zero, joystick input is enabled. */ + + CONFIG_VARIABLE_INT(use_joystick), + + /* Joystick virtual button that fires the current weapon. */ + + CONFIG_VARIABLE_INT(joyb_fire), + + /* Joystick virtual button that makes the player strafe while + * held down. + */ + + CONFIG_VARIABLE_INT(joyb_strafe), + + /* Joystick virtual button to "use" an object, eg. a door or switch. */ + + CONFIG_VARIABLE_INT(joyb_use), + + /* Joystick virtual button that makes the player run while held + * down. + * + * If this has a value of 20 or greater, the player will always run, + * even if use_joystick is 0. + */ + + CONFIG_VARIABLE_INT(joyb_speed), + + /* @game hexen strife + * + * Joystick virtual button that makes the player jump. + */ + + CONFIG_VARIABLE_INT(joyb_jump), + + /* @game doom heretic hexen + * + * Screen size, range 3-11. + * + * A value of 11 gives a full-screen view with the status bar not + * displayed. A value of 10 gives a full-screen view with the + * status bar displayed. + */ + + CONFIG_VARIABLE_INT(screenblocks), + + /* @game strife + * + * Screen size, range 3-11. + * + * A value of 11 gives a full-screen view with the status bar not + * displayed. A value of 10 gives a full-screen view with the + * status bar displayed. + */ + + CONFIG_VARIABLE_INT(screensize), + + /* @game doom + * + * Screen detail. Zero gives normal "high detail" mode, while + * a non-zero value gives "low detail" mode. + */ + + CONFIG_VARIABLE_INT(detaillevel), + + /* Number of sounds that will be played simultaneously. */ + + CONFIG_VARIABLE_INT(snd_channels), + + /* Music output device. A non-zero value gives MIDI sound output, + * while a value of zero disables music. + */ + + CONFIG_VARIABLE_INT(snd_musicdevice), + + /* Sound effects device. A value of zero disables in-game sound + * effects, a value of 1 enables PC speaker sound effects, while + * a value in the range 2-9 enables the "normal" digital sound + * effects. + */ + + CONFIG_VARIABLE_INT(snd_sfxdevice), + + /* SoundBlaster I/O port. Unused. */ + + CONFIG_VARIABLE_INT(snd_sbport), + + /* SoundBlaster IRQ. Unused. */ + + CONFIG_VARIABLE_INT(snd_sbirq), + + /* SoundBlaster DMA channel. Unused. */ + + CONFIG_VARIABLE_INT(snd_sbdma), + + /* Output port to use for OPL MIDI playback. Unused. */ + + CONFIG_VARIABLE_INT(snd_mport), + + /* Gamma correction level. A value of zero disables gamma + * correction, while a value in the range 1-4 gives increasing + * levels of gamma correction. + */ + + CONFIG_VARIABLE_INT(usegamma), + + /* @game hexen + * + * Directory in which to store savegames. + */ + + CONFIG_VARIABLE_STRING(savedir), + + /* @game hexen + * + * Controls whether messages are displayed in the heads-up display. + * If this has a non-zero value, messages are displayed. + */ + + CONFIG_VARIABLE_INT(messageson), + + /* @game strife + * + * Name of background flat used by view border. + */ + + CONFIG_VARIABLE_STRING(back_flat), + + /* @game strife + * + * Multiplayer nickname (?). + */ + + CONFIG_VARIABLE_STRING(nickname), + + /* Multiplayer chat macro: message to send when alt+0 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro0), + + /* Multiplayer chat macro: message to send when alt+1 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro1), + + /* Multiplayer chat macro: message to send when alt+2 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro2), + + /* Multiplayer chat macro: message to send when alt+3 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro3), + + /* Multiplayer chat macro: message to send when alt+4 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro4), + + /* Multiplayer chat macro: message to send when alt+5 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro5), + + /* Multiplayer chat macro: message to send when alt+6 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro6), + + /* Multiplayer chat macro: message to send when alt+7 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro7), + + /* Multiplayer chat macro: message to send when alt+8 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro8), + + /* Multiplayer chat macro: message to send when alt+9 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro9), + + /* @game strife + * + * Serial port number to use for SERSETUP.EXE (unused). + */ + + CONFIG_VARIABLE_INT(comport), +}; + +static default_collection_t doom_defaults = +{ + doom_defaults_list, + arrlen(doom_defaults_list), + NULL, +}; + +/* @begin_config_file extended */ + +static default_t extra_defaults_list[] = +{ + /* Name of the SDL video driver to use. If this is an empty string, + * the default video driver is used. + */ + + CONFIG_VARIABLE_STRING(video_driver), + + /* Position of the window on the screen when running in windowed + * mode. Accepted values are: "" (empty string) - don't care, + * "center" - place window at center of screen, "x,y" - place + * window at the specified coordinates. + */ + + CONFIG_VARIABLE_STRING(window_position), + + /* If non-zero, the game will run in full screen mode. If zero, + * the game will run in a window. + */ + + CONFIG_VARIABLE_INT(fullscreen), + + /* Index of the display on which the game should run. This has no + * effect if running in windowed mode (fullscreen=0) and + * window_position is not set to "center". + */ + + CONFIG_VARIABLE_INT(video_display), + + /* If non-zero, the screen will be stretched vertically to display + * correctly on a square pixel video mode. + */ + + CONFIG_VARIABLE_INT(aspect_ratio_correct), + + /* If non-zero, the screen will have smooth scaling. */ + + CONFIG_VARIABLE_INT(smooth_pixel_scaling), + + /* If non-zero, forces integer scales for resolution-independent + * rendering. + */ + + CONFIG_VARIABLE_INT(integer_scaling), + + /* If non-zero, any pillar/letter boxes drawn around the game area + * will "flash" when the game palette changes, simulating the VGA + * "porch" + */ + + CONFIG_VARIABLE_INT(vga_porch_flash), + + /* Window width when running in windowed mode. */ + + CONFIG_VARIABLE_INT(window_width), + + /* Window height when running in windowed mode. */ + + CONFIG_VARIABLE_INT(window_height), + + /* Width for screen mode when running fullscreen. + * If this and fullscreen_height are both set to zero, we run + * fullscreen as a desktop window that covers the entire screen, + * rather than ever switching screen modes. It should usually + * be unnecessary to set this value. + */ + + CONFIG_VARIABLE_INT(fullscreen_width), + + /* Height for screen mode when running fullscreen. + * See documentation for fullscreen_width. + */ + + CONFIG_VARIABLE_INT(fullscreen_height), + + /* If non-zero, force the use of a software renderer. For use on + * systems lacking hardware acceleration. + */ + + CONFIG_VARIABLE_INT(force_software_renderer), + + /* Maximum number of pixels to use for intermediate scaling buffer. + * More pixels mean that the screen can be rendered more precisely, + * but there are diminishing returns on quality. The default limits to + * 16,000,000 pixels, which is enough to cover 4K monitor standards. + */ + + CONFIG_VARIABLE_INT(max_scaling_buffer_pixels), + + /* Number of milliseconds to wait on startup after the video mode + * has been set, before the game will start. This allows the + * screen to settle on some monitors that do not display an image + * for a brief interval after changing video modes. + */ + + CONFIG_VARIABLE_INT(startup_delay), + + /* @game heretic hexen strife + * + * If non-zero, display the graphical startup screen. + */ + + CONFIG_VARIABLE_INT(graphical_startup), + + /* @game doom heretic strife + * + * If non-zero, the ENDOOM text screen is displayed when exiting the + * game. If zero, the ENDOOM screen is not displayed. + */ + + CONFIG_VARIABLE_INT(show_endoom), + + /* @game doom strife + * + * If non-zero, a disk activity indicator is displayed when data is read + * from disk. If zero, the disk activity indicator is not displayed. + */ + + CONFIG_VARIABLE_INT(show_diskicon), + + /* If non-zero, save screenshots in PNG format. If zero, screenshots are + * saved in PCX format, as Vanilla Doom does. + */ + + CONFIG_VARIABLE_INT(png_screenshots), + + /* Sound output sample rate, in Hz. Typical values to use are + * 11025, 22050, 44100 and 48000. + */ + + CONFIG_VARIABLE_INT(snd_samplerate), + + /* Maximum number of bytes to allocate for caching converted sound + * effects in memory. If set to zero, there is no limit applied. + */ + + CONFIG_VARIABLE_INT(snd_cachesize), + + /* Maximum size of the output sound buffer size in milliseconds. + * Sound output is generated periodically in slices. Higher values + * might be more efficient but will introduce latency to the + * sound output. The default is 28ms (one slice per tic with the + * 35fps timer). + */ + + CONFIG_VARIABLE_INT(snd_maxslicetime_ms), + + /* If non-zero, sound effects will have their pitch varied up or + * down by a random amount during play. If zero, sound effects + * play back at their default pitch. + */ + + CONFIG_VARIABLE_INT(snd_pitchshift), + + /* External command to invoke to perform MIDI playback. If set to + * the empty string, SDL_mixer's internal MIDI playback is used. + * This only has any effect when snd_musicdevice is set to General + * MIDI output. + */ + + CONFIG_VARIABLE_STRING(snd_musiccmd), + + /* Controls whether libsamplerate support is used for performing + * sample rate conversions of sound effects. Support for this + * must be compiled into the program. + * + * If zero, libsamplerate support is disabled. If non-zero, + * libsamplerate is enabled. Increasing values roughly correspond + * to higher quality conversion; the higher the quality, the + * slower the conversion process. Linear conversion = 1; + * Zero order hold = 2; Fast Sinc filter = 3; Medium quality + * Sinc filter = 4; High quality Sinc filter = 5. + */ + + CONFIG_VARIABLE_INT(use_libsamplerate), + + /* Scaling factor used by libsamplerate. This is used when converting + * sounds internally back into integer form; normally it should not + * be necessary to change it from the default value. The only time + * it might be needed is if a PWAD file is loaded that contains very + * loud sounds, in which case the conversion may cause sound clipping + * and the scale factor should be reduced. The lower the value, the + * quieter the sound effects become, so it should be set as high as is + * possible without clipping occurring. + */ + + CONFIG_VARIABLE_FLOAT(libsamplerate_scale), + + /* Full path to a directory in which WAD files and dehacked patches + * can be placed to be automatically loaded on startup. A subdirectory + * of this directory matching the IWAD name is checked to find the + * files to load. + */ + + CONFIG_VARIABLE_STRING(autoload_path), + + /* Full path to a directory containing configuration files for + * substitute music packs. These packs contain high quality renderings + * of game music to be played instead of using the system's built-in + * MIDI playback. + */ + + CONFIG_VARIABLE_STRING(music_pack_path), + +#ifdef HAVE_FLUIDSYNTH + /* If 1, activate the FluidSynth chorus effects module. If 0, no chorus + * will be added to the output signal. + */ + + CONFIG_VARIABLE_INT(fsynth_chorus_active), + + /* Specifies the modulation depth of the FluidSynth chorus. Default is + * 5.0, range is 0.0 to 256.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_chorus_depth), + + /* Specifies the output amplitude of the FluidSynth chorus signal. Default + * is 0.35, range is 0.0 to 10.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_chorus_level), + + /* Sets the voice count of the FluidSynth chorus signal. Default is 3, + * range is 0 to 99. + */ + + CONFIG_VARIABLE_INT(fsynth_chorus_nr), + + /* Sets the FluidSynth chorus modulation speed in Hz. Default is 0.3, + * range is 0.1 to 5.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_chorus_speed), + + /* This setting defines how FluidSynth interprets Bank Select messages. + * The default is "gs". Other possible values are "gm", "xg" and "mma". + */ + + CONFIG_VARIABLE_STRING(fsynth_midibankselect), + + /* Sets the number of FluidSynth voices that can be played in parallel. + * Default is 256, range is 1 - 65535. + */ + + CONFIG_VARIABLE_INT(fsynth_polyphony), + + /* If 1, activate the FluidSynth reverb effects module. If 0, no reverb + * will be added to the output signal. + */ + + CONFIG_VARIABLE_INT(fsynth_reverb_active), + + /* Sets the amount of FluidSynth reverb damping. Default is 0.4, range is + * 0.0 to 1.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_reverb_damp), + + /* Sets the FluidSynth reverb amplitude. Default is 0.15, range is 0.0 - + * 1.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_reverb_level), + + /* Sets the room size(i.e. amount of wet) FluidSynth reverb. Default is + * 0.6, range is 0.0 - 1.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_reverb_roomsize), + + /* Sets the stereo spread of the FluidSynth reverb signal. Default is + * 0.4, range is 0.0 - 100.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_reverb_width), + + /* Fine tune the FluidSynth output level. Default is 1.0, + * range is 0.0 - 10.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_gain), + + /* Full path to a soundfont file to use with FluidSynth MIDI playback. */ + + CONFIG_VARIABLE_STRING(fsynth_sf_path), +#endif /* HAVE_FLUIDSYNTH */ + + /* Full path to a Timidity configuration file to use for MIDI + * playback. The file will be evaluated from the directory where + * it is evaluated, so there is no need to add "dir" commands + * into it. + */ + + CONFIG_VARIABLE_STRING(timidity_cfg_path), + + /* Path to GUS patch files to use when operating in GUS emulation + * mode. + */ + + CONFIG_VARIABLE_STRING(gus_patch_path), + + /* Number of kilobytes of RAM to use in GUS emulation mode. Valid + * values are 256, 512, 768 or 1024. + */ + + CONFIG_VARIABLE_INT(gus_ram_kb), + + /* @game doom strife + * + * If non-zero, the Vanilla savegame limit is enforced; if the + * savegame exceeds 180224 bytes in size, the game will exit with + * an error. If this has a value of zero, there is no limit to + * the size of savegames. + */ + + CONFIG_VARIABLE_INT(vanilla_savegame_limit), + + /* @game doom strife + * + * If non-zero, the Vanilla demo size limit is enforced; the game + * exits with an error when a demo exceeds the demo size limit + * (128KiB by default). If this has a value of zero, there is no + * limit to the size of demos. + */ + + CONFIG_VARIABLE_INT(vanilla_demo_limit), + + /* If non-zero, the game behaves like Vanilla Doom, always assuming + * an American keyboard mapping. If this has a value of zero, the + * native keyboard mapping of the keyboard is used. + */ + + CONFIG_VARIABLE_INT(vanilla_keyboard_mapping), + + /* Name to use in network games for identification. This is only + * used on the "waiting" screen while waiting for the game to start. + */ + + CONFIG_VARIABLE_STRING(player_name), + + /* If this is non-zero, the mouse will be "grabbed" when running + * in windowed mode so that it can be used as an input device. + * When running full screen, this has no effect. + */ + + CONFIG_VARIABLE_INT(grabmouse), + + /* If non-zero, all vertical mouse movement is ignored. This + * emulates the behavior of the "novert" tool available under DOS + * that performs the same function. + */ + + CONFIG_VARIABLE_INT(novert), + + /* Mouse acceleration factor. When the speed of mouse movement + * exceeds the threshold value (mouse_threshold), the speed is + * multiplied by this value. + */ + + CONFIG_VARIABLE_FLOAT(mouse_acceleration), + + /* Mouse acceleration threshold. When the speed of mouse movement + * exceeds this threshold value, the speed is multiplied by an + * acceleration factor (mouse_acceleration). + */ + + CONFIG_VARIABLE_INT(mouse_threshold), + + /* Mouse button to strafe left. */ + + CONFIG_VARIABLE_INT(mouseb_strafeleft), + + /* Mouse button to strafe right. */ + + CONFIG_VARIABLE_INT(mouseb_straferight), + + /* Mouse button to turn left. */ + + CONFIG_VARIABLE_INT(mouseb_turnleft), + + /* Mouse button to turn right. */ + + CONFIG_VARIABLE_INT(mouseb_turnright), + + /* Mouse button to "use" an object, eg. a door or switch. */ + + CONFIG_VARIABLE_INT(mouseb_use), + + /* Mouse button to move backwards. */ + + CONFIG_VARIABLE_INT(mouseb_backward), + + /* Mouse button to cycle to the previous weapon. */ + + CONFIG_VARIABLE_INT(mouseb_prevweapon), + + /* Mouse button to cycle to the next weapon. */ + + CONFIG_VARIABLE_INT(mouseb_nextweapon), + + /* @game heretic + * + * Mouse button to move to the left in the inventory. + */ + + CONFIG_VARIABLE_INT(mouseb_invleft), + + /* @game heretic + * + * Mouse button to move to the right in the inventory. + */ + + CONFIG_VARIABLE_INT(mouseb_invright), + + /* @game heretic hexen + * + * Mouse button to use artifact. + */ + + CONFIG_VARIABLE_INT(mouseb_useartifact), + + /* If non-zero, double-clicking a mouse button acts like pressing + * the "use" key to use an object in-game, eg. a door or switch. + */ + + CONFIG_VARIABLE_INT(dclick_use), + + /* SDL GUID string indicating the joystick to use. An empty string + * indicates that no joystick is configured. + */ + + CONFIG_VARIABLE_STRING(joystick_guid), + + /* Index of SDL joystick to use; this is only used in the case where + * multiple identical joystick devices are connected which have the + * same GUID, to distinguish between devices. + */ + + CONFIG_VARIABLE_INT(joystick_index), + + /* If non-zero, use analog movement when playing with a gamepad. */ + + CONFIG_VARIABLE_INT(use_analog), + + /* Joystick axis to use to for horizontal (X) movement. */ + + CONFIG_VARIABLE_INT(joystick_x_axis), + + /* If non-zero, movement on the horizontal joystick axis is inverted. */ + + CONFIG_VARIABLE_INT(joystick_x_invert), + + /* Joystick turn analog sensitivity, specified as a value between 0 + * and 20. + */ + + CONFIG_VARIABLE_INT(joystick_turn_sensitivity), + + /* Joystick axis to use to for vertical (Y) movement. */ + + CONFIG_VARIABLE_INT(joystick_y_axis), + + /* If non-zero, movement on the vertical joystick axis is inverted. */ + + CONFIG_VARIABLE_INT(joystick_y_invert), + + /* Joystick axis to use to for strafing movement. */ + + CONFIG_VARIABLE_INT(joystick_strafe_axis), + + /* If non-zero, movement on the joystick axis used for strafing + * is inverted. + */ + + CONFIG_VARIABLE_INT(joystick_strafe_invert), + + /* Joystick move and strafe analog sensitivity, specified as a value + * between 0 and 20. + */ + + CONFIG_VARIABLE_INT(joystick_move_sensitivity), + + /* Joystick axis to use to for looking up and down. */ + + CONFIG_VARIABLE_INT(joystick_look_axis), + + /* If non-zero, movement on the joystick axis used for looking + * is inverted. + */ + + CONFIG_VARIABLE_INT(joystick_look_invert), + + /* Joystick look analog sensitivity, specified as a value between 0 + * and 20. + */ + + CONFIG_VARIABLE_INT(joystick_look_sensitivity), + + /* The physical joystick button that corresponds to joystick + * virtual button #0. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button0), + + /* The physical joystick button that corresponds to joystick + * virtual button #1. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button1), + + /* The physical joystick button that corresponds to joystick + * virtual button #2. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button2), + + /* The physical joystick button that corresponds to joystick + * virtual button #3. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button3), + + /* The physical joystick button that corresponds to joystick + * virtual button #4. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button4), + + /* The physical joystick button that corresponds to joystick + * virtual button #5. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button5), + + /* The physical joystick button that corresponds to joystick + * virtual button #6. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button6), + + /* The physical joystick button that corresponds to joystick + * virtual button #7. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button7), + + /* The physical joystick button that corresponds to joystick + * virtual button #8. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button8), + + /* The physical joystick button that corresponds to joystick + * virtual button #9. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button9), + + /* The physical joystick button that corresponds to joystick + * virtual button #10. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button10), + + /* The physical joystick button that corresponds to joystick + * virtual button #11. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button11), + + /* The physical joystick button that corresponds to joystick + * virtual button #12. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button12), + + /* The physical joystick button that corresponds to joystick + * virtual button #13. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button13), + + /* The physical joystick button that corresponds to joystick + * virtual button #14. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button14), + + /* The physical joystick button that corresponds to joystick + * virtual button #15. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button15), + + /* The physical joystick button that corresponds to joystick + * virtual button #16. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button16), + + /* If non-zero, use the SDL_GameController interface instead of the + * SDL_Joystick interface. + */ + + CONFIG_VARIABLE_INT(use_gamepad), + + /* Stores the SDL_GameControllerType of the last configured gamepad. */ + + CONFIG_VARIABLE_INT(gamepad_type), + + /* Joystick x axis dead zone, specified as a percentage of the axis max + * value. + */ + + CONFIG_VARIABLE_INT(joystick_x_dead_zone), + + /* Joystick y axis dead zone, specified as a percentage of the axis max + * value. + */ + + CONFIG_VARIABLE_INT(joystick_y_dead_zone), + + /* Joystick strafe axis dead zone, specified as a percentage of the axis + * max value. + */ + + CONFIG_VARIABLE_INT(joystick_strafe_dead_zone), + + /* Joystick look axis dead zone, specified as a percentage of the axis max + * value. + */ + + CONFIG_VARIABLE_INT(joystick_look_dead_zone), + + /* Joystick virtual button to make the player strafe left. */ + + CONFIG_VARIABLE_INT(joyb_strafeleft), + + /* Joystick virtual button to make the player strafe right. */ + + CONFIG_VARIABLE_INT(joyb_straferight), + + /* Joystick virtual button to activate the menu. */ + + CONFIG_VARIABLE_INT(joyb_menu_activate), + + /* Joystick virtual button to toggle the automap. */ + + CONFIG_VARIABLE_INT(joyb_toggle_automap), + + /* Joystick virtual button that cycles to the previous weapon. */ + + CONFIG_VARIABLE_INT(joyb_prevweapon), + + /* Joystick virtual button that cycles to the next weapon. */ + + CONFIG_VARIABLE_INT(joyb_nextweapon), + + /* @game heretic hexen + * Joystick virtual button to activate artifact. + */ + + CONFIG_VARIABLE_INT(joyb_useartifact), + + /* @game heretic hexen + * Joystick virtual button to move left in the inventory. + */ + + CONFIG_VARIABLE_INT(joyb_invleft), + + /* @game heretic hexen + * Joystick virtual button to move right in the inventory. + */ + + CONFIG_VARIABLE_INT(joyb_invright), + + /* @game heretic hexen + * Joystick virtual button to fly up. + */ + + CONFIG_VARIABLE_INT(joyb_flyup), + + /* @game heretic hexen + * Joystick virtual button to fly down. + */ + + CONFIG_VARIABLE_INT(joyb_flydown), + + /* @game heretic hexen + * Joystick virtual button to center flying. + */ + + CONFIG_VARIABLE_INT(joyb_flycenter), + + /* Key to pause or unpause the game. */ + + CONFIG_VARIABLE_KEY(key_pause), + + /* Key that activates the menu when pressed. */ + + CONFIG_VARIABLE_KEY(key_menu_activate), + + /* Key that moves the cursor up on the menu. */ + + CONFIG_VARIABLE_KEY(key_menu_up), + + /* Key that moves the cursor down on the menu. */ + + CONFIG_VARIABLE_KEY(key_menu_down), + + /* Key that moves the currently selected slider on the menu left. */ + + CONFIG_VARIABLE_KEY(key_menu_left), + + /* Key that moves the currently selected slider on the menu right. */ + + CONFIG_VARIABLE_KEY(key_menu_right), + + /* Key to go back to the previous menu. */ + + CONFIG_VARIABLE_KEY(key_menu_back), + + /* Key to activate the currently selected menu item. */ + + CONFIG_VARIABLE_KEY(key_menu_forward), + + /* Key to answer 'yes' to a question in the menu. */ + + CONFIG_VARIABLE_KEY(key_menu_confirm), + + /* Key to answer 'no' to a question in the menu. */ + + CONFIG_VARIABLE_KEY(key_menu_abort), + + /* Keyboard shortcut to bring up the help screen. */ + + CONFIG_VARIABLE_KEY(key_menu_help), + + /* Keyboard shortcut to bring up the save game menu. */ + + CONFIG_VARIABLE_KEY(key_menu_save), + + /* Keyboard shortcut to bring up the load game menu. */ + + CONFIG_VARIABLE_KEY(key_menu_load), + + /* Keyboard shortcut to bring up the sound volume menu. */ + + CONFIG_VARIABLE_KEY(key_menu_volume), + + /* Keyboard shortcut to toggle the detail level. */ + + CONFIG_VARIABLE_KEY(key_menu_detail), + + /* Keyboard shortcut to quicksave the current game. */ + + CONFIG_VARIABLE_KEY(key_menu_qsave), + + /* Keyboard shortcut to end the game. */ + + CONFIG_VARIABLE_KEY(key_menu_endgame), + + /* Keyboard shortcut to toggle heads-up messages. */ + + CONFIG_VARIABLE_KEY(key_menu_messages), + + /* Keyboard shortcut to load the last quicksave. */ + + CONFIG_VARIABLE_KEY(key_menu_qload), + + /* Keyboard shortcut to quit the game. */ + + CONFIG_VARIABLE_KEY(key_menu_quit), + + /* Keyboard shortcut to toggle the gamma correction level. */ + + CONFIG_VARIABLE_KEY(key_menu_gamma), + + /* Keyboard shortcut to switch view in multiplayer. */ + + CONFIG_VARIABLE_KEY(key_spy), + + /* Keyboard shortcut to increase the screen size. */ + + CONFIG_VARIABLE_KEY(key_menu_incscreen), + + /* Keyboard shortcut to decrease the screen size. */ + + CONFIG_VARIABLE_KEY(key_menu_decscreen), + + /* Keyboard shortcut to save a screenshot. */ + + CONFIG_VARIABLE_KEY(key_menu_screenshot), + + /* Key to toggle the map view. */ + + CONFIG_VARIABLE_KEY(key_map_toggle), + + /* Key to pan north when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_north), + + /* Key to pan south when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_south), + + /* Key to pan east when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_east), + + /* Key to pan west when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_west), + + /* Key to zoom in when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_zoomin), + + /* Key to zoom out when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_zoomout), + + /* Key to zoom out the maximum amount when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_maxzoom), + + /* Key to toggle follow mode when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_follow), + + /* Key to toggle the grid display when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_grid), + + /* Key to set a mark when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_mark), + + /* Key to clear all marks when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_clearmark), + + /* Key to select weapon 1. */ + + CONFIG_VARIABLE_KEY(key_weapon1), + + /* Key to select weapon 2. */ + + CONFIG_VARIABLE_KEY(key_weapon2), + + /* Key to select weapon 3. */ + + CONFIG_VARIABLE_KEY(key_weapon3), + + /* Key to select weapon 4. */ + + CONFIG_VARIABLE_KEY(key_weapon4), + + /* Key to select weapon 5. */ + + CONFIG_VARIABLE_KEY(key_weapon5), + + /* Key to select weapon 6. */ + + CONFIG_VARIABLE_KEY(key_weapon6), + + /* Key to select weapon 7. */ + + CONFIG_VARIABLE_KEY(key_weapon7), + + /* Key to select weapon 8. */ + + CONFIG_VARIABLE_KEY(key_weapon8), + + /* Key to cycle to the previous weapon. */ + + CONFIG_VARIABLE_KEY(key_prevweapon), + + /* Key to cycle to the next weapon. */ + + CONFIG_VARIABLE_KEY(key_nextweapon), + + /* @game heretic + * + * Key to use "quartz flask" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_quartz), + + /* @game heretic + * + * Key to use "mystic urn" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_urn), + + /* @game heretic + * + * Key to use "timebomb of the ancients" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_bomb), + + /* @game heretic + * + * Key to use "tome of power" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_tome), + + /* @game heretic + * + * Key to use "ring of invincibility" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_ring), + + /* @game heretic + * + * Key to use "chaos device" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_chaosdevice), + + /* @game heretic + * + * Key to use "shadowsphere" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_shadowsphere), + + /* @game heretic + * + * Key to use "wings of wrath" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_wings), + + /* @game heretic + * + * Key to use "torch" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_torch), + + /* @game heretic + * + * Key to use "morph ovum" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_morph), + + /* @game hexen + * + * Key to use one of each artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_all), + + /* @game hexen + * + * Key to use "quartz flask" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_health), + + /* @game hexen + * + * Key to use "flechette" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_poisonbag), + + /* @game hexen + * + * Key to use "disc of repulsion" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_blastradius), + + /* @game hexen + * + * Key to use "chaos device" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_teleport), + + /* @game hexen + * + * Key to use "banishment device" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_teleportother), + + /* @game hexen + * + * Key to use "porkalator" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_egg), + + /* @game hexen + * + * Key to use "icon of the defender" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_invulnerability), + + /* Key to re-display last message. */ + + CONFIG_VARIABLE_KEY(key_message_refresh), + + /* Key to quit the game when recording a demo. */ + + CONFIG_VARIABLE_KEY(key_demo_quit), + + /* Key to send a message during multiplayer games. */ + + CONFIG_VARIABLE_KEY(key_multi_msg), + + /* Key to send a message to player 1 during multiplayer games. */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer1), + + /* Key to send a message to player 2 during multiplayer games. */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer2), + + /* Key to send a message to player 3 during multiplayer games. */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer3), + + /* Key to send a message to player 4 during multiplayer games. */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer4), + + /* @game hexen strife + * + * Key to send a message to player 5 during multiplayer games. + */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer5), + + /* @game hexen strife + * + * Key to send a message to player 6 during multiplayer games. + */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer6), + + /* @game hexen strife + * + * Key to send a message to player 7 during multiplayer games. + */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer7), + + /* @game hexen strife + * + * Key to send a message to player 8 during multiplayer games. + */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer8), +}; + +static default_collection_t extra_defaults = +{ + extra_defaults_list, + arrlen(extra_defaults_list), + NULL, +}; + +/* Mapping from DOS keyboard scan code to internal key code (as defined + * in doomkey.h). I think I (fraggle) reused this from somewhere else + * but I can't find where. Anyway, notes: + * * KEY_PAUSE is wrong - it's in the KEY_NUMLOCK spot. This shouldn't + * matter in terms of Vanilla compatibility because neither of + * those were valid for key bindings. + * * There is no proper scan code for PrintScreen (on DOS machines it + * sends an interrupt). So I added a fake scan code of 126 for it. + * The presence of this is important so we can bind PrintScreen as + * a screenshot key. + */ + +static const int scantokey[128] = +{ + 0, + 27, + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '0', + '-', + '=', + KEY_BACKSPACE, + 9, + 'q', + 'w', + 'e', + 'r', + 't', + 'y', + 'u', + 'i', + 'o', + 'p', + '[', + ']', + 13, + KEY_RCTRL, + 'a', + 's', + 'd', + 'f', + 'g', + 'h', + 'j', + 'k', + 'l', + ';', + '\'', + '`', + KEY_RSHIFT, + '\\', + 'z', + 'x', + 'c', + 'v', + 'b', + 'n', + 'm', + ',', + '.', + '/', + KEY_RSHIFT, + KEYP_MULTIPLY, + KEY_RALT, + ' ', + KEY_CAPSLOCK, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + + /* KEY_NUMLOCK? */ + + KEY_PAUSE, + KEY_SCRLCK, + KEY_HOME, + KEY_UPARROW, + KEY_PGUP, + KEY_MINUS, + KEY_LEFTARROW, + KEYP_5, + KEY_RIGHTARROW, + KEYP_PLUS, + KEY_END, + KEY_DOWNARROW, + KEY_PGDN, + KEY_INS, + KEY_DEL, + 0, + 0, + 0, + KEY_F11, + KEY_F12, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + KEY_PRTSCR, + 0, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* DEFAULTS */ + +/* Location where all configuration data is stored - default.cfg, savegames, + * etc. + */ + +const char *configdir; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: search_collection + * + * Description: + * Search a collection for a variable + * + ****************************************************************************/ + +static default_t *search_collection(default_collection_t *collection, + const char *name) +{ + int i; + + for (i = 0; i < collection->numdefaults; ++i) + { + if (!strcmp(name, collection->defaults[i].name)) + { + return &collection->defaults[i]; + } + } + + return NULL; +} + +static void save_default_collection(default_collection_t *collection) +{ + default_t *defaults; + int i; + int v; + FILE *f; + + f = fopen(collection->filename, "w"); + if (!f) return; /* can't write the file, but don't complain */ + + defaults = collection->defaults; + + for (i = 0; i < collection->numdefaults; i++) + { + int chars_written; + + /* Ignore unbound variables */ + + if (!defaults[i].bound) + { + continue; + } + + /* Print the name and line up all values at 30 characters */ + + chars_written = fprintf(f, "%s ", defaults[i].name); + + for (; chars_written < 30; ++chars_written) + fprintf(f, " "); + + /* Print the value */ + + switch (defaults[i].type) + { + case DEFAULT_KEY: + + /* use the untranslated version if we can, to reduce + * the possibility of screwing up the user's config + * file + */ + + v = *defaults[i].location.i; + + if (v == KEY_RSHIFT) + { + /* Special case: for shift, force scan code for + * right shift, as this is what Vanilla uses. + * This overrides the change check below, to fix + * configuration files made by old versions that + * mistakenly used the scan code for left shift. + */ + + v = 54; + } + else if (defaults[i].untranslated && + v == defaults[i].original_translated) + { + /* Has not been changed since the last time we read the config + * file. + */ + + v = defaults[i].untranslated; + } + else + { + /* search for a reverse mapping back to a scancode + * in the scantokey table + */ + + int s; + + for (s = 0; s < 128; ++s) + { + if (scantokey[s] == v) + { + v = s; + break; + } + } + } + + fprintf(f, "%i", v); + break; + + case DEFAULT_INT: + fprintf(f, "%i", *defaults[i].location.i); + break; + + case DEFAULT_INT_HEX: + fprintf(f, "0x%x", *defaults[i].location.i); + break; + + case DEFAULT_FLOAT: + fprintf(f, "%f", *defaults[i].location.f); + break; + + case DEFAULT_STRING: + fprintf(f, "\"%s\"", *defaults[i].location.s); + break; + } + + fprintf(f, "\n"); + } + + fclose(f); +} + +/**************************************************************************** + * Name: parse_int_parameter + * + * Description: + * Parses integer values in the configuration file + * + ****************************************************************************/ + +static int parse_int_parameter(const char *strparm) +{ + int param; + + if (strparm[0] == '0' && strparm[1] == 'x') + sscanf(strparm + 2, "%x", (unsigned int *)¶m); + else + sscanf(strparm, "%i", ¶m); + + return param; +} + +static void set_variable(default_t *def, const char *value) +{ + int intparm; + + /* parameter found */ + + switch (def->type) + { + case DEFAULT_STRING: + *def->location.s = m_string_duplicate(value); + break; + + case DEFAULT_INT: + case DEFAULT_INT_HEX: + *def->location.i = parse_int_parameter(value); + break; + + case DEFAULT_KEY: + + /* translate scancodes read from config + * file (save the old value in untranslated) + */ + + intparm = parse_int_parameter(value); + def->untranslated = intparm; + if (intparm >= 0 && intparm < 128) + { + intparm = scantokey[intparm]; + } + else + { + intparm = 0; + } + + def->original_translated = intparm; + *def->location.i = intparm; + break; + + case DEFAULT_FLOAT: + { + /* Different locales use different decimal separators. + * However, the choice of the current locale isn't always + * under our own control. If the atof() function fails to + * parse the string representing the floating point number + * using the current locale's decimal separator, it will + * return 0, resulting in silent sound effects. To + * mitigate this, we replace the first non-digit, + * non-minus character in the string with the current + * locale's decimal separator before passing it to atof(). + */ + + struct lconv *lc = localeconv(); + char dec; + char *str; + int i = 0; + + dec = lc->decimal_point[0]; + str = m_string_duplicate(value); + + /* Skip sign indicators. */ + + if (str[i] == '-' || str[i] == '+') + { + i++; + } + + for (; str[i] != '\0'; i++) + { + if (!isdigit(str[i])) + { + str[i] = dec; + break; + } + } + + *def->location.f = (float)atof(str); + free(str); + } + break; + } +} + +static void load_default_collection(default_collection_t *collection) +{ + FILE *f; + default_t *def; + char defname[80]; + char strparm[100]; + + /* read the file in, overriding any set defaults */ + + f = fopen(collection->filename, "r"); + + if (f == NULL) + { + /* File not opened, but don't complain. + * It's probably just the first time they ran the game. + */ + + return; + } + + while (!feof(f)) + { + if (fscanf(f, "%79s %99[^\n]\n", defname, strparm) != 2) + { + /* This line doesn't match */ + + continue; + } + + /* Find the setting in the list */ + + def = search_collection(collection, defname); + + if (def == NULL || !def->bound) + { + /* Unknown variable? Unbound variables are also treated + * as unknown. + */ + + continue; + } + + /* Strip off trailing non-printable characters (\r characters + * from DOS text files) + */ + + while (strlen(strparm) > 0 && !isprint(strparm[strlen(strparm) - 1])) + { + strparm[strlen(strparm) - 1] = '\0'; + } + + /* Surrounded by quotes? If so, remove them. */ + + if (strlen(strparm) >= 2 && strparm[0] == '"' && + strparm[strlen(strparm) - 1] == '"') + { + strparm[strlen(strparm) - 1] = '\0'; + memmove(strparm, strparm + 1, sizeof(strparm) - 1); + } + + set_variable(def, strparm); + } + + fclose(f); +} + +/* Get a configuration file variable by its name */ + +static default_t *get_default_for_name(const char *name) +{ + default_t *result; + + /* Try the main list and the extras */ + + result = search_collection(&doom_defaults, name); + + if (result == NULL) + { + result = search_collection(&extra_defaults, name); + } + + /* Not found? Internal error. */ + + if (result == NULL) + { + i_error("Unknown configuration variable: '%s'", name); + } + + return result; +} + +/* Get the path to the default configuration dir to use, if NULL + * is passed to m_set_config_dir. + */ + +static char *get_default_config_dir(void) +{ + /* Configuration settings are stored in an OS-appropriate path + * determined by SDL. On typical Unix systems, this might be + * ~/.local/share/chocolate-doom. On Windows, we behave like + * Vanilla Doom and save in the current directory. + */ + + char *result = CONFIG_GAMES_NXDOOM_PREFDIR "/"; + char *copy = m_string_duplicate(result); + return copy; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Set the default filenames to use for configuration files. */ + +void m_set_config_filenames(const char *main_config, + const char *extra_config) +{ + default_main_config = main_config; + default_extra_config = extra_config; +} + +void m_save_defaults(void) +{ + save_default_collection(&doom_defaults); + save_default_collection(&extra_defaults); +} + +/* Save defaults to alternate filenames */ + +void m_save_defaults_alternate(const char *main, const char *extra) +{ + const char *orig_main; + const char *orig_extra; + + /* Temporarily change the filenames */ + + orig_main = doom_defaults.filename; + orig_extra = extra_defaults.filename; + + doom_defaults.filename = main; + extra_defaults.filename = extra; + + m_save_defaults(); + + /* Restore normal filenames */ + + doom_defaults.filename = orig_main; + extra_defaults.filename = orig_extra; +} + +void m_load_defaults(void) +{ + int i; + + /* This variable is a special snowflake for no good reason. */ + + m_bind_string_variable("autoload_path", &autoload_path); + + /* check for a custom default file */ + + /* @arg + * @vanilla + * + * Load main configuration from the specified file, instead of the + * default. + */ + + i = m_check_parm_with_args("-config", 1); + + if (i) + { + doom_defaults.filename = myargv[i + 1]; + printf("\tdefault file: %s\n", doom_defaults.filename); + } + else + { + doom_defaults.filename = + m_string_join(configdir, default_main_config, NULL); + } + + printf("saving config in %s\n", doom_defaults.filename); + + /* @arg + * + * Load additional configuration from the specified file, instead of + * the default. + */ + + i = m_check_parm_with_args("-extraconfig", 1); + + if (i) + { + extra_defaults.filename = myargv[i + 1]; + printf(" extra configuration file: %s\n", + extra_defaults.filename); + } + else + { + extra_defaults.filename = + m_string_join(configdir, default_extra_config, NULL); + } + + load_default_collection(&doom_defaults); + load_default_collection(&extra_defaults); +} + +/* Bind a variable to a given configuration file variable, by name. */ + +void m_bind_int_variable(const char *name, int *location) +{ + default_t *variable; + + variable = get_default_for_name(name); + assert(variable->type == DEFAULT_INT || + variable->type == DEFAULT_INT_HEX || variable->type == DEFAULT_KEY); + + variable->location.i = location; + variable->bound = true; +} + +void m_bind_float_variable(const char *name, float *location) +{ + default_t *variable; + + variable = get_default_for_name(name); + assert(variable->type == DEFAULT_FLOAT); + + variable->location.f = location; + variable->bound = true; +} + +void m_bind_string_variable(const char *name, char **location) +{ + default_t *variable; + + variable = get_default_for_name(name); + assert(variable->type == DEFAULT_STRING); + + variable->location.s = location; + variable->bound = true; +} + +/* Set the value of a particular variable; an API function for other + * parts of the program to assign values to config variables by name. + */ + +boolean m_set_variable(const char *name, const char *value) +{ + default_t *variable; + + variable = get_default_for_name(name); + + if (variable == NULL || !variable->bound) + { + return false; + } + + set_variable(variable, value); + + return true; +} + +const char *m_get_string_variable(const char *name) +{ + default_t *variable; + + variable = get_default_for_name(name); + + if (variable == NULL || !variable->bound || + variable->type != DEFAULT_STRING) + { + return NULL; + } + + return *variable->location.s; +} + +/* SetConfigDir: + * + * Sets the location of the configuration directory, where configuration + * files are stored - default.cfg, chocolate-doom.cfg, savegames, etc. + */ + +void m_set_config_dir(const char *dir) +{ + /* Use the directory that was passed, or find the default. */ + + if (dir != NULL) + { + configdir = dir; + } + else + { + configdir = get_default_config_dir(); + } + + if (strcmp(configdir, exedir) != 0) + { + printf("Using %s for configuration and saves\n", configdir); + } + + /* Make the directory if it doesn't already exist: */ + + m_make_directory(configdir); +} + +/* Set the value of music_pack_path if it is currently empty, and create + * the directory if necessary. + */ + +void m_set_music_pack_dir(void) +{ + const char *current_path; + char *prefdir; + char *music_pack_path; + + current_path = m_get_string_variable("music_pack_path"); + + if (current_path != NULL && strlen(current_path) > 0) + { + return; + } + + prefdir = CONFIG_GAMES_NXDOOM_PREFDIR "/"; + music_pack_path = m_string_join(prefdir, "music-packs", NULL); + + m_make_directory(prefdir); + m_make_directory(music_pack_path); + m_set_variable("music_pack_path", music_pack_path); + + free(music_pack_path); +} + +/* Calculate the path to the directory to use to store save games. + * Creates the directory as necessary. + */ + +char *m_get_save_game_dir(const char *iwadname) +{ + char *savegamedir; + char *topdir; + int p; + + /* @arg + * + * Specify a path from which to load and save games. If the + * directory does not exist then it will automatically be created. + */ + + p = m_check_parm_with_args("-savedir", 1); + if (p) + { + savegamedir = myargv[p + 1]; + if (!m_file_exists(savegamedir)) + { + m_make_directory(savegamedir); + } + + /* add separator at end just in case */ + + savegamedir = m_string_join(savegamedir, DIR_SEPARATOR_S, NULL); + + printf("Save directory changed to %s.\n", savegamedir); + } + + /* If not "doing" a configuration directory (Windows), don't "do" + * a savegame directory, either. + */ + + else if (!strcmp(configdir, exedir)) + { + savegamedir = m_string_duplicate(""); + } + else + { + /* ~/.local/share/chocolate-doom/savegames */ + + topdir = m_string_join(configdir, "savegames", NULL); + m_make_directory(topdir); + + /* eg. ~/.local/share/chocolate-doom/savegames/doom2.wad/ */ + + savegamedir = m_string_join(topdir, DIR_SEPARATOR_S, iwadname, + DIR_SEPARATOR_S, NULL); + + m_make_directory(savegamedir); + + free(topdir); + } + + return savegamedir; +} + +/* Calculate the path to the directory for autoloaded WADs/DEHs. + * Creates the directory as necessary. + */ + +char *m_get_autoload_dir(const char *iwadname) +{ + char *result; + + if (autoload_path == NULL || strlen(autoload_path) == 0) + { + char *prefdir = CONFIG_GAMES_NXDOOM_PREFDIR "/"; + autoload_path = m_string_join(prefdir, "autoload", NULL); + } + + m_make_directory(autoload_path); + + result = m_string_join(autoload_path, DIR_SEPARATOR_S, iwadname, NULL); + m_make_directory(result); + + /* TODO: Add README file */ + + return result; +} diff --git a/games/NXDoom/src/m_config.h b/games/NXDoom/src/m_config.h new file mode 100644 index 00000000000..5dd8358b742 --- /dev/null +++ b/games/NXDoom/src/m_config.h @@ -0,0 +1,58 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_config.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Configuration file interface. + * + ****************************************************************************/ + +#ifndef __M_CONFIG__ +#define __M_CONFIG__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern const char *configdir; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void m_load_defaults(void); +void m_save_defaults(void); +void m_save_defaults_alternate(const char *main, const char *extra); +void m_set_config_dir(const char *dir); +void m_set_music_pack_dir(void); +void m_bind_int_variable(const char *name, int *variable); +void m_bind_float_variable(const char *name, float *variable); +void m_bind_string_variable(const char *name, char **variable); +boolean m_set_variable(const char *name, const char *value); +const char *m_get_string_variable(const char *name); +void m_set_config_filenames(const char *main_config, + const char *extra_config); +char *m_get_save_game_dir(const char *iwadname); +char *m_get_autoload_dir(const char *iwadname); + +#endif /* __M_CONFIG__ */ diff --git a/games/NXDoom/src/m_controls.c b/games/NXDoom/src/m_controls.c new file mode 100644 index 00000000000..2266df2ceaf --- /dev/null +++ b/games/NXDoom/src/m_controls.c @@ -0,0 +1,467 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_controls.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomkeys.h" +#include "doomtype.h" + +#include "m_config.h" +#include "m_misc.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Keyboard controls */ + +int key_right = KEY_RIGHTARROW; +int key_left = KEY_LEFTARROW; + +int key_up = KEY_UPARROW; +int key_down = KEY_DOWNARROW; +int key_strafeleft = 0x2c; /* Comma, ',' */ +int key_straferight = '.'; +int key_fire = KEY_RCTRL; +int key_use = ' '; +int key_strafe = KEY_RALT; +int key_speed = KEY_RSHIFT; + +/* Heretic keyboard controls */ + +int key_flyup = KEY_PGUP; +int key_flydown = KEY_INS; +int key_flycenter = KEY_HOME; + +int key_lookup = KEY_PGDN; +int key_lookdown = KEY_DEL; +int key_lookcenter = KEY_END; + +int key_invleft = '['; +int key_invright = ']'; +int key_useartifact = KEY_ENTER; + +int key_arti_quartz = 0; +int key_arti_urn = 0; +int key_arti_bomb = 0; +int key_arti_tome = 127; +int key_arti_ring = 0; +int key_arti_chaosdevice = 0; +int key_arti_shadowsphere = 0; +int key_arti_wings = 0; +int key_arti_torch = 0; +int key_arti_morph = 0; + +/* Hexen key controls */ + +int key_jump = '/'; + +int key_arti_all = KEY_BACKSPACE; +int key_arti_health = '\\'; +int key_arti_poisonbag = '0'; +int key_arti_blastradius = '9'; +int key_arti_teleport = '8'; +int key_arti_teleportother = '7'; +int key_arti_egg = '6'; +int key_arti_invulnerability = '5'; + +/* Strife key controls + * + * haleyjd 09/01/10 + * + * Note: Strife also uses key_invleft, key_invright, key_jump, key_lookup, + * and key_lookdown, but with different default values. + */ + +int key_usehealth = 'h'; +int key_invquery = 'q'; +int key_mission = 'w'; +int key_invpop = 'z'; +int key_invkey = 'k'; +int key_invhome = KEY_HOME; +int key_invend = KEY_END; +int key_invuse = KEY_ENTER; +int key_invdrop = KEY_BACKSPACE; + +/* Mouse controls */ + +int mousebfire = 0; +int mousebstrafe = 1; +int mousebforward = 2; +int mousebspeed = 3; + +int mousebjump = -1; + +int mousebstrafeleft = -1; +int mousebstraferight = -1; +int mousebturnleft = -1; +int mousebturnright = -1; +int mousebbackward = -1; +int mousebuse = -1; + +int mousebprevweapon = -1; +int mousebnextweapon = -1; +int mousebinvleft = -1; +int mousebinvright = -1; +int mousebuseartifact = -1; + +int key_message_refresh = KEY_ENTER; +int key_pause = KEY_PAUSE; +int key_demo_quit = 'q'; +int key_spy = KEY_F12; + +/* Multiplayer chat keys: */ + +int key_multi_msg = 't'; +int key_multi_msgplayer[8]; + +/* Weapon selection keys: */ + +int key_weapon1 = '1'; +int key_weapon2 = '2'; +int key_weapon3 = '3'; +int key_weapon4 = '4'; +int key_weapon5 = '5'; +int key_weapon6 = '6'; +int key_weapon7 = '7'; +int key_weapon8 = '8'; +int key_prevweapon = 0; +int key_nextweapon = 0; + +/* Map control keys: */ + +int key_map_north = KEY_UPARROW; +int key_map_south = KEY_DOWNARROW; +int key_map_east = KEY_RIGHTARROW; +int key_map_west = KEY_LEFTARROW; +int key_map_zoomin = '='; +int key_map_zoomout = '-'; +int key_map_toggle = KEY_TAB; +int key_map_maxzoom = '0'; +int key_map_follow = 'f'; +int key_map_grid = 'g'; +int key_map_mark = 'm'; +int key_map_clearmark = 'c'; + +/* menu keys: */ + +int key_menu_activate = KEY_ESCAPE; +int key_menu_up = KEY_UPARROW; +int key_menu_down = KEY_DOWNARROW; +int key_menu_left = KEY_LEFTARROW; +int key_menu_right = KEY_RIGHTARROW; +int key_menu_back = KEY_BACKSPACE; +int key_menu_forward = KEY_ENTER; +int key_menu_confirm = 'y'; +int key_menu_abort = 'n'; + +int key_menu_help = KEY_F1; +int key_menu_save = KEY_F2; +int key_menu_load = KEY_F3; +int key_menu_volume = KEY_F4; +int key_menu_detail = KEY_F5; +int key_menu_qsave = KEY_F6; +int key_menu_endgame = KEY_F7; +int key_menu_messages = KEY_F8; +int key_menu_qload = KEY_F9; +int key_menu_quit = KEY_F10; +int key_menu_gamma = KEY_F11; + +int key_menu_incscreen = KEY_EQUALS; +int key_menu_decscreen = KEY_MINUS; +int key_menu_screenshot = 0; + +/* Joystick controls */ + +int joybfire = 0; +int joybstrafe = 1; +int joybuse = 3; +int joybspeed = 2; + +int joybstrafeleft = -1; +int joybstraferight = -1; + +int joybjump = -1; + +int joybprevweapon = -1; +int joybnextweapon = -1; + +int joybmenu = -1; +int joybautomap = -1; + +int joybuseartifact = -1; +int joybinvleft = -1; +int joybinvright = -1; + +int joybflyup = -1; +int joybflydown = -1; +int joybflycenter = -1; + +/* Control whether if a mouse button is double clicked, it acts like + * "use" has been pressed + */ + +int dclick_use = 1; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Bind all of the common controls used by Doom and all other games. */ + +void m_bind_base_controls(void) +{ + m_bind_int_variable("key_right", &key_right); + m_bind_int_variable("key_left", &key_left); + m_bind_int_variable("key_up", &key_up); + m_bind_int_variable("key_down", &key_down); + m_bind_int_variable("key_strafeleft", &key_strafeleft); + m_bind_int_variable("key_straferight", &key_straferight); + m_bind_int_variable("key_fire", &key_fire); + m_bind_int_variable("key_use", &key_use); + m_bind_int_variable("key_strafe", &key_strafe); + m_bind_int_variable("key_speed", &key_speed); + + m_bind_int_variable("mouseb_fire", &mousebfire); + m_bind_int_variable("mouseb_strafe", &mousebstrafe); + m_bind_int_variable("mouseb_forward", &mousebforward); + m_bind_int_variable("mouseb_speed", &mousebspeed); + + m_bind_int_variable("joyb_fire", &joybfire); + m_bind_int_variable("joyb_strafe", &joybstrafe); + m_bind_int_variable("joyb_use", &joybuse); + m_bind_int_variable("joyb_speed", &joybspeed); + + m_bind_int_variable("joyb_menu_activate", &joybmenu); + m_bind_int_variable("joyb_toggle_automap", &joybautomap); + + /* Extra controls that are not in the Vanilla versions: */ + + m_bind_int_variable("joyb_strafeleft", &joybstrafeleft); + m_bind_int_variable("joyb_straferight", &joybstraferight); + m_bind_int_variable("mouseb_strafeleft", &mousebstrafeleft); + m_bind_int_variable("mouseb_straferight", &mousebstraferight); + m_bind_int_variable("mouseb_turnleft", &mousebturnleft); + m_bind_int_variable("mouseb_turnright", &mousebturnright); + m_bind_int_variable("mouseb_use", &mousebuse); + m_bind_int_variable("mouseb_backward", &mousebbackward); + m_bind_int_variable("dclick_use", &dclick_use); + m_bind_int_variable("key_pause", &key_pause); + m_bind_int_variable("key_message_refresh", &key_message_refresh); +} + +void m_bind_heretic_controls(void) +{ + m_bind_int_variable("key_flyup", &key_flyup); + m_bind_int_variable("key_flydown", &key_flydown); + m_bind_int_variable("key_flycenter", &key_flycenter); + + m_bind_int_variable("key_lookup", &key_lookup); + m_bind_int_variable("key_lookdown", &key_lookdown); + m_bind_int_variable("key_lookcenter", &key_lookcenter); + + m_bind_int_variable("key_invleft", &key_invleft); + m_bind_int_variable("key_invright", &key_invright); + m_bind_int_variable("key_useartifact", &key_useartifact); + + m_bind_int_variable("mouseb_invleft", &mousebinvleft); + m_bind_int_variable("mouseb_invright", &mousebinvright); + m_bind_int_variable("mouseb_useartifact", &mousebuseartifact); + + m_bind_int_variable("joyb_invleft", &joybinvleft); + m_bind_int_variable("joyb_invright", &joybinvright); + m_bind_int_variable("joyb_useartifact", &joybuseartifact); + + m_bind_int_variable("joyb_flyup", &joybflyup); + m_bind_int_variable("joyb_flydown", &joybflydown); + m_bind_int_variable("joyb_flycenter", &joybflycenter); + + m_bind_int_variable("key_arti_quartz", &key_arti_quartz); + m_bind_int_variable("key_arti_urn", &key_arti_urn); + m_bind_int_variable("key_arti_bomb", &key_arti_bomb); + m_bind_int_variable("key_arti_tome", &key_arti_tome); + m_bind_int_variable("key_arti_ring", &key_arti_ring); + m_bind_int_variable("key_arti_chaosdevice", &key_arti_chaosdevice); + m_bind_int_variable("key_arti_shadowsphere", &key_arti_shadowsphere); + m_bind_int_variable("key_arti_wings", &key_arti_wings); + m_bind_int_variable("key_arti_torch", &key_arti_torch); + m_bind_int_variable("key_arti_morph", &key_arti_morph); +} + +void m_bind_hexen_controls(void) +{ + m_bind_int_variable("key_jump", &key_jump); + m_bind_int_variable("mouseb_jump", &mousebjump); + m_bind_int_variable("joyb_jump", &joybjump); + + m_bind_int_variable("key_arti_all", &key_arti_all); + m_bind_int_variable("key_arti_health", &key_arti_health); + m_bind_int_variable("key_arti_poisonbag", &key_arti_poisonbag); + m_bind_int_variable("key_arti_blastradius", &key_arti_blastradius); + m_bind_int_variable("key_arti_teleport", &key_arti_teleport); + m_bind_int_variable("key_arti_teleportother", &key_arti_teleportother); + m_bind_int_variable("key_arti_egg", &key_arti_egg); + m_bind_int_variable("key_arti_invulnerability", &key_arti_invulnerability); +} + +void m_bind_strife_controls(void) +{ + /* These are shared with all games, but have different defaults: */ + + key_message_refresh = '/'; + + /* These keys are shared with Heretic/Hexen but have different defaults: */ + + key_jump = 'a'; + key_lookup = KEY_PGUP; + key_lookdown = KEY_PGDN; + key_invleft = KEY_INS; + key_invright = KEY_DEL; + + m_bind_int_variable("key_jump", &key_jump); + m_bind_int_variable("key_look_up", &key_lookup); + m_bind_int_variable("key_look_down", &key_lookdown); + m_bind_int_variable("key_inv_left", &key_invleft); + m_bind_int_variable("key_inv_right", &key_invright); + + /* Custom Strife-only Keys: */ + + m_bind_int_variable("key_use_health", &key_usehealth); + m_bind_int_variable("key_invquery", &key_invquery); + m_bind_int_variable("key_mission", &key_mission); + m_bind_int_variable("key_inv_pop", &key_invpop); + m_bind_int_variable("key_inv_key", &key_invkey); + m_bind_int_variable("key_invHome", &key_invhome); + m_bind_int_variable("key_inv_end", &key_invend); + m_bind_int_variable("key_inv_use", &key_invuse); + m_bind_int_variable("key_inv_drop", &key_invdrop); + + /* Strife also supports jump on mouse and joystick, and in the exact same + * manner as Hexen! + */ + + m_bind_int_variable("mouseb_jump", &mousebjump); + m_bind_int_variable("joyb_jump", &joybjump); + + /* Subset of inventory actions common to Heretic/Hexen and Strife. */ + + m_bind_int_variable("joyb_invleft", &joybinvleft); + m_bind_int_variable("joyb_invright", &joybinvright); + + /* This is technically "invuse" in Strife, but let's reuse the value. */ + + m_bind_int_variable("joyb_useartifact", &joybuseartifact); +} + +void m_bind_weapon_controls(void) +{ + m_bind_int_variable("key_weapon1", &key_weapon1); + m_bind_int_variable("key_weapon2", &key_weapon2); + m_bind_int_variable("key_weapon3", &key_weapon3); + m_bind_int_variable("key_weapon4", &key_weapon4); + m_bind_int_variable("key_weapon5", &key_weapon5); + m_bind_int_variable("key_weapon6", &key_weapon6); + m_bind_int_variable("key_weapon7", &key_weapon7); + m_bind_int_variable("key_weapon8", &key_weapon8); + + m_bind_int_variable("key_prevweapon", &key_prevweapon); + m_bind_int_variable("key_nextweapon", &key_nextweapon); + + m_bind_int_variable("joyb_prevweapon", &joybprevweapon); + m_bind_int_variable("joyb_nextweapon", &joybnextweapon); + + m_bind_int_variable("mouseb_prevweapon", &mousebprevweapon); + m_bind_int_variable("mouseb_nextweapon", &mousebnextweapon); +} + +void m_bind_map_controls(void) +{ + m_bind_int_variable("key_map_north", &key_map_north); + m_bind_int_variable("key_map_south", &key_map_south); + m_bind_int_variable("key_map_east", &key_map_east); + m_bind_int_variable("key_map_west", &key_map_west); + m_bind_int_variable("key_map_zoomin", &key_map_zoomin); + m_bind_int_variable("key_map_zoomout", &key_map_zoomout); + m_bind_int_variable("key_map_toggle", &key_map_toggle); + m_bind_int_variable("key_map_maxzoom", &key_map_maxzoom); + m_bind_int_variable("key_map_follow", &key_map_follow); + m_bind_int_variable("key_map_grid", &key_map_grid); + m_bind_int_variable("key_map_mark", &key_map_mark); + m_bind_int_variable("key_map_clearmark", &key_map_clearmark); +} + +void m_bind_menu_controls(void) +{ + m_bind_int_variable("key_menu_activate", &key_menu_activate); + m_bind_int_variable("key_menu_up", &key_menu_up); + m_bind_int_variable("key_menu_down", &key_menu_down); + m_bind_int_variable("key_menu_left", &key_menu_left); + m_bind_int_variable("key_menu_right", &key_menu_right); + m_bind_int_variable("key_menu_back", &key_menu_back); + m_bind_int_variable("key_menu_forward", &key_menu_forward); + m_bind_int_variable("key_menu_confirm", &key_menu_confirm); + m_bind_int_variable("key_menu_abort", &key_menu_abort); + + m_bind_int_variable("key_menu_help", &key_menu_help); + m_bind_int_variable("key_menu_save", &key_menu_save); + m_bind_int_variable("key_menu_load", &key_menu_load); + m_bind_int_variable("key_menu_volume", &key_menu_volume); + m_bind_int_variable("key_menu_detail", &key_menu_detail); + m_bind_int_variable("key_menu_qsave", &key_menu_qsave); + m_bind_int_variable("key_menu_endgame", &key_menu_endgame); + m_bind_int_variable("key_menu_messages", &key_menu_messages); + m_bind_int_variable("key_menu_qload", &key_menu_qload); + m_bind_int_variable("key_menu_quit", &key_menu_quit); + m_bind_int_variable("key_menu_gamma", &key_menu_gamma); + + m_bind_int_variable("key_menu_incscreen", &key_menu_incscreen); + m_bind_int_variable("key_menu_decscreen", &key_menu_decscreen); + m_bind_int_variable("key_menu_screenshot", &key_menu_screenshot); + m_bind_int_variable("key_demo_quit", &key_demo_quit); + m_bind_int_variable("key_spy", &key_spy); +} + +void m_bind_chat_controls(unsigned int num_players) +{ + char name[32]; /* haleyjd: 20 not large enough - Thank you, come again! */ + unsigned int i; /* haleyjd: signedness conflict */ + + m_bind_int_variable("key_multi_msg", &key_multi_msg); + + for (i = 0; i < num_players; ++i) + { + snprintf(name, sizeof(name), "key_multi_msgplayer%i", i + 1); + m_bind_int_variable(name, &key_multi_msgplayer[i]); + } +} + +/* Apply custom patches to the default values depending on the + * platform we are running on. + */ + +void m_apply_platform_defaults(void) +{ + /* no-op. Add your platform-specific patches here. */ +} diff --git a/games/NXDoom/src/m_controls.h b/games/NXDoom/src/m_controls.h new file mode 100644 index 00000000000..fb756ec119e --- /dev/null +++ b/games/NXDoom/src/m_controls.h @@ -0,0 +1,207 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_controls.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef __M_CONTROLS_H__ +#define __M_CONTROLS_H__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +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 key_jump; + +extern int key_flyup; +extern int key_flydown; +extern int key_flycenter; +extern int key_lookup; +extern int key_lookdown; +extern int key_lookcenter; +extern int key_invleft; +extern int key_invright; +extern int key_useartifact; + +/* villsa [STRIFE] strife keys */ + +extern int key_usehealth; +extern int key_invquery; +extern int key_mission; +extern int key_invpop; +extern int key_invkey; +extern int key_invhome; +extern int key_invend; +extern int key_invuse; +extern int key_invdrop; + +extern int key_message_refresh; +extern int key_pause; + +extern int key_multi_msg; +extern int key_multi_msgplayer[8]; + +extern int key_weapon1; +extern int key_weapon2; +extern int key_weapon3; +extern int key_weapon4; +extern int key_weapon5; +extern int key_weapon6; +extern int key_weapon7; +extern int key_weapon8; + +extern int key_arti_quartz; +extern int key_arti_urn; +extern int key_arti_bomb; +extern int key_arti_tome; +extern int key_arti_ring; +extern int key_arti_chaosdevice; +extern int key_arti_shadowsphere; +extern int key_arti_wings; +extern int key_arti_torch; +extern int key_arti_morph; + +extern int key_arti_all; +extern int key_arti_health; +extern int key_arti_poisonbag; +extern int key_arti_blastradius; +extern int key_arti_teleport; +extern int key_arti_teleportother; +extern int key_arti_egg; +extern int key_arti_invulnerability; + +extern int key_demo_quit; +extern int key_spy; +extern int key_prevweapon; +extern int key_nextweapon; + +extern int key_map_north; +extern int key_map_south; +extern int key_map_east; +extern int key_map_west; +extern int key_map_zoomin; +extern int key_map_zoomout; +extern int key_map_toggle; +extern int key_map_maxzoom; +extern int key_map_follow; +extern int key_map_grid; +extern int key_map_mark; +extern int key_map_clearmark; + +/* menu keys: */ + +extern int key_menu_activate; +extern int key_menu_up; +extern int key_menu_down; +extern int key_menu_left; +extern int key_menu_right; +extern int key_menu_back; +extern int key_menu_forward; +extern int key_menu_confirm; +extern int key_menu_abort; + +extern int key_menu_help; +extern int key_menu_save; +extern int key_menu_load; +extern int key_menu_volume; +extern int key_menu_detail; +extern int key_menu_qsave; +extern int key_menu_endgame; +extern int key_menu_messages; +extern int key_menu_qload; +extern int key_menu_quit; +extern int key_menu_gamma; + +extern int key_menu_incscreen; +extern int key_menu_decscreen; +extern int key_menu_screenshot; + +extern int mousebfire; +extern int mousebstrafe; +extern int mousebforward; +extern int mousebspeed; + +extern int mousebjump; + +extern int mousebstrafeleft; +extern int mousebstraferight; +extern int mousebturnleft; +extern int mousebturnright; +extern int mousebbackward; +extern int mousebuse; + +extern int mousebprevweapon; +extern int mousebnextweapon; +extern int mousebinvleft; +extern int mousebinvright; +extern int mousebuseartifact; + +extern int joybfire; +extern int joybstrafe; +extern int joybuse; +extern int joybspeed; + +extern int joybjump; + +extern int joybstrafeleft; +extern int joybstraferight; + +extern int joybprevweapon; +extern int joybnextweapon; + +extern int joybmenu; +extern int joybautomap; + +extern int joybuseartifact; +extern int joybinvleft; +extern int joybinvright; + +extern int joybflyup; +extern int joybflydown; +extern int joybflycenter; + +extern int dclick_use; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void m_bind_base_controls(void); +void m_bind_heretic_controls(void); +void m_bind_hexen_controls(void); +void m_bind_strife_controls(void); +void m_bind_weapon_controls(void); +void m_bind_map_controls(void); +void m_bind_menu_controls(void); +void m_bind_chat_controls(unsigned int num_players); + +void m_apply_platform_defaults(void); + +#endif /* __M_CONTROLS_H__ */ diff --git a/games/NXDoom/src/m_fixed.c b/games/NXDoom/src/m_fixed.c new file mode 100644 index 00000000000..c8931d96c18 --- /dev/null +++ b/games/NXDoom/src/m_fixed.c @@ -0,0 +1,62 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_fixed.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Fixed point implementation. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "stdlib.h" + +#include "doomtype.h" +#include "i_system.h" + +#include "m_fixed.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Fixme. __USE_C_FIXED__ or something. */ + +fixed_t fixed_mul(fixed_t a, fixed_t b) +{ + return ((int64_t)a * (int64_t)b) >> FRACBITS; +} + +/* fixed_div, C version. */ + +fixed_t fixed_div(fixed_t a, fixed_t b) +{ + if ((abs(a) >> 14) >= abs(b)) + { + return (a ^ b) < 0 ? INT_MIN : INT_MAX; + } + else + { + int64_t result; + + result = ((int64_t)a << FRACBITS) / b; + + return (fixed_t)result; + } +} diff --git a/games/NXDoom/src/m_fixed.h b/games/NXDoom/src/m_fixed.h new file mode 100644 index 00000000000..abb837b318b --- /dev/null +++ b/games/NXDoom/src/m_fixed.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_fixed.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Fixed point arithemtics, implementation. + * + ****************************************************************************/ + +#ifndef __M_FIXED__ +#define __M_FIXED__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Fixed point, 32bit as 16.16. */ + +#define FRACBITS 16 +#define FRACUNIT (1 << FRACBITS) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef int fixed_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +fixed_t fixed_mul(fixed_t a, fixed_t b); +fixed_t fixed_div(fixed_t a, fixed_t b); + +#endif /* __M_FIXED__ */ diff --git a/games/NXDoom/src/m_misc.c b/games/NXDoom/src/m_misc.c new file mode 100644 index 00000000000..515ff46f0ca --- /dev/null +++ b/games/NXDoom/src/m_misc.c @@ -0,0 +1,621 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_misc.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Miscellaneous. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include "doomtype.h" + +#include "i_system.h" +#include "m_misc.h" +#include "z_zone.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void m_make_directory(const char *path) +{ + mkdir(path, 0755); +} + +boolean m_file_exists(const char *filename) +{ + FILE *fstream; + + fstream = fopen(filename, "r"); + + if (fstream != NULL) + { + fclose(fstream); + return true; + } + else + { + /* If we can't open because the file is a directory, the + * "file" exists at least! + */ + + return errno == EISDIR; + } +} + +/* Check if a file exists by probing for common case variation of its + * filename. Returns a newly allocated string that the caller is responsible + * for freeing. + */ + +char *m_file_case_exists(const char *path) +{ + char *path_dup; + char *filename; + char *ext; + + path_dup = m_string_duplicate(path); + + /* 0: actual path */ + + if (m_file_exists(path_dup)) + { + return path_dup; + } + + filename = strrchr(path_dup, DIR_SEPARATOR); + if (filename != NULL) + { + filename++; + } + else + { + filename = path_dup; + } + + /* 1: lowercase filename, e.g. doom2.wad */ + + m_force_lowercase(filename); + + if (m_file_exists(path_dup)) + { + return path_dup; + } + + /* 2: uppercase filename, e.g. DOOM2.WAD */ + + m_force_uppercase(filename); + + if (m_file_exists(path_dup)) + { + return path_dup; + } + + /* 3. uppercase basename with lowercase extension, e.g. DOOM2.wad */ + + ext = strrchr(path_dup, '.'); + if (ext != NULL && ext > filename) + { + m_force_lowercase(ext + 1); + + if (m_file_exists(path_dup)) + { + return path_dup; + } + } + + /* 4. lowercase filename with uppercase first letter, e.g. Doom2.wad */ + + if (strlen(filename) > 1) + { + m_force_lowercase(filename + 1); + + if (m_file_exists(path_dup)) + { + return path_dup; + } + } + + /* 5. no luck */ + + free(path_dup); + return NULL; +} + +long m_file_length(FILE *handle) +{ + long savedpos; + long length; + + /* save the current position in the file */ + + savedpos = ftell(handle); + + /* jump to the end and find the length */ + + fseek(handle, 0, SEEK_END); + length = ftell(handle); + + /* go back to the old location */ + + fseek(handle, savedpos, SEEK_SET); + + return length; +} + +boolean m_write_file(const char *name, const void *source, int length) +{ + FILE *handle; + int count; + + handle = fopen(name, "wb"); + + if (handle == NULL) return false; + + count = fwrite(source, 1, length, handle); + fclose(handle); + + if (count < length) return false; + + return true; +} + +int m_read_file(const char *name, byte **buffer) +{ + FILE *handle; + int count; + int length; + byte *buf; + + handle = fopen(name, "rb"); + if (handle == NULL) i_error("Couldn't read file %s", name); + + /* find the size of the file by seeking to the end and + * reading the current position + */ + + length = m_file_length(handle); + + buf = z_malloc(length + 1, PU_STATIC, NULL); + count = fread(buf, 1, length, handle); + fclose(handle); + + if (count < length) i_error("Couldn't read file %s", name); + + buf[length] = '\0'; + *buffer = buf; + return length; +} + +/* Returns the path to a temporary file of the given name, stored + * inside the system temporary directory. + * + * The returned value must be freed with z_free after use. + */ + +char *m_temp_file(const char *s) +{ + const char *tempdir; + + /* Check the $TMPDIR environment variable to find the location. */ + + tempdir = getenv("TMPDIR"); + + if (tempdir == NULL) + { + tempdir = "/tmp"; + } + + return m_string_join(tempdir, DIR_SEPARATOR_S, s, NULL); +} + +boolean m_str_to_int(const char *str, int *result) +{ + return sscanf(str, " 0x%x", (unsigned int *)result) == 1 || + sscanf(str, " 0X%x", (unsigned int *)result) == 1 || + sscanf(str, " 0%o", (unsigned int *)result) == 1 || + sscanf(str, " %d", result) == 1; +} + +/* Returns the directory portion of the given path, without the trailing + * slash separator character. If no directory is described in the path, + * the string "." is returned. In either case, the result is newly allocated + * and must be freed by the caller after use. + */ + +char *m_dir_name(const char *path) +{ + char *result; + const char *pf; + const char *pb; + + pf = strrchr(path, '/'); + pb = NULL; + + if (pf == NULL && pb == NULL) + { + return m_string_duplicate("."); + } + else + { + const char *p = (pb > pf) ? pb : pf; + result = m_string_duplicate(path); + result[p - path] = '\0'; + return result; + } +} + +/* Returns the base filename described by the given path (without the + * directory name). The result points inside path and nothing new is + * allocated. + */ + +const char *m_base_name(const char *path) +{ + const char *pf; + const char *pb; + + pf = strrchr(path, '/'); + pb = NULL; + + if (pf == NULL && pb == NULL) + { + return path; + } + else + { + const char *p = (pb > pf) ? pb : pf; + return p + 1; + } +} + +void m_extract_file_base(const char *path, char *dest) +{ + const char *src; + const char *filename; + int length; + + src = path + strlen(path) - 1; + + /* back up until a \ or the start */ + + while (src != path && *(src - 1) != DIR_SEPARATOR) + { + src--; + } + + filename = src; + + /* Copy up to eight characters + * Note: Vanilla Doom exits with an error if a filename is specified + * with a base of more than eight characters. To remove the 8.3 + * filename limit, instead we simply truncate the name. + */ + + length = 0; + memset(dest, 0, 8); + + while (*src != '\0' && *src != '.') + { + if (length >= 8) + { + printf("Warning: Truncated '%s' lump name to '%.8s'.\n", filename, + dest); + break; + } + + dest[length++] = toupper((int)*src++); + } +} + +/**************************************************************************** + * Name: m_force_uppercase + * + * Description: + * (PROC) Change string to uppercase. + * + ****************************************************************************/ + +void m_force_uppercase(char *text) +{ + char *p; + + for (p = text; *p != '\0'; ++p) + { + *p = toupper(*p); + } +} + +/**************************************************************************** + * Name: m_force_lowercase + * + * Description: + * (PROC) Change string to lowercase. + * + ****************************************************************************/ + +void m_force_lowercase(char *text) +{ + char *p; + + for (p = text; *p != '\0'; ++p) + { + *p = tolower(*p); + } +} + +/**************************************************************************** + * Name: m_string_duplicate + * + * Description: + * Safe version of strdup() that checks the string was successfully + * allocated. + * + ****************************************************************************/ + +char *m_string_duplicate(const char *orig) +{ + char *result; + + result = strdup(orig); + + if (result == NULL) + { + i_error("Failed to duplicate string (length %zu)\n", strlen(orig)); + } + + return result; +} + +char *m_string_replace(const char *haystack, const char *needle, + const char *replacement) +{ + char *result; + char *dst; + const char *p; + size_t needle_len = strlen(needle); + size_t result_len; + size_t dst_len; + + /* Iterate through occurrences of 'needle' and calculate the size of + * the new string. + */ + + result_len = strlen(haystack) + 1; + p = haystack; + + for (; ; ) + { + p = strstr(p, needle); + if (p == NULL) + { + break; + } + + p += needle_len; + result_len += strlen(replacement) - needle_len; + } + + /* Construct new string. */ + + result = malloc(result_len); + if (result == NULL) + { + i_error("m_string_replace: Failed to allocate new string"); + return NULL; + } + + dst = result; + dst_len = result_len; + p = haystack; + + while (*p != '\0') + { + if (!strncmp(p, needle, needle_len)) + { + m_str_copy(dst, replacement, dst_len); + p += needle_len; + dst += strlen(replacement); + dst_len -= strlen(replacement); + } + else + { + *dst = *p; + ++dst; + --dst_len; + ++p; + } + } + + *dst = '\0'; + + return result; +} + +/* Safe string copy function that works like OpenBSD's strlcpy(). + * Returns true if the string was not truncated. + */ + +boolean m_str_copy(char *dest, const char *src, size_t dest_size) +{ + size_t len; + + if (dest_size >= 1) + { + dest[dest_size - 1] = '\0'; + strncpy(dest, src, dest_size - 1); + } + else + { + return false; + } + + len = strlen(dest); + return src[len] == '\0'; +} + +/* Safe string concat function that works like OpenBSD's strlcat(). + * Returns true if string not truncated. + */ + +boolean m_string_concat(char *dest, const char *src, size_t dest_size) +{ + size_t offset; + + offset = strlen(dest); + if (offset > dest_size) + { + offset = dest_size; + } + + return m_str_copy(dest + offset, src, dest_size - offset); +} + +/* Returns true if 's' begins with the specified prefix. */ + +boolean m_string_starts_with(const char *s, const char *prefix) +{ + return strlen(s) >= strlen(prefix) && + strncmp(s, prefix, strlen(prefix)) == 0; +} + +/* Returns true if 's' ends with the specified suffix. */ + +boolean m_string_ends_with(const char *s, const char *suffix) +{ + return strlen(s) >= strlen(suffix) && + strcmp(s + strlen(s) - strlen(suffix), suffix) == 0; +} + +/* Return a newly-malloced string with all the strings given as arguments + * concatenated together. + */ + +char *m_string_join(const char *s, ...) +{ + char *result; + const char *v; + va_list args; + size_t result_len; + + result_len = strlen(s) + 1; + + va_start(args, s); + + for (; ; ) + { + v = va_arg(args, const char *); + if (v == NULL) + { + break; + } + + result_len += strlen(v); + } + + va_end(args); + + result = malloc(result_len); + + if (result == NULL) + { + i_error("m_string_join: Failed to allocate new string."); + return NULL; + } + + m_str_copy(result, s, result_len); + + va_start(args, s); + + for (; ; ) + { + v = va_arg(args, const char *); + if (v == NULL) + { + break; + } + + m_string_concat(result, v, result_len); + } + + va_end(args); + + return result; +} + +/**************************************************************************** + * Name: m_normalize_slashes + * + * Description: + * Remove trailing slashes, translate backslashes to slashes. The string to + * normalize is passed and returned in str. + * + * killough 11/98: rewritten + * + * [STRIFE] - haleyjd 20110210: Borrowed from Eternity and adapted to + * respect the DIR_SEPARATOR define used by Choco Doom. This routine + * originated in BOOM. + * + ****************************************************************************/ + +void m_normalize_slashes(char *str) +{ + char *p; + + /* Convert all slashes/backslashes to DIR_SEPARATOR */ + + for (p = str; *p; p++) + { + if ((*p == '/' || *p == '\\') && *p != DIR_SEPARATOR) + { + *p = DIR_SEPARATOR; + } + } + + /* Remove trailing slashes */ + + while (p > str && *--p == DIR_SEPARATOR) + { + *p = 0; + } + + /* Collapse multiple slashes */ + + for (p = str; (*str++ = *p); ) + { + if (*p++ == DIR_SEPARATOR) + { + while (*p == DIR_SEPARATOR) + { + p++; + } + } + } +} diff --git a/games/NXDoom/src/m_misc.h b/games/NXDoom/src/m_misc.h new file mode 100644 index 00000000000..0b310371e35 --- /dev/null +++ b/games/NXDoom/src/m_misc.h @@ -0,0 +1,111 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_misc.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Miscellaneous. + * + ****************************************************************************/ + +#ifndef __M_MISC__ +#define __M_MISC__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* debugging code to check there are no loops in a linked list + * disabled unless explicitly requested + */ + +#ifdef DEBUG_LINKED_LISTS + +#define LINKED_LIST_CHECK_NO_CYCLE(list_type, list, next_member) \ + do \ + { \ + if (list != NULL) \ + { \ + list_type *slow, *fast; \ + slow = list; \ + fast = list->next_member; \ + while (fast) \ + { \ + if (!fast->next_member) \ + { \ + break; \ + } \ + fast = fast->next_member->next_member; \ + slow = slow->next_member; \ + if (slow == fast) \ + { \ + fprintf(stderr, "loop in linked list " #list " in %s:%d", \ + __FILE__, __LINE__); \ + __builtin_trap(); \ + } \ + } \ + } \ + } \ + while (0) + +#else /* DEBUG_LINKED_LISTS */ + +#define LINKED_LIST_CHECK_NO_CYCLE(list_type, list, next_member) \ + do \ + { \ + } \ + while (0) + +#endif /* DEBUG_LINKED_LISTS */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean m_write_file(const char *name, const void *source, int length); +int m_read_file(const char *name, byte **buffer); +void m_make_directory(const char *dir); +char *m_temp_file(const char *s); +boolean m_file_exists(const char *file); +char *m_file_case_exists(const char *file); +long m_file_length(FILE *handle); +boolean m_str_to_int(const char *str, int *result); +char *m_dir_name(const char *path); +const char *m_base_name(const char *path); +void m_extract_file_base(const char *path, char *dest); +void m_force_uppercase(char *text); +void m_force_lowercase(char *text); +char *m_string_duplicate(const char *orig); +boolean m_str_copy(char *dest, const char *src, size_t dest_size); +boolean m_string_concat(char *dest, const char *src, size_t dest_size); +char *m_string_replace(const char *haystack, const char *needle, + const char *replacement); +char *m_string_join(const char *s, ...); +boolean m_string_starts_with(const char *s, const char *prefix); +boolean m_string_ends_with(const char *s, const char *suffix); +void m_normalize_slashes(char *str); + +#endif /* __M_MISC__ */ diff --git a/games/NXDoom/src/manifest.xml b/games/NXDoom/src/manifest.xml new file mode 100644 index 00000000000..05a19e7ad92 --- /dev/null +++ b/games/NXDoom/src/manifest.xml @@ -0,0 +1,12 @@ + + + + + + + true + + + + diff --git a/games/NXDoom/src/memio.c b/games/NXDoom/src/memio.c new file mode 100644 index 00000000000..aeb8ac8a8ce --- /dev/null +++ b/games/NXDoom/src/memio.c @@ -0,0 +1,215 @@ +/**************************************************************************** + * apps/games/NXDoom/src/memio.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Emulates the IO functions in C stdio.h reading and writing to + * memory. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "memio.h" + +#include "z_zone.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + MODE_READ, + MODE_WRITE, +} memfile_mode_t; + +struct _MEMFILE +{ + unsigned char *buf; + size_t buflen; + size_t alloced; + unsigned int position; + memfile_mode_t mode; +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Open a memory area for reading */ + +MEMFILE *mem_fopen_read(void *buf, size_t buflen) +{ + MEMFILE *file; + + file = z_malloc(sizeof(MEMFILE), PU_STATIC, 0); + + file->buf = (unsigned char *)buf; + file->buflen = buflen; + file->position = 0; + file->mode = MODE_READ; + + return file; +} + +/* Read bytes */ + +size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream) +{ + size_t items; + + if (stream->mode != MODE_READ) + { + printf("not a read stream\n"); + return -1; + } + + /* Trying to read more bytes than we have left? */ + + items = nmemb; + + if (items * size > stream->buflen - stream->position) + { + items = (stream->buflen - stream->position) / size; + } + + /* Copy bytes to buffer */ + + memcpy(buf, stream->buf + stream->position, items * size); + + /* Update position */ + + stream->position += items * size; + + return items; +} + +/* Open a memory area for writing */ + +MEMFILE *mem_fopen_write(void) +{ + MEMFILE *file; + + file = z_malloc(sizeof(MEMFILE), PU_STATIC, 0); + + file->alloced = 1024; + file->buf = z_malloc(file->alloced, PU_STATIC, 0); + file->buflen = 0; + file->position = 0; + file->mode = MODE_WRITE; + + return file; +} + +/* Write bytes to stream */ + +size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, + MEMFILE *stream) +{ + size_t bytes; + + if (stream->mode != MODE_WRITE) + { + return -1; + } + + /* More bytes than can fit in the buffer? + * If so, reallocate bigger. + */ + + bytes = size * nmemb; + + while (bytes > stream->alloced - stream->position) + { + unsigned char *newbuf; + + newbuf = z_malloc(stream->alloced * 2, PU_STATIC, 0); + memcpy(newbuf, stream->buf, stream->alloced); + z_free(stream->buf); + stream->buf = newbuf; + stream->alloced *= 2; + } + + /* Copy into buffer */ + + memcpy(stream->buf + stream->position, ptr, bytes); + stream->position += bytes; + + if (stream->position > stream->buflen) stream->buflen = stream->position; + + return nmemb; +} + +void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen) +{ + *buf = stream->buf; + *buflen = stream->buflen; +} + +void mem_fclose(MEMFILE *stream) +{ + if (stream->mode == MODE_WRITE) + { + z_free(stream->buf); + } + + z_free(stream); +} + +long mem_ftell(MEMFILE *stream) +{ + return stream->position; +} + +int mem_fseek(MEMFILE *stream, signed long position, mem_rel_t whence) +{ + unsigned int newpos; + + switch (whence) + { + case MEM_SEEK_SET: + newpos = (int)position; + break; + + case MEM_SEEK_CUR: + newpos = (int)(stream->position + position); + break; + + case MEM_SEEK_END: + newpos = (int)(stream->buflen + position); + break; + default: + return -1; + } + + if (newpos < stream->buflen) + { + stream->position = newpos; + return 0; + } + else + { + printf("Error seeking to %u\n", newpos); + return -1; + } +} diff --git a/games/NXDoom/src/memio.h b/games/NXDoom/src/memio.h new file mode 100644 index 00000000000..275fbc753b5 --- /dev/null +++ b/games/NXDoom/src/memio.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * apps/games/NXDoom/src/memio.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef MEMIO_H +#define MEMIO_H + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct _MEMFILE MEMFILE; + +typedef enum +{ + MEM_SEEK_SET, + MEM_SEEK_CUR, + MEM_SEEK_END, +} mem_rel_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +MEMFILE *mem_fopen_read(void *buf, size_t buflen); +size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream); +MEMFILE *mem_fopen_write(void); +size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, + MEMFILE *stream); +void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen); +void mem_fclose(MEMFILE *stream); +long mem_ftell(MEMFILE *stream); +int mem_fseek(MEMFILE *stream, signed long offset, mem_rel_t whence); + +#endif /* MEMIO_H */ diff --git a/games/NXDoom/src/midifile.c b/games/NXDoom/src/midifile.c new file mode 100644 index 00000000000..8374ac50dd6 --- /dev/null +++ b/games/NXDoom/src/midifile.c @@ -0,0 +1,878 @@ +/**************************************************************************** + * apps/games/NXDoom/src/midifile.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Reading of MIDI files. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomtype.h" +#include "i_swap.h" +#include "i_system.h" +#include "m_misc.h" +#include "midifile.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HEADER_CHUNK_ID "MThd" +#define TRACK_CHUNK_ID "MTrk" +#define MAX_BUFFER_SIZE 0x10000 + +/* haleyjd 09/09/10: packing required */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +begin_packed_struct struct chunk_header_t +{ + byte chunk_id[4]; + unsigned int chunk_size; +} end_packed_struct; + +typedef struct chunk_header_t chunk_header_t; + +begin_packed_struct struct midi_header_t +{ + chunk_header_t chunk_header; + unsigned short format_type; + unsigned short num_tracks; + unsigned short time_division; +} begin_packed_struct; + +typedef struct midi_header_t midi_header_t; + +/* haleyjd 09/09/10: packing off. */ + +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +typedef struct +{ + /* Length in bytes: */ + + unsigned int data_len; + + /* Events in this track: */ + + midi_event_t *events; + int num_events; +} midi_track_t; + +struct midi_track_iter_s +{ + midi_track_t *track; + unsigned int position; + unsigned int loop_point; +}; + +struct midi_file_s +{ + midi_header_t header; + + /* All tracks in this file: */ + + midi_track_t *tracks; + unsigned int num_tracks; + + /* Data buffer used to store data read for SysEx or meta events: */ + + byte *buffer; + unsigned int buffer_size; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Check the header of a chunk: */ + +static boolean check_chunk_header(chunk_header_t *chunk, + const char *expected_id) +{ + boolean result; + + result = (memcmp((char *)chunk->chunk_id, expected_id, 4) == 0); + + if (!result) + { + fprintf(stderr, + "check_chunk_header: Expected '%s' chunk header, " + "got '%c%c%c%c'\n", + expected_id, chunk->chunk_id[0], chunk->chunk_id[1], + chunk->chunk_id[2], chunk->chunk_id[3]); + } + + return result; +} + +/* Read a single byte. Returns false on error. */ + +static boolean read_byte(byte *result, FILE *stream) +{ + int c; + + c = fgetc(stream); + + if (c == EOF) + { + fprintf(stderr, "read_byte: Unexpected end of file\n"); + return false; + } + else + { + *result = (byte)c; + + return true; + } +} + +/* Read a variable-length value. */ + +static boolean read_variable_length(unsigned int *result, FILE *stream) +{ + int i; + byte b = 0; + + *result = 0; + + for (i = 0; i < 4; ++i) + { + if (!read_byte(&b, stream)) + { + fprintf(stderr, "read_variable_length: Error while reading " + "variable-length value\n"); + return false; + } + + /* Insert the bottom seven bits from this byte. */ + + *result <<= 7; + *result |= b & 0x7f; + + /* If the top bit is not set, this is the end. */ + + if ((b & 0x80) == 0) + { + return true; + } + } + + fprintf(stderr, "read_variable_length: Variable-length value too " + "long: maximum of four bytes\n"); + return false; +} + +/* Read a byte sequence into the data buffer. */ + +static void *read_byte_sequence(unsigned int num_bytes, FILE *stream) +{ + unsigned int i; + byte *result; + + /* Allocate a buffer. Allocate one extra byte, as malloc(0) is + * non-portable. + */ + + result = malloc(num_bytes + 1); + + if (result == NULL) + { + fprintf(stderr, "read_byte_sequence: Failed to allocate buffer\n"); + return NULL; + } + + /* Read the data: */ + + for (i = 0; i < num_bytes; ++i) + { + if (!read_byte(&result[i], stream)) + { + fprintf(stderr, + "read_byte_sequence: Error while reading byte %u\n", i); + free(result); + return NULL; + } + } + + return result; +} + +/* Read a MIDI channel event. + * two_param indicates that the event type takes two parameters + * (three byte) otherwise it is single parameter (two byte) + */ + +static boolean read_channel_event(midi_event_t *event, byte event_type, + boolean two_param, FILE *stream) +{ + byte b = 0; + + /* Set basics: */ + + event->event_type = event_type & 0xf0; + event->data.channel.channel = event_type & 0x0f; + + /* Read parameters: */ + + if (!read_byte(&b, stream)) + { + fprintf(stderr, "read_channel_event: Error while reading channel " + "event parameters\n"); + return false; + } + + event->data.channel.param1 = b; + + /* Second parameter: */ + + if (two_param) + { + if (!read_byte(&b, stream)) + { + fprintf(stderr, "read_channel_event: Error while reading channel " + "event parameters\n"); + return false; + } + + event->data.channel.param2 = b; + } + + return true; +} + +/* Read sysex event: */ + +static boolean read_sys_ex_event(midi_event_t *event, int event_type, + FILE *stream) +{ + event->event_type = event_type; + + if (!read_variable_length(&event->data.sysex.length, stream)) + { + fprintf(stderr, "read_sys_ex_event: Failed to read length of " + "SysEx block\n"); + return false; + } + + /* Read the byte sequence: */ + + event->data.sysex.data = + read_byte_sequence(event->data.sysex.length, stream); + + if (event->data.sysex.data == NULL) + { + fprintf(stderr, + "read_sys_ex_event: Failed while reading SysEx event\n"); + return false; + } + + return true; +} + +/* Read meta event: */ + +static boolean read_meta_event(midi_event_t *event, FILE *stream) +{ + byte b = 0; + + event->event_type = MIDI_EVENT_META; + + /* Read meta event type: */ + + if (!read_byte(&b, stream)) + { + fprintf(stderr, "read_meta_event: Failed to read meta event type\n"); + return false; + } + + event->data.meta.type = b; + + /* Read length of meta event data: */ + + if (!read_variable_length(&event->data.meta.length, stream)) + { + fprintf(stderr, "read_sys_ex_event: Failed to read length of " + "SysEx block\n"); + return false; + } + + /* Read the byte sequence: */ + + event->data.meta.data = + read_byte_sequence(event->data.meta.length, stream); + + if (event->data.meta.data == NULL) + { + fprintf(stderr, + "read_sys_ex_event: Failed while reading SysEx event\n"); + return false; + } + + return true; +} + +static boolean read_event(midi_event_t *event, unsigned int *last_event_type, + FILE *stream) +{ + byte event_type = 0; + + if (!read_variable_length(&event->delta_time, stream)) + { + fprintf(stderr, "read_event: Failed to read event timestamp\n"); + return false; + } + + if (!read_byte(&event_type, stream)) + { + fprintf(stderr, "read_event: Failed to read event type\n"); + return false; + } + + /* All event types have their top bit set. Therefore, if + * the top bit is not set, it is because we are using the "same + * as previous event type" shortcut to save a byte. Skip back + * a byte so that we read this byte again. + */ + + if ((event_type & 0x80) == 0) + { + event_type = *last_event_type; + + if (fseek(stream, -1, SEEK_CUR) < 0) + { + fprintf(stderr, "read_event: Unable to seek in stream\n"); + return false; + } + } + else + { + *last_event_type = event_type; + } + + /* Check event type: */ + + switch (event_type & 0xf0) + { + /* Two parameter channel events: */ + + case MIDI_EVENT_NOTE_OFF: + case MIDI_EVENT_NOTE_ON: + case MIDI_EVENT_AFTERTOUCH: + case MIDI_EVENT_CONTROLLER: + case MIDI_EVENT_PITCH_BEND: + return read_channel_event(event, event_type, true, stream); + + /* Single parameter channel events: */ + + case MIDI_EVENT_PROGRAM_CHANGE: + case MIDI_EVENT_CHAN_AFTERTOUCH: + return read_channel_event(event, event_type, false, stream); + + default: + break; + } + + /* Specific value? */ + + switch (event_type) + { + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + return read_sys_ex_event(event, event_type, stream); + + case MIDI_EVENT_META: + return read_meta_event(event, stream); + + default: + break; + } + + fprintf(stderr, "read_event: Unknown MIDI event type: 0x%x\n", event_type); + return false; +} + +/* Free an event: */ + +static void free_event(midi_event_t *event) +{ + /* Some event types have dynamically allocated buffers assigned + * to them that must be freed. + */ + + switch (event->event_type) + { + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + free(event->data.sysex.data); + break; + + case MIDI_EVENT_META: + free(event->data.meta.data); + break; + + default: + break; /* Nothing to do. */ + } +} + +/* Read and check the track chunk header */ + +static boolean read_track_header(midi_track_t *track, FILE *stream) +{ + size_t records_read; + chunk_header_t chunk_header; + + records_read = fread(&chunk_header, sizeof(chunk_header_t), 1, stream); + + if (records_read < 1) + { + return false; + } + + if (!check_chunk_header(&chunk_header, TRACK_CHUNK_ID)) + { + return false; + } + + track->data_len = be32toh(chunk_header.chunk_size); + + return true; +} + +static boolean read_track(midi_track_t *track, FILE *stream) +{ + midi_event_t *new_events; + midi_event_t *event; + unsigned int last_event_type; + + track->num_events = 0; + track->events = NULL; + + /* Read the header: */ + + if (!read_track_header(track, stream)) + { + return false; + } + + /* Then the events: */ + + last_event_type = 0; + + for (; ; ) + { + /* Resize the track slightly larger to hold another event: */ + + new_events = i_realloc(track->events, + sizeof(midi_event_t) * (track->num_events + 1)); + track->events = new_events; + + /* Read the next event: */ + + event = &track->events[track->num_events]; + if (!read_event(event, &last_event_type, stream)) + { + return false; + } + + ++track->num_events; + + /* End of track? */ + + if (event->event_type == MIDI_EVENT_META && + event->data.meta.type == MIDI_META_END_OF_TRACK) + { + break; + } + } + + return true; +} + +/* Free a track: */ + +static void free_track(midi_track_t *track) +{ + unsigned int i; + + for (i = 0; i < track->num_events; ++i) + { + free_event(&track->events[i]); + } + + free(track->events); +} + +static boolean read_all_tracks(midi_file_t *file, FILE *stream) +{ + unsigned int i; + + /* Allocate list of tracks and read each track: */ + + file->tracks = malloc(sizeof(midi_track_t) * file->num_tracks); + + if (file->tracks == NULL) + { + return false; + } + + memset(file->tracks, 0, sizeof(midi_track_t) * file->num_tracks); + + /* Read each track: */ + + for (i = 0; i < file->num_tracks; ++i) + { + if (!read_track(&file->tracks[i], stream)) + { + return false; + } + } + + return true; +} + +/* Read and check the header chunk. */ + +static boolean read_file_header(midi_file_t *file, FILE *stream) +{ + size_t records_read; + unsigned int format_type; + + records_read = fread(&file->header, sizeof(midi_header_t), 1, stream); + + if (records_read < 1) + { + return false; + } + + if (!check_chunk_header(&file->header.chunk_header, HEADER_CHUNK_ID) || + be32toh(file->header.chunk_header.chunk_size) != 6) + { + fprintf(stderr, + "read_file_header: Invalid MIDI chunk header! " + "chunk_size=%i\n", + be32toh(file->header.chunk_header.chunk_size)); + return false; + } + + format_type = be32toh(file->header.format_type); + file->num_tracks = be32toh(file->header.num_tracks); + + if ((format_type != 0 && format_type != 1) || file->num_tracks < 1) + { + fprintf(stderr, "read_file_header: Only type 0/1 " + "MIDI files supported!\n"); + return false; + } + + return true; +} + +void midi_free_file(midi_file_t *file) +{ + int i; + + if (file->tracks != NULL) + { + for (i = 0; i < file->num_tracks; ++i) + { + free_track(&file->tracks[i]); + } + + free(file->tracks); + } + + free(file); +} + +midi_file_t *midi_loadfile(char *filename) +{ + midi_file_t *file; + FILE *stream; + + file = malloc(sizeof(midi_file_t)); + + if (file == NULL) + { + return NULL; + } + + file->tracks = NULL; + file->num_tracks = 0; + file->buffer = NULL; + file->buffer_size = 0; + + /* Open file */ + + stream = fopen(filename, "rb"); + + if (stream == NULL) + { + fprintf(stderr, "midi_loadfile: Failed to open '%s'\n", filename); + midi_free_file(file); + return NULL; + } + + /* Read MIDI file header */ + + if (!read_file_header(file, stream)) + { + fclose(stream); + midi_free_file(file); + return NULL; + } + + /* Read all tracks: */ + + if (!read_all_tracks(file, stream)) + { + fclose(stream); + midi_free_file(file); + return NULL; + } + + fclose(stream); + + return file; +} + +#ifdef TEST +static char *midi_event_type_to_string(midi_event_type_t event_type) +{ + switch (event_type) + { + case MIDI_EVENT_NOTE_OFF: + return "MIDI_EVENT_NOTE_OFF"; + case MIDI_EVENT_NOTE_ON: + return "MIDI_EVENT_NOTE_ON"; + case MIDI_EVENT_AFTERTOUCH: + return "MIDI_EVENT_AFTERTOUCH"; + case MIDI_EVENT_CONTROLLER: + return "MIDI_EVENT_CONTROLLER"; + case MIDI_EVENT_PROGRAM_CHANGE: + return "MIDI_EVENT_PROGRAM_CHANGE"; + case MIDI_EVENT_CHAN_AFTERTOUCH: + return "MIDI_EVENT_CHAN_AFTERTOUCH"; + case MIDI_EVENT_PITCH_BEND: + return "MIDI_EVENT_PITCH_BEND"; + case MIDI_EVENT_SYSEX: + return "MIDI_EVENT_SYSEX"; + case MIDI_EVENT_SYSEX_SPLIT: + return "MIDI_EVENT_SYSEX_SPLIT"; + case MIDI_EVENT_META: + return "MIDI_EVENT_META"; + + default: + return "(unknown)"; + } +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Get the number of tracks in a MIDI file. */ + +unsigned int midi_num_tracks(midi_file_t *file) +{ + return file->num_tracks; +} + +/* Start iterating over the events in a track. */ + +midi_track_iter_t *midi_iterate_track(midi_file_t *file, unsigned int track) +{ + midi_track_iter_t *iter; + + assert(track < file->num_tracks); + + iter = malloc(sizeof(*iter)); + iter->track = &file->tracks[track]; + iter->position = 0; + iter->loop_point = 0; + + return iter; +} + +void midi_free_iterator(midi_track_iter_t *iter) +{ + free(iter); +} + +/* Get the time until the next MIDI event in a track. */ + +unsigned int midi_get_delta_time(midi_track_iter_t *iter) +{ + if (iter->position < iter->track->num_events) + { + midi_event_t *next_event; + + next_event = &iter->track->events[iter->position]; + + return next_event->delta_time; + } + else + { + return 0; + } +} + +/* Get a pointer to the next MIDI event. */ + +int midi_get_next_event(midi_track_iter_t *iter, midi_event_t **event) +{ + if (iter->position < iter->track->num_events) + { + *event = &iter->track->events[iter->position]; + ++iter->position; + + return 1; + } + else + { + return 0; + } +} + +unsigned int midi_get_file_time_division(midi_file_t *file) +{ + short result = be16toh(file->header.time_division); + + /* Negative time division indicates SMPTE time and must be handled + * differently. + */ + + if (result < 0) + { + return (signed int)(-(result / 256)) * + (signed int)(result & 0xff); + } + else + { + return result; + } +} + +void midi_restart_iterator(midi_track_iter_t *iter) +{ + iter->position = 0; + iter->loop_point = 0; +} + +void midi_set_loop_point(midi_track_iter_t *iter) +{ + iter->loop_point = iter->position; +} + +void midi_restart_at_loop_point(midi_track_iter_t *iter) +{ + iter->position = iter->loop_point; +} + +#ifdef TEST +void print_track(midi_track_t *track) +{ + midi_event_t *event; + unsigned int i; + + for (i = 0; i < track->num_events; ++i) + { + event = &track->events[i]; + + if (event->delta_time > 0) + { + printf("Delay: %u ticks\n", event->delta_time); + } + + printf("Event type: %s (%i)\n", + midi_event_type_to_string(event->event_type), + event->event_type); + + switch (event->event_type) + { + case MIDI_EVENT_NOTE_OFF: + case MIDI_EVENT_NOTE_ON: + case MIDI_EVENT_AFTERTOUCH: + case MIDI_EVENT_CONTROLLER: + case MIDI_EVENT_PROGRAM_CHANGE: + case MIDI_EVENT_CHAN_AFTERTOUCH: + case MIDI_EVENT_PITCH_BEND: + printf("\tChannel: %u\n", event->data.channel.channel); + printf("\tParameter 1: %u\n", event->data.channel.param1); + printf("\tParameter 2: %u\n", event->data.channel.param2); + break; + + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + printf("\tLength: %u\n", event->data.sysex.length); + break; + + case MIDI_EVENT_META: + printf("\tMeta type: %u\n", event->data.meta.type); + printf("\tLength: %u\n", event->data.meta.length); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + midi_file_t *file; + unsigned int i; + + if (argc < 2) + { + printf("Usage: %s \n", argv[0]); + exit(1); + } + + file = midi_loadfile(argv[1]); + + if (file == NULL) + { + fprintf(stderr, "Failed to open %s\n", argv[1]); + exit(1); + } + + for (i = 0; i < file->num_tracks; ++i) + { + printf("\n== Track %u ==\n\n", i); + + print_track(&file->tracks[i]); + } + + return 0; +} +#endif diff --git a/games/NXDoom/src/midifile.h b/games/NXDoom/src/midifile.h new file mode 100644 index 00000000000..e6dc9166e51 --- /dev/null +++ b/games/NXDoom/src/midifile.h @@ -0,0 +1,251 @@ +/**************************************************************************** + * apps/games/NXDoom/src/midifile.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * MIDI file parsing. + * + ****************************************************************************/ + +#ifndef MIDIFILE_H +#define MIDIFILE_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MIDI_CHANNELS_PER_TRACK 16 + +#define MIDI_RPN_MSB 0x00 +#define MIDI_RPN_PITCH_BEND_SENS_LSB 0x00 +#define MIDI_RPN_FINE_TUNING_LSB 0x01 +#define MIDI_RPN_COARSE_TUNING_LSB 0x02 +#define MIDI_RPN_NULL 0x7f + +#define EMIDI_LOOP_FLAG 0x7f + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct midi_file_s midi_file_t; +typedef struct midi_track_iter_s midi_track_iter_t; + +typedef enum +{ + MIDI_EVENT_NOTE_OFF = 0x80, + MIDI_EVENT_NOTE_ON = 0x90, + MIDI_EVENT_AFTERTOUCH = 0xa0, + MIDI_EVENT_CONTROLLER = 0xb0, + MIDI_EVENT_PROGRAM_CHANGE = 0xc0, + MIDI_EVENT_CHAN_AFTERTOUCH = 0xd0, + MIDI_EVENT_PITCH_BEND = 0xe0, + + MIDI_EVENT_SYSEX = 0xf0, + MIDI_EVENT_SYSEX_SPLIT = 0xf7, + MIDI_EVENT_META = 0xff, +} midi_event_type_t; + +typedef enum +{ + MIDI_CONTROLLER_BANK_SELECT_MSB = 0x00, + MIDI_CONTROLLER_MODULATION = 0x01, + MIDI_CONTROLLER_BREATH_CONTROL = 0x02, + MIDI_CONTROLLER_FOOT_CONTROL = 0x04, + MIDI_CONTROLLER_PORTAMENTO = 0x05, + MIDI_CONTROLLER_DATA_ENTRY_MSB = 0x06, + MIDI_CONTROLLER_VOLUME_MSB = 0x07, + MIDI_CONTROLLER_PAN = 0x0a, + MIDI_CONTROLLER_EXPRESSION = 0x0b, + + MIDI_CONTROLLER_BANK_SELECT_LSB = 0x20, + MIDI_CONTROLLER_DATA_ENTRY_LSB = 0x26, + MIDI_CONTROLLER_VOLUME_LSB = 0X27, + + MIDI_CONTROLLER_HOLD1_PEDAL = 0x40, + MIDI_CONTROLLER_SOFT_PEDAL = 0x43, + + MIDI_CONTROLLER_REVERB = 0x5b, + MIDI_CONTROLLER_CHORUS = 0x5d, + + MIDI_CONTROLLER_NRPN_LSB = 0x62, + MIDI_CONTROLLER_NRPN_MSB = 0x63, + MIDI_CONTROLLER_RPN_LSB = 0x64, + MIDI_CONTROLLER_RPN_MSB = 0x65, + + MIDI_CONTROLLER_ALL_SOUND_OFF = 0x78, + MIDI_CONTROLLER_RESET_ALL_CTRLS = 0x79, + MIDI_CONTROLLER_ALL_NOTES_OFF = 0x7b, + + MIDI_CONTROLLER_POLY_MODE_OFF = 0x7e, + MIDI_CONTROLLER_POLY_MODE_ON = 0x7f, +} midi_controller_t; + +typedef enum +{ + MIDI_META_SEQUENCE_NUMBER = 0x00, + + MIDI_META_TEXT = 0x01, + MIDI_META_COPYRIGHT = 0x02, + MIDI_META_TRACK_NAME = 0x03, + MIDI_META_INSTR_NAME = 0x04, + MIDI_META_LYRICS = 0x05, + MIDI_META_MARKER = 0x06, + MIDI_META_CUE_POINT = 0x07, + + MIDI_META_CHANNEL_PREFIX = 0x20, + MIDI_META_END_OF_TRACK = 0x2f, + + MIDI_META_SET_TEMPO = 0x51, + MIDI_META_SMPTE_OFFSET = 0x54, + MIDI_META_TIME_SIGNATURE = 0x58, + MIDI_META_KEY_SIGNATURE = 0x59, + MIDI_META_SEQUENCER_SPECIFIC = 0x7f, +} midi_meta_event_type_t; + +typedef enum +{ + EMIDI_DEVICE_GENERAL_MIDI = 0x00, + EMIDI_DEVICE_SOUND_CANVAS = 0x01, + EMIDI_DEVICE_AWE32 = 0x02, + EMIDI_DEVICE_WAVE_BLASTER = 0x03, + EMIDI_DEVICE_SOUND_BLASTER = 0x04, + EMIDI_DEVICE_PRO_AUDIO = 0x05, + EMIDI_DEVICE_SOUND_MAN_16 = 0x06, + EMIDI_DEVICE_ADLIB = 0x07, + EMIDI_DEVICE_SOUNDSCAPE = 0x08, + EMIDI_DEVICE_ULTRASOUND = 0x09, + EMIDI_DEVICE_ALL = 0x7f, +} emidi_device_t; + +typedef enum +{ + EMIDI_CONTROLLER_TRACK_DESIGNATION = 0x6e, + EMIDI_CONTROLLER_TRACK_EXCLUSION = 0x6f, + EMIDI_CONTROLLER_PROGRAM_CHANGE = 0x70, + EMIDI_CONTROLLER_VOLUME = 0x71, + EMIDI_CONTROLLER_LOOP_BEGIN = 0x74, + EMIDI_CONTROLLER_LOOP_END = 0x75, + EMIDI_CONTROLLER_GLOBAL_LOOP_BEGIN = 0x76, + EMIDI_CONTROLLER_GLOBAL_LOOP_END = 0x77, +} emidi_controller_t; + +typedef struct +{ + /* Meta event type: */ + + unsigned int type; + + /* Length: */ + + unsigned int length; + + /* Meta event data: */ + + byte *data; +} midi_meta_event_data_t; + +typedef struct +{ + /* Length: */ + + unsigned int length; + + /* Event data: */ + + byte *data; +} midi_sysex_event_data_t; + +typedef struct +{ + /* The channel number to which this applies: */ + + unsigned int channel; + + /* Extra parameters: */ + + unsigned int param1; + unsigned int param2; +} midi_channel_event_data_t; + +typedef struct +{ + /* Time between the previous event and this event. */ + + unsigned int delta_time; + + /* Type of event: */ + + midi_event_type_t event_type; + + union + { + midi_channel_event_data_t channel; + midi_meta_event_data_t meta; + midi_sysex_event_data_t sysex; + } data; +} midi_event_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Load a MIDI file. */ + +midi_file_t *midi_loadfile(char *filename); + +/* Free a MIDI file. */ + +void midi_free_file(midi_file_t *file); + +/* Get the time division value from the MIDI header. */ + +unsigned int midi_get_file_time_division(midi_file_t *file); + +/* Get the number of tracks in a MIDI file. */ + +unsigned int midi_num_tracks(midi_file_t *file); + +/* Start iterating over the events in a track. */ + +midi_track_iter_t *midi_iterate_track(midi_file_t *file, + unsigned int track_num); + +/* Free an iterator. */ + +void midi_free_iterator(midi_track_iter_t *iter); + +/* Get the time until the next MIDI event in a track. */ + +unsigned int midi_get_delta_time(midi_track_iter_t *iter); + +/* Get a pointer to the next MIDI event. */ + +int midi_get_next_event(midi_track_iter_t *iter, midi_event_t **event); + +/* Reset an iterator to the beginning of a track. */ + +void midi_restart_iterator(midi_track_iter_t *iter); + +/* Set loop point to current position. */ + +void midi_set_loop_point(midi_track_iter_t *iter); + +/* Set position to saved loop point. */ + +void midi_restart_at_loop_point(midi_track_iter_t *iter); + +#endif /* MIDIFILE_H */ diff --git a/games/NXDoom/src/mus2mid.c b/games/NXDoom/src/mus2mid.c new file mode 100644 index 00000000000..502e1025262 --- /dev/null +++ b/games/NXDoom/src/mus2mid.c @@ -0,0 +1,794 @@ +/**************************************************************************** + * apps/games/NXDoom/src/mus2mid.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2006 Ben Ryves 2006 + * + * 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. + * + * mus2mid.c - Ben Ryves 2006 - http: *benryves.com - benryves@benryves.com + * Use to convert a MUS file into a single track, type 0 MIDI file. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" +#include "i_swap.h" + +#include "memio.h" +#include "mus2mid.h" + +#ifdef STANDALONE +#include "m_misc.h" +#include "z_zone.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NUM_CHANNELS 16 + +#define MIDI_PERCUSSION_CHAN 9 +#define MUS_PERCUSSION_CHAN 15 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* MUS event codes */ + +typedef enum +{ + mus_releasekey = 0x00, + mus_presskey = 0x10, + mus_pitchwheel = 0x20, + mus_systemevent = 0x30, + mus_changecontroller = 0x40, + mus_scoreend = 0x60 +} musevent; + +/* MIDI event codes */ + +typedef enum +{ + midi_releasekey = 0x80, + midi_presskey = 0x90, + midi_aftertouchkey = 0xa0, + midi_changecontroller = 0xb0, + midi_changepatch = 0xc0, + midi_aftertouchchannel = 0xd0, + midi_pitchwheel = 0xe0 +} midievent; + +/* Structure to hold MUS file header */ + +begin_packed_struct struct musheader +{ + byte id[4]; + unsigned short scorelength; + unsigned short scorestart; + unsigned short primarychannels; + unsigned short secondarychannels; + unsigned short instrumentcount; +} end_packed_struct; + +typedef struct musheader musheader; + +/* Standard MIDI type 0 header + track header */ + +static const byte midiheader[] = +{ + 'M', 'T', 'h', 'd', /* Main header */ + 0x00, 0x00, 0x00, 0x06, /* Header size */ + 0x00, 0x00, /* MIDI type (0) */ + 0x00, 0x01, /* Number of tracks */ + 0x00, 0x46, /* Resolution */ + 'M', 'T', 'r', 'k', /* Start of track */ + 0x00, 0x00, 0x00, 0x00 /* Placeholder for track length */ +}; + +/* Cached channel velocities */ + +static byte channelvelocities[] = +{ + 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, +}; + +/* Timestamps between sequences of MUS events */ + +static unsigned int queuedtime = 0; + +/* Counter for the length of the track */ + +static unsigned int tracksize; + +static const byte controller_map[] = +{ + 0x00, 0x20, 0x01, 0x07, 0x0a, 0x0b, 0x5b, 0x5d, + 0x40, 0x43, 0x78, 0x7b, 0x7e, 0x7f, 0x79, +}; + +static int channel_map[NUM_CHANNELS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Write timestamp to a MIDI file. */ + +static boolean write_time(unsigned int time, MEMFILE *midioutput) +{ + unsigned int buffer = time & 0x7f; + byte writeval; + + while ((time >>= 7) != 0) + { + buffer <<= 8; + buffer |= ((time & 0x7f) | 0x80); + } + + for (; ; ) + { + writeval = (byte)(buffer & 0xff); + + if (mem_fwrite(&writeval, 1, 1, midioutput) != 1) + { + return true; + } + + ++tracksize; + + if ((buffer & 0x80) != 0) + { + buffer >>= 8; + } + else + { + queuedtime = 0; + return false; + } + } +} + +/* Write the end of track marker */ + +static boolean write_end_track(MEMFILE *midioutput) +{ + byte endtrack[] = + { + 0xff, 0x2f, 0x00 + }; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(endtrack, 1, 3, midioutput) != 3) + { + return true; + } + + tracksize += 3; + return false; +} + +/* Write a key press event */ + +static boolean write_press_key(byte channel, byte key, byte velocity, + MEMFILE *midioutput) +{ + byte working = midi_presskey | channel; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = key & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = velocity & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 3; + + return false; +} + +/* Write a key release event */ + +static boolean write_release_key(byte channel, byte key, MEMFILE *midioutput) +{ + byte working = midi_releasekey | channel; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = key & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = 0; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 3; + + return false; +} + +/* Write a pitch wheel/bend event */ + +static boolean write_pitch_wheel(byte channel, short wheel, + MEMFILE *midioutput) +{ + byte working = midi_pitchwheel | channel; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = wheel & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = (wheel >> 7) & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 3; + return false; +} + +/* Write a patch change event */ + +static boolean write_change_patch(byte channel, byte patch, + MEMFILE *midioutput) +{ + byte working = midi_changepatch | channel; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = patch & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 2; + + return false; +} + +/* Write a valued controller change event */ + +static boolean write_change_controller_valued(byte channel, byte control, + byte value, MEMFILE *midioutput) +{ + byte working = midi_changecontroller | channel; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = control & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + /* Quirk in vanilla DOOM? MUS controller values should be + * 7-bit, not 8-bit. + */ + + working = value; /* & 0x7F; */ + + /* Fix on said quirk to stop MIDI players from complaining that + * the value is out of range: + */ + + if (working & 0x80) + { + working = 0x7f; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 3; + + return false; +} + +/* Write a valueless controller change event */ + +static boolean write_change_controller_valueless(byte channel, byte control, + MEMFILE *midioutput) +{ + return write_change_controller_valued(channel, control, 0, midioutput); +} + +/* Allocate a free MIDI channel. */ + +static int allocate_midi_channel(void) +{ + int result; + int max; + int i; + + /* Find the current highest-allocated channel. */ + + max = -1; + + for (i = 0; i < NUM_CHANNELS; ++i) + { + if (channel_map[i] > max) + { + max = channel_map[i]; + } + } + + /* max is now equal to the highest-allocated MIDI channel. We can + * now allocate the next available channel. This also works if + * no channels are currently allocated (max=-1) + */ + + result = max + 1; + + /* Don't allocate the MIDI percussion channel! */ + + if (result == MIDI_PERCUSSION_CHAN) + { + ++result; + } + + return result; +} + +/* Given a MUS channel number, get the MIDI channel number to use + * in the outputted file. + */ + +static int get_midi_channel(int mus_channel, MEMFILE *midioutput) +{ + /* Find the MIDI channel to use for this MUS channel. + * MUS channel 15 is the percusssion channel. + */ + + if (mus_channel == MUS_PERCUSSION_CHAN) + { + return MIDI_PERCUSSION_CHAN; + } + else + { + /* If a MIDI channel hasn't been allocated for this MUS channel + * yet, allocate the next free MIDI channel. + */ + + if (channel_map[mus_channel] == -1) + { + channel_map[mus_channel] = allocate_midi_channel(); + + /* First time using the channel, send an "all notes off" + * event. This fixes "The D_DDTBLU disease" described here: + * https: *www.doomworld.com/vb/source-ports/66802-the + */ + + write_change_controller_valueless(channel_map[mus_channel], 0x7b, + midioutput); + } + + return channel_map[mus_channel]; + } +} + +static boolean read_mus_header(MEMFILE *file, musheader *header) +{ + boolean result; + + result = + mem_fread(&header->id, sizeof(byte), 4, file) == 4 && + mem_fread(&header->scorelength, sizeof(short), 1, file) == 1 && + mem_fread(&header->scorestart, sizeof(short), 1, file) == 1 && + mem_fread(&header->primarychannels, sizeof(short), 1, file) == 1 && + mem_fread(&header->secondarychannels, sizeof(short), 1, file) == 1 && + mem_fread(&header->instrumentcount, sizeof(short), 1, file) == 1; + + if (result) + { + header->scorelength = SHORT(header->scorelength); + header->scorestart = SHORT(header->scorestart); + header->primarychannels = SHORT(header->primarychannels); + header->secondarychannels = SHORT(header->secondarychannels); + header->instrumentcount = SHORT(header->instrumentcount); + } + + return result; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Read a MUS file from a stream (musinput) and output a MIDI file to + * a stream (midioutput). + * + * Returns 0 on success or 1 on failure. + */ + +boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput) +{ + /* Header for the MUS file */ + + musheader musfileheader; + + /* Descriptor for the current MUS event */ + + byte eventdescriptor; + int channel; /* Channel number */ + musevent event; + + /* Bunch of vars read from MUS lump */ + + byte key; + byte controllernumber; + byte controllervalue; + + /* Buffer used for MIDI track size record */ + + byte tracksizebuffer[4]; + + /* Flag for when the score end marker is hit. */ + + int hitscoreend = 0; + + /* Temp working byte */ + + byte working; + + /* Used in building up time delays */ + + unsigned int timedelay; + + /* Initialise channel map to mark all channels as unused. */ + + for (channel = 0; channel < NUM_CHANNELS; ++channel) + { + channel_map[channel] = -1; + } + + /* Grab the header */ + + if (!read_mus_header(musinput, &musfileheader)) + { + return true; + } + + /* Check MUS header */ + +#ifdef CHECK_MUS_HEADER + if (musfileheader.id[0] != 'M' || musfileheader.id[1] != 'U' || + musfileheader.id[2] != 'S' || musfileheader.id[3] != 0x1a) + { + return true; + } +#endif + + /* Seek to where the data is held */ + + if (mem_fseek(musinput, (long)musfileheader.scorestart, MEM_SEEK_SET) != 0) + { + return true; + } + + /* So, we can assume the MUS file is faintly legit. Let's start + * writing MIDI data... + */ + + mem_fwrite(midiheader, 1, sizeof(midiheader), midioutput); + tracksize = 0; + + /* Now, process the MUS file: */ + + while (!hitscoreend) + { + /* Handle a block of events: */ + + while (!hitscoreend) + { + /* Fetch channel number and event code: */ + + if (mem_fread(&eventdescriptor, 1, 1, musinput) != 1) + { + return true; + } + + channel = get_midi_channel(eventdescriptor & 0x0f, midioutput); + event = eventdescriptor & 0x70; + + switch (event) + { + case mus_releasekey: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + return true; + } + + if (write_release_key(channel, key, midioutput)) + { + return true; + } + + break; + + case mus_presskey: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + return true; + } + + if (key & 0x80) + { + if (mem_fread(&channelvelocities[channel], 1, 1, + musinput) != 1) + { + return true; + } + + channelvelocities[channel] &= 0x7f; + } + + if (write_press_key(channel, key, channelvelocities[channel], + midioutput)) + { + return true; + } + + break; + + case mus_pitchwheel: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + break; + } + + if (write_pitch_wheel(channel, (short)(key * 64), midioutput)) + { + return true; + } + + break; + + case mus_systemevent: + if (mem_fread(&controllernumber, 1, 1, musinput) != 1) + { + return true; + } + + if (controllernumber < 10 || controllernumber > 14) + { + return true; + } + + if (write_change_controller_valueless( + channel, controller_map[controllernumber], midioutput)) + { + return true; + } + + break; + + case mus_changecontroller: + if (mem_fread(&controllernumber, 1, 1, musinput) != 1) + { + return true; + } + + if (mem_fread(&controllervalue, 1, 1, musinput) != 1) + { + return true; + } + + if (controllernumber == 0) + { + if (write_change_patch(channel, controllervalue, + midioutput)) + { + return true; + } + } + else + { + if (controllernumber < 1 || controllernumber > 9) + { + return true; + } + + if (write_change_controller_valued( + channel, controller_map[controllernumber], + controllervalue, midioutput)) + { + return true; + } + } + + break; + + case mus_scoreend: + hitscoreend = 1; + break; + + default: + return true; + break; + } + + if (eventdescriptor & 0x80) + { + break; + } + } + + /* Now we need to read the time code: */ + + if (!hitscoreend) + { + timedelay = 0; + for (; ; ) + { + if (mem_fread(&working, 1, 1, musinput) != 1) + { + return true; + } + + timedelay = timedelay * 128 + (working & 0x7f); + if ((working & 0x80) == 0) + { + break; + } + } + + queuedtime += timedelay; + } + } + + /* End of track */ + + if (write_end_track(midioutput)) + { + return true; + } + + /* Write the track size into the stream */ + + if (mem_fseek(midioutput, 18, MEM_SEEK_SET)) + { + return true; + } + + tracksizebuffer[0] = (tracksize >> 24) & 0xff; + tracksizebuffer[1] = (tracksize >> 16) & 0xff; + tracksizebuffer[2] = (tracksize >> 8) & 0xff; + tracksizebuffer[3] = tracksize & 0xff; + + if (mem_fwrite(tracksizebuffer, 1, 4, midioutput) != 4) + { + return true; + } + + return false; +} + +#ifdef STANDALONE +int main(int argc, char *argv[]) +{ + MEMFILE *src, *dst; + byte *infile; + long infile_len; + void *outfile; + size_t outfile_len; + + if (argc != 3) + { + printf("Usage: %s \n", argv[0]); + exit(-1); + } + + z_init(); + + infile_len = m_read_file(argv[1], &infile); + + src = mem_fopen_read(infile, infile_len); + dst = mem_fopen_write(); + + if (mus2mid(src, dst)) + { + fprintf(stderr, "mus2mid() failed\n"); + exit(-1); + } + + /* Write result to output file: */ + + mem_get_buf(dst, &outfile, &outfile_len); + + m_write_file(argv[2], outfile, outfile_len); + + return 0; +} +#endif /* STANDALONE */ diff --git a/games/NXDoom/src/mus2mid.h b/games/NXDoom/src/mus2mid.h new file mode 100644 index 00000000000..e8cafc4a8ac --- /dev/null +++ b/games/NXDoom/src/mus2mid.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * apps/games/NXDoom/src/mus2mid.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2006 Ben Ryves 2006 + * + * 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. + * + * mus2mid.h - Ben Ryves 2006 - http: *benryves.com - benryves@benryves.com + * Use to convert a MUS file into a single track, type 0 MIDI file. + * + ****************************************************************************/ + +#ifndef MUS2MID_H +#define MUS2MID_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" +#include "memio.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput); + +#endif /* MUS2MID_H */ diff --git a/games/NXDoom/src/net_client.c b/games/NXDoom/src/net_client.c new file mode 100644 index 00000000000..fb589f692a3 --- /dev/null +++ b/games/NXDoom/src/net_client.c @@ -0,0 +1,1311 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_client.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Network client code + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "config.h" +#include "d_loop.h" +#include "deh_main.h" +#include "deh_str.h" +#include "doomtype.h" +#include "i_system.h" +#include "i_timer.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_fixed.h" +#include "m_misc.h" +#include "net_client.h" +#include "net_common.h" +#include "net_defs.h" +#include "net_gui.h" +#include "net_io.h" +#include "net_packet.h" +#include "net_query.h" +#include "net_server.h" +#include "net_structrw.h" +#include "w_checksum.h" +#include "w_wad.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define net_cl_expand_ticnum(b) net_expand_tic_num(recvwindow_start, (b)) + +#define KP 0.1 +#define KI 0.01 +#define KD 0.02 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + /* waiting for the game to launch */ + + CLIENT_STATE_WAITING_LAUNCH, + + /* waiting for the game to start */ + + CLIENT_STATE_WAITING_START, + + /* in game */ + + CLIENT_STATE_IN_GAME, +} net_clientstate_t; + +/* Type of structure used in the receive window */ + +typedef struct +{ + /* Whether this tic has been received yet */ + + boolean active; + + /* Last time we sent a resend request for this tic */ + + unsigned int resend_time; + + /* Tic data from server */ + + net_full_ticcmd_t cmd; +} net_server_recv_t; + +/* Type of structure used in the send window */ + +typedef struct +{ + /* Whether this slot is active yet */ + + boolean active; + + /* The tic number */ + + unsigned int seq; + + /* Time the command was generated */ + + unsigned int time; + + /* Ticcmd diff */ + + net_ticdiff_t cmd; +} net_server_send_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static net_connection_t client_connection; +static net_clientstate_t client_state; +static net_addr_t *server_addr; +static net_context_t *client_context; + +/* game settings, as received from the server when the game started */ + +static net_gamesettings_t settings; + +/* The last ticcmd constructed */ + +static ticcmd_t last_ticcmd; + +/* Buffer of ticcmd diffs being sent to the server */ + +static net_server_send_t send_queue[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + +/* Receive window */ + +static ticcmd_t recvwindow_cmd_base[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +static int recvwindow_start; +static net_server_recv_t recvwindow[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + +/* Whether we need to send an acknowledgement and + * when gamedata was last received. + */ + +static boolean need_to_acknowledge; +static unsigned int gamedata_recv_time; + +/* The latency (time between when we sent our command and we got all + * the other players' commands from the server) for the last tic we + * received. We include this latency in tics we send to the server so + * that they can adjust to us. + */ + +static int last_latency; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Why did the server reject us? */ + +char *net_client_reject_reason = NULL; + +/* true if the client code is in use */ + +boolean net_client_connected; + +/* true if we have received waiting data from the server, + * and the wait data that was received. + */ + +boolean net_client_received_wait_data; +net_waitdata_t net_client_wait_data; + +/* Waiting at the initial wait screen for the game to be launched? */ + +boolean net_waiting_for_launch = false; + +/* Name that we send to the server */ + +char *net_player_name = NULL; + +/* Connected but not participating in the game (observer) */ + +boolean drone = false; + +/* Hash checksums of our wad directory and dehacked data. */ + +sha1_digest_t net_local_wad_sha1sum; +sha1_digest_t net_local_deh_sha1sum; + +/* Are we playing with the freedoom IWAD? */ + +unsigned int net_local_is_freedoom; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Called when we become disconnected from the server */ + +static void net_cl_disconnected(void) +{ + d_receive_tic(NULL, NULL); +} + +/* Called when a packet is received from the server containing game + * data. This updates the clock synchronization variable (offsetms) + * using a PID filter that keeps client clocks in sync. + */ + +static void update_clock_sync(unsigned int seq, unsigned int remote_latency) +{ + static int last_error; + static int cumul_error; + int latency; + int error; + + if (seq == send_queue[seq % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].seq) + { + latency = i_get_time_ms() - + send_queue[seq % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].time; + } + else if (seq > send_queue[seq % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].seq) + { + /* We have received the ticcmd from the server before we have + * even sent ours + */ + + latency = 0; + } + else + { + return; + } + + /* PID filter. These are manually trained parameters. */ + + /* How does our latency compare to the worst other player? */ + + error = latency - remote_latency; + cumul_error += error; + + offsetms = KP * (FRACUNIT * error) - KI * (FRACUNIT * cumul_error) + + (KD * FRACUNIT) * (last_error - error); + + last_error = error; + last_latency = latency; + + net_log_info( + "client: latency %d, remote %d -> offset=%dms, cumul_error=%d", + latency, remote_latency, offsetms / FRACUNIT, cumul_error); +} + +/* Expand a net_full_ticcmd_t, applying the diffs in cmd->cmds as + * patches against recvwindow_cmd_base. Place the results into + * the d_net.c structures (netcmds/nettics) and save the new ticcmd + * back into recvwindow_cmd_base. + */ + +static void net_cl_expand_full_ticcmd(net_full_ticcmd_t *cmd, + unsigned int seq, ticcmd_t *ticcmds) +{ + int i; + + /* Expand tic diffs for all players */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (i == settings.consoleplayer && !drone) + { + continue; + } + + if (cmd->playeringame[i]) + { + net_ticdiff_t *diff; + + diff = &cmd->cmds[i]; + + /* Use the ticcmd diff to patch the previous ticcmd to + * the new ticcmd + */ + + net_ticcmd_patch(&recvwindow_cmd_base[i], diff, &ticcmds[i]); + + /* Store a copy for next time */ + + recvwindow_cmd_base[i] = ticcmds[i]; + } + } +} + +/* Advance the receive window */ + +static void net_cl_advance_window(void) +{ + ticcmd_t ticcmds[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; + + while (recvwindow[0].active) + { + /* Expand tic diff data into d_net.c structures */ + + net_cl_expand_full_ticcmd(&recvwindow[0].cmd, recvwindow_start, + ticcmds); + d_receive_tic(ticcmds, recvwindow[0].cmd.playeringame); + + /* Advance the window */ + + memmove(recvwindow, recvwindow + 1, + sizeof(net_server_recv_t) * + (CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1)); + memset(&recvwindow[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1], 0, + sizeof(net_server_recv_t)); + + ++recvwindow_start; + + net_log_info("client: advanced receive window to %d", + recvwindow_start); + } +} + +/* Shut down the client code, etc. Invoked after a disconnect. */ + +static void net_cl_shutdown(void) +{ + if (net_client_connected) + { + net_client_connected = false; + + net_release_address(server_addr); + + /* Shut down network module, etc. To do. */ + } +} + +static void net_cl_send_game_data_ack(void) +{ + net_packet_t *packet; + + packet = net_new_packet(10); + + net_write_int16(packet, NET_PACKET_TYPE_GAMEDATA_ACK); + net_write_int8(packet, recvwindow_start & 0xff); + + net_conn_send_packet(&client_connection, packet); + + net_free_packet(packet); + + need_to_acknowledge = false; +} + +static void net_cl_send_tics(int start, int end) +{ + net_packet_t *packet; + int i; + + if (!net_client_connected) + { + /* Disconnected from server */ + + return; + } + + if (start < 0) start = 0; + + /* Build a new packet to send to the server */ + + packet = net_new_packet(512); + net_write_int16(packet, NET_PACKET_TYPE_GAMEDATA); + + /* Write the start tic and number of tics. Send only the low byte + * of start - it can be inferred by the server. + */ + + net_write_int8(packet, recvwindow_start & 0xff); + net_write_int8(packet, start & 0xff); + net_write_int8(packet, end - start + 1); + + /* Add the tics. */ + + for (i = start; i <= end; ++i) + { + net_server_send_t *sendobj; + + sendobj = &send_queue[i % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + net_write_int16(packet, last_latency); + + net_write_ticcmd_diff(packet, &sendobj->cmd, settings.lowres_turn); + } + + /* Send the packet */ + + net_conn_send_packet(&client_connection, packet); + + /* All done! */ + + net_free_packet(packet); + + /* Acknowledgement has been sent as part of the packet */ + + need_to_acknowledge = false; +} + +/* Parse a SYN packet received back from the server indicating a successful + * connection attempt. + */ + +static void net_cl_parse_syn(net_packet_t *packet) +{ + net_protocol_t protocol; + char *server_version; + + net_log_info("client: processing SYN response"); + + server_version = net_read_safe_string(packet); + if (server_version == NULL) + { + net_log_err("client: failed to read server version"); + return; + } + + protocol = net_read_protocol(packet); + if (protocol == NET_PROTOCOL_UNKNOWN) + { + net_log_err("client: can't find a common protocol"); + return; + } + + /* We are now successfully connected. */ + + net_log_info("client: connected to server"); + client_connection.state = NET_CONN_STATE_CONNECTED; + client_connection.protocol = protocol; + + /* Even though we have negotiated a compatible protocol, the game may still + * desync. Chocolate Doom's philosophy makes this unlikely, but if we're + * playing with a forked version, or even against a different version that + * fixes a compatibility issue, we may still have problems. + */ + + if (strcmp(server_version, PACKAGE_STRING) != 0) + { + fprintf(stderr, + "NET_CL_ParseSYN: This is '%s', but the server is " + "'%s'. It is possible that this mismatch may cause the game " + "to desync.\n", + PACKAGE_STRING, server_version); + } +} + +static void set_reject_reason(const char *s) +{ + free(net_client_reject_reason); + if (s != NULL) + { + net_client_reject_reason = strdup(s); + } + else + { + net_client_reject_reason = NULL; + } +} + +static void net_cl_parse_reject(net_packet_t *packet) +{ + char *msg; + + msg = net_read_safe_string(packet); + if (msg == NULL) + { + return; + } + + if (client_connection.state == NET_CONN_STATE_CONNECTING) + { + client_connection.state = NET_CONN_STATE_DISCONNECTED; + client_connection.disconnect_reason = NET_DISCONNECT_REMOTE; + set_reject_reason(msg); + } +} + +/* data received while we are waiting for the game to start */ + +static void net_cl_parse_waiting_data(net_packet_t *packet) +{ + net_waitdata_t wait_data; + + if (!net_read_wait_data(packet, &wait_data)) + { + /* Invalid packet? */ + + return; + } + + if (wait_data.num_players > wait_data.max_players || + wait_data.ready_players > wait_data.num_players || + wait_data.max_players > CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS) + { + /* insane data */ + + return; + } + + if ((wait_data.consoleplayer >= 0 && drone) || + (wait_data.consoleplayer < 0 && !drone) || + (wait_data.consoleplayer >= wait_data.num_players)) + { + /* Invalid player number */ + + return; + } + + memcpy(&net_client_wait_data, &wait_data, sizeof(net_waitdata_t)); + net_client_received_wait_data = true; +} + +static void net_cl_parse_launch(net_packet_t *packet) +{ + unsigned int num_players; + + net_log_info("client: processing launch packet"); + + if (client_state != CLIENT_STATE_WAITING_LAUNCH) + { + net_log_err("client: not in waiting launch state, client_state=%d", + client_state); + return; + } + + /* The launch packet contains the number of players that will be + * in the game when it starts, so that we can do the startup + * progress indicator (the wait data is unreliable). + */ + + if (!net_read_int8(packet, &num_players)) + { + net_log_err("client: failed to read number of players"); + return; + } + + net_client_wait_data.num_players = num_players; + client_state = CLIENT_STATE_WAITING_START; + net_log_err("client: now waiting for game start"); +} + +static void net_cl_parse_game_start(net_packet_t *packet) +{ + net_log_info("client: processing game start packet"); + + if (!net_read_settings(packet, &settings)) + { + net_log_err("client: failed to read settings"); + return; + } + + if (client_state != CLIENT_STATE_WAITING_START) + { + net_log_err("client: not in waiting start state, client_state=%d", + client_state); + return; + } + + if (settings.num_players > CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS || + settings.consoleplayer >= (signed int)settings.num_players) + { + /* insane values */ + + net_log_err("client: bad settings, num_players=%d, consoleplayer=%d", + settings.num_players, settings.consoleplayer); + return; + } + + if ((drone && settings.consoleplayer >= 0) || + (!drone && settings.consoleplayer < 0)) + { + /* Invalid player number: must be positive for real players, + * negative for drones + */ + + net_log_err("client: mismatch: drone=%d, consoleplayer=%d", drone, + settings.consoleplayer); + return; + } + + net_log_info("client: beginning game state"); + client_state = CLIENT_STATE_IN_GAME; + + /* Clear the receive window */ + + memset(recvwindow, 0, sizeof(recvwindow)); + recvwindow_start = 0; + memset(&recvwindow_cmd_base, 0, sizeof(recvwindow_cmd_base)); + + /* Clear the send queue */ + + memset(&send_queue, 0x00, sizeof(send_queue)); +} + +static void net_cl_send_resend_request(int start, int end) +{ + net_packet_t *packet; + unsigned int nowtime; + int i; + + packet = net_new_packet(64); + net_write_int16(packet, NET_PACKET_TYPE_GAMEDATA_RESEND); + net_write_int32(packet, start); + net_write_int8(packet, end - start + 1); + net_conn_send_packet(&client_connection, packet); + net_free_packet(packet); + + nowtime = i_get_time_ms(); + + /* Save the time we sent the resend request */ + + for (i = start; i <= end; ++i) + { + int index; + + index = i - recvwindow_start; + + if (index < 0 || index >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) continue; + + recvwindow[index].resend_time = nowtime; + } +} + +/* Check for expired resend requests */ + +static void net_cl_check_resends(void) +{ + int i; + int resend_start; + int resend_end; + unsigned int nowtime; + boolean maybe_deadlocked; + + nowtime = i_get_time_ms(); + maybe_deadlocked = nowtime - gamedata_recv_time > 1000; + + resend_start = -1; + resend_end = -1; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_BACKUPTICS; ++i) + { + net_server_recv_t *recvobj; + boolean need_resend; + + recvobj = &recvwindow[i]; + + /* if need_resend is true, this tic needs another retransmit + * request (300ms timeout) + */ + + need_resend = !recvobj->active && recvobj->resend_time != 0 && + nowtime > recvobj->resend_time + 300; + + /* if no game data has been received in a long time, we may be in + * a deadlock scenario where tics from the server have been lost, so + * we've stopped generating any more, so the server isn't sending us + * any, so we don't get any to trigger a resend request. So force the + * first few tics in the receive window to be requested. + */ + + if (i == 0 && !recvobj->active && recvobj->resend_time == 0 && + maybe_deadlocked) + { + need_resend = true; + } + + if (need_resend) + { + /* Start a new run of resend tics? */ + + if (resend_start < 0) + { + resend_start = i; + } + + resend_end = i; + } + else if (resend_start >= 0) + { + /* End of a run of resend tics */ + + net_log_info("client: resend request timed out for %d-%d (%d)", + recvwindow_start + resend_start, + recvwindow_start + resend_end, + recvwindow[resend_start].resend_time); + net_cl_send_resend_request(recvwindow_start + resend_start, + recvwindow_start + resend_end); + resend_start = -1; + } + } + + if (resend_start >= 0) + { + net_log_info("client: resend request timed out for %d-%d (%d)", + recvwindow_start + resend_start, + recvwindow_start + resend_end, + recvwindow[resend_start].resend_time); + net_cl_send_resend_request(recvwindow_start + resend_start, + recvwindow_start + resend_end); + } + + /* We have received some data from the server and not acknowledged + * it yet. Normally this gets acknowledged when we send our game + * data, but if the client is a drone we need to do this. + */ + + if (need_to_acknowledge && nowtime - gamedata_recv_time > 200) + { + net_log_info("client: no game data received since %d: triggering ack", + gamedata_recv_time); + net_cl_send_game_data_ack(); + } +} + +/* Parsing of NET_PACKET_TYPE_GAMEDATA packets + * (packets containing the actual ticcmd data) + */ + +static void net_cl_parse_game_data(net_packet_t *packet) +{ + net_server_recv_t *recvobj; + unsigned int seq; + unsigned int num_tics; + unsigned int nowtime; + int resend_start; + int resend_end; + size_t i; + int index; + + net_log_info("client: processing game data packet"); + + /* Read header */ + + if (!net_read_int8(packet, &seq) || !net_read_int8(packet, &num_tics)) + { + net_log_info("client: error: failed to read header"); + return; + } + + nowtime = i_get_time_ms(); + + /* Whatever happens, we now need to send an acknowledgement of our + * current receive point. + */ + + if (!need_to_acknowledge) + { + need_to_acknowledge = true; + gamedata_recv_time = nowtime; + } + + /* Expand byte value into the full tic number */ + + seq = net_cl_expand_ticnum(seq); + net_log_info("client: got game data, seq=%d, num_tics=%d", seq, num_tics); + + for (i = 0; i < num_tics; ++i) + { + net_full_ticcmd_t cmd; + + index = seq - recvwindow_start + i; + + if (!net_read_full_ticcmd(packet, &cmd, settings.lowres_turn)) + { + net_log_err("client: failed to read ticcmd %zu", i); + return; + } + + if (index < 0 || index >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + /* Out of range of the recv window */ + + continue; + } + + /* Store in the receive window */ + + recvobj = &recvwindow[index]; + + recvobj->active = true; + recvobj->cmd = cmd; + net_log_info("client: stored tic %zu in receive window", seq + i); + + /* If a packet is lost or arrives out of order, we might get + * the tic in the next packet instead (because of extratic). + * If that's the case then the latency for receiving that tic + * now will be bogus. So we only use the last tic in the packet + * to trigger a clock sync update. + */ + + if (i == num_tics - 1) + { + update_clock_sync(seq + i, cmd.latency); + } + } + + /* Has this been received out of sequence, ie. have we not received + * all tics before the first tic in this packet? If so, send a + * resend request. + */ + + resend_end = seq - recvwindow_start; + + if (resend_end <= 0) return; + + if (resend_end >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + resend_end = CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1; + } + + index = resend_end - 1; + resend_start = resend_end; + + while (index >= 0) + { + recvobj = &recvwindow[index]; + + if (recvobj->active) + { + /* ended our run of unreceived tics */ + + break; + } + + if (recvobj->resend_time != 0) + { + /* Already sent a resend request for this tic */ + + break; + } + + resend_start = index; + --index; + } + + /* Possibly send a resend request */ + + if (resend_start < resend_end) + { + net_log_info("client: request resend for %d-%d before %d", + recvwindow_start + resend_start, + recvwindow_start + resend_end - 1, seq); + net_cl_send_resend_request(recvwindow_start + resend_start, + recvwindow_start + resend_end - 1); + } +} + +/* Parse a resend request from the server due to a dropped packet */ + +static void net_cl_parse_resend_request(net_packet_t *packet) +{ + static unsigned int start; + static unsigned int end; + static unsigned int num_tics; + + net_log_info("client: processing resend request"); + + if (drone) + { + /* Drones don't send gamedata. */ + + net_log_err("client: resend request but we're a drone?"); + return; + } + + if (!net_read_int32(packet, &start) || !net_read_int8(packet, &num_tics)) + { + net_log_err("client: couldn't read start and num_tics"); + return; + } + + end = start + num_tics - 1; + + net_log_info("client: resend request: start=%d, num_tics=%d", start, + num_tics); + + /* Check we have the tics being requested. If not, reduce the + * window of tics to only what we have. + */ + + while ( + start <= end && + (!send_queue[start % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].active || + send_queue[start % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].seq != start)) + { + ++start; + } + + while (start <= end && + (!send_queue[end % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].active || + send_queue[end % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].seq != end)) + { + --end; + } + + /* Resend those tics */ + + if (start <= end) + { + net_log_info("client: resending %d-%d", start, end); + net_cl_send_tics(start, end); + } + else + { + net_log_info("client: don't have the tics to resend"); + } +} + +/* Console message that the server wants the client to print */ + +static void net_cl_parse_console_message(net_packet_t *packet) +{ + char *msg; + + msg = net_read_safe_string(packet); + + if (msg == NULL) + { + return; + } + + printf("Message from server:\n%s\n", msg); +} + +/* parse a received packet */ + +static void net_cl_parse_packet(net_packet_t *packet) +{ + unsigned int packet_type; + + if (!net_read_int16(packet, &packet_type)) + { + return; + } + + net_log_info("client: packet from server, type %d", + packet_type & ~NET_RELIABLE_PACKET); + net_log_packet(packet); + + if (net_conn_packet(&client_connection, packet, &packet_type)) + { + /* Packet eaten by the common connection code */ + } + else + { + switch (packet_type) + { + case NET_PACKET_TYPE_SYN: + net_cl_parse_syn(packet); + break; + + case NET_PACKET_TYPE_REJECTED: + net_cl_parse_reject(packet); + break; + + case NET_PACKET_TYPE_WAITING_DATA: + net_cl_parse_waiting_data(packet); + break; + + case NET_PACKET_TYPE_LAUNCH: + net_cl_parse_launch(packet); + break; + + case NET_PACKET_TYPE_GAMESTART: + net_cl_parse_game_start(packet); + break; + + case NET_PACKET_TYPE_GAMEDATA: + net_cl_parse_game_data(packet); + break; + + case NET_PACKET_TYPE_GAMEDATA_RESEND: + net_cl_parse_resend_request(packet); + break; + + case NET_PACKET_TYPE_CONSOLE_MESSAGE: + net_cl_parse_console_message(packet); + break; + + default: + break; + } + } +} + +static void net_cl_send_syn(net_connect_data_t *data) +{ + net_packet_t *packet; + + net_log_info("client: sending SYN"); + + packet = net_new_packet(10); + net_write_int16(packet, NET_PACKET_TYPE_SYN); + net_write_int32(packet, NET_MAGIC_NUMBER); + net_write_string(packet, PACKAGE_STRING); + net_write_protocol_list(packet); + net_write_connect_data(packet, data); + net_write_string(packet, net_player_name); + net_conn_send_packet(&client_connection, packet); + net_free_packet(packet); +} + +static void net_cl_init(void) +{ + /* Try to set from the USER and USERNAME environment variables + * Otherwise, fallback to "Player" + */ + + if (net_player_name == NULL) + { + net_player_name = "Player"; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void net_cl_launch_game(void) +{ + net_conn_new_reliable(&client_connection, NET_PACKET_TYPE_LAUNCH); +} + +void net_cl_start_game(net_gamesettings_t *p_settings) +{ + net_packet_t *packet; + + /* Start from a ticcmd of all zeros */ + + memset(&last_ticcmd, 0, sizeof(ticcmd_t)); + + /* Send packet */ + + packet = + net_conn_new_reliable(&client_connection, NET_PACKET_TYPE_GAMESTART); + + net_write_settings(packet, p_settings); +} + +/* Add a new ticcmd to the send queue */ + +void net_cl_send_ticcmd(ticcmd_t *ticcmd, int maketic) +{ + net_ticdiff_t diff; + net_server_send_t *sendobj; + int starttic; + int endtic; + + /* Calculate the difference to the last ticcmd */ + + net_ticcmd_diff(&last_ticcmd, ticcmd, &diff); + + /* Store in the send queue */ + + sendobj = &send_queue[maketic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + sendobj->active = true; + sendobj->seq = maketic; + sendobj->time = i_get_time_ms(); + sendobj->cmd = diff; + + last_ticcmd = *ticcmd; + + /* Send to server. */ + + starttic = maketic - settings.extratics; + endtic = maketic; + + if (starttic < 0) starttic = 0; + + net_log_info("client: generated tic %d, sending %d-%d", maketic, starttic, + endtic); + net_cl_send_tics(starttic, endtic); +} + +/* "Run" the client code: check for new packets, send packets as + * needed + */ + +void net_cl_run(void) +{ + net_addr_t *addr; + net_packet_t *packet; + + if (!net_client_connected) + { + return; + } + + while (net_recv_packet(client_context, &addr, &packet)) + { + /* only accept packets from the server */ + + if (addr == server_addr) + { + net_cl_parse_packet(packet); + } + + net_free_packet(packet); + net_release_address(addr); + } + + /* Run the common connection code to send any packets as needed */ + + net_conn_run(&client_connection); + + if (client_connection.state == NET_CONN_STATE_DISCONNECTED || + client_connection.state == NET_CONN_STATE_DISCONNECTED_SLEEP) + { + net_cl_disconnected(); + + net_cl_shutdown(); + } + + net_waiting_for_launch = + client_connection.state == NET_CONN_STATE_CONNECTED && + client_state == CLIENT_STATE_WAITING_LAUNCH; + + if (client_state == CLIENT_STATE_IN_GAME) + { + /* Possibly advance the receive window */ + + net_cl_advance_window(); + + /* Check if our resend requests have timed out */ + + net_cl_check_resends(); + } +} + +/* Connect to a server */ + +boolean net_cl_connect(net_addr_t *addr, net_connect_data_t *data) +{ + int start_time; + int last_send_time; + boolean sent_hole_punch; + + server_addr = addr; + net_reference_address(addr); + + memcpy(net_local_wad_sha1sum, data->wad_sha1sum, sizeof(sha1_digest_t)); + memcpy(net_local_deh_sha1sum, data->deh_sha1sum, sizeof(sha1_digest_t)); + net_local_is_freedoom = data->is_freedoom; + + /* create a new network I/O context and add just the necessary module */ + + client_context = net_new_context(); + + /* initialize module for client mode */ + + if (!addr->module->init_client()) + { + set_reject_reason("Failed to initialize client module"); + return false; + } + + net_add_module(client_context, addr->module); + + net_client_connected = true; + net_client_received_wait_data = false; + sent_hole_punch = false; + + net_conn_init_client(&client_connection, addr, NET_PROTOCOL_UNKNOWN); + + /* try to connect */ + + start_time = i_get_time_ms(); + last_send_time = -1; + set_reject_reason("Unknown reason"); + + while (client_connection.state == NET_CONN_STATE_CONNECTING) + { + int nowtime = i_get_time_ms(); + + /* Send a SYN packet every second. */ + + if (nowtime - last_send_time > 1000 || last_send_time < 0) + { + net_cl_send_syn(data); + last_send_time = nowtime; + } + + /* time out after 5 seconds */ + + if (nowtime - start_time > 5000) + { + set_reject_reason("No response from server"); + break; + } + + if (!sent_hole_punch && nowtime - start_time > 2000) + { + net_log_warn("client: no response to SYN, requesting hole punch"); + net_request_hole_punch(client_context, addr); + sent_hole_punch = true; + } + + /* run client code */ + + net_cl_run(); + + /* run the server, just in case we are doing a loopback connect */ + + net_sv_run(); + + /* Don't hog the CPU */ + + usleep(1000); + } + + if (client_connection.state == NET_CONN_STATE_CONNECTED) + { + /* connected ok! */ + + net_log_info("client: connected successfully"); + set_reject_reason(NULL); + client_state = CLIENT_STATE_WAITING_LAUNCH; + drone = data->drone; + + return true; + } + else + { + /* failed to connect */ + + net_log_err("client: failed to connect"); + net_cl_shutdown(); + + return false; + } +} + +/* read game settings received from server */ + +boolean net_cl_get_settings(net_gamesettings_t *_settings) +{ + if (client_state != CLIENT_STATE_IN_GAME) + { + return false; + } + + memcpy(_settings, &settings, sizeof(net_gamesettings_t)); + + return true; +} + +/* disconnect from the server */ + +void net_cl_disconnect(void) +{ + int start_time; + + if (!net_client_connected) + { + return; + } + + net_log_info("client: beginning disconnect"); + net_conn_disconnect(&client_connection); + + start_time = i_get_time_ms(); + + while (client_connection.state != NET_CONN_STATE_DISCONNECTED && + client_connection.state != NET_CONN_STATE_DISCONNECTED_SLEEP) + { + if (i_get_time_ms() - start_time > 5000) + { + /* time out after 5 seconds */ + + net_log_err("client: no acknowledgement of disconnect received"); + client_state = CLIENT_STATE_WAITING_START; + + fprintf(stderr, "net_cl_disconnect: Timeout while disconnecting " + "from server\n"); + break; + } + + net_cl_run(); + net_sv_run(); + + usleep(1000); + } + + /* Finished sending disconnect packets, etc. */ + + net_log_info("client: disconnect complete"); + net_cl_shutdown(); +} + +void net_init(void) +{ + net_cl_init(); +} + +void net_bind_variables(void) +{ + m_bind_string_variable("player_name", &net_player_name); +} diff --git a/games/NXDoom/src/net_client.h b/games/NXDoom/src/net_client.h new file mode 100644 index 00000000000..abe3d1b8256 --- /dev/null +++ b/games/NXDoom/src/net_client.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_client.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Network client code + * + ****************************************************************************/ + +#ifndef NET_CLIENT_H +#define NET_CLIENT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_ticcmd.h" +#include "doomtype.h" +#include "net_defs.h" +#include "sha1.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern boolean net_client_connected; +extern boolean net_client_received_wait_data; +extern net_waitdata_t net_client_wait_data; +extern char *net_client_reject_reason; +extern boolean net_waiting_for_launch; +extern char *net_player_name; + +extern sha1_digest_t net_server_wad_sha1sum; +extern sha1_digest_t net_server_deh_sha1sum; +extern unsigned int net_server_is_freedoom; +extern sha1_digest_t net_local_wad_sha1sum; +extern sha1_digest_t net_local_deh_sha1sum; +extern unsigned int net_local_is_freedoom; + +extern boolean drone; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean net_cl_connect(net_addr_t *addr, net_connect_data_t *data); +void net_cl_disconnect(void); +void net_cl_run(void); +void net_cl_launch_game(void); +void net_cl_start_game(net_gamesettings_t *settings); +void net_cl_send_ticcmd(ticcmd_t *ticcmd, int maketic); +boolean net_cl_get_settings(net_gamesettings_t *_settings); +void net_init(void); + +void net_bind_variables(void); + +#endif /* NET_CLIENT_H */ diff --git a/games/NXDoom/src/net_common.c b/games/NXDoom/src/net_common.c new file mode 100644 index 00000000000..62dac01ca6d --- /dev/null +++ b/games/NXDoom/src/net_common.c @@ -0,0 +1,532 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_common.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Common code shared between the client and server + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "d_mode.h" +#include "doomtype.h" +#include "i_system.h" +#include "i_timer.h" +#include "m_argv.h" +#include "m_misc.h" + +#include "net_common.h" +#include "net_io.h" +#include "net_packet.h" +#include "net_structrw.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* connections time out after 30 seconds */ + +#define CONNECTION_TIMEOUT_LEN 30 + +/* maximum time between sending packets */ + +#define KEEPALIVE_PERIOD 1 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* reliable packet that is guaranteed to reach its destination */ + +struct net_reliable_packet_s +{ + net_packet_t *packet; + int last_send_time; + int seq; + net_reliable_packet_t *next; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void net_conn_init(net_connection_t *conn, net_addr_t *addr, + net_protocol_t protocol) +{ + conn->last_send_time = -1; + conn->num_retries = 0; + conn->addr = addr; + conn->protocol = protocol; + conn->reliable_packets = NULL; + conn->reliable_send_seq = 0; + conn->reliable_recv_seq = 0; + conn->keepalive_recv_time = i_get_time_ms(); +} + +static void net_conn_parse_disconnect(net_connection_t *conn, + net_packet_t *packet) +{ + net_packet_t *reply; + + /* Other end wants to disconnect + * Send a DISCONNECT_ACK reply. + */ + + reply = net_new_packet(10); + net_write_int16(reply, NET_PACKET_TYPE_DISCONNECT_ACK); + net_conn_send_packet(conn, reply); + net_free_packet(reply); + + conn->last_send_time = i_get_time_ms(); + + conn->state = NET_CONN_STATE_DISCONNECTED_SLEEP; + conn->disconnect_reason = NET_DISCONNECT_REMOTE; +} + +/* Parse a DISCONNECT_ACK packet */ + +static void net_conn_parse_disconnect_ack(net_connection_t *conn, + net_packet_t *packet) +{ + if (conn->state == NET_CONN_STATE_DISCONNECTING) + { + /* We have received an acknowledgement to our disconnect + * request. We have been disconnected successfully. + */ + + conn->state = NET_CONN_STATE_DISCONNECTED; + conn->disconnect_reason = NET_DISCONNECT_LOCAL; + conn->last_send_time = -1; + } +} + +static void net_conn_parse_reliable_ack(net_connection_t *conn, + net_packet_t *packet) +{ + unsigned int seq; + + if (!net_read_int8(packet, &seq)) + { + return; + } + + if (conn->reliable_packets == NULL) + { + return; + } + + /* Is this an acknowledgement for the first packet in the list? */ + + if (seq == (unsigned int)((conn->reliable_packets->seq + 1) & 0xff)) + { + net_reliable_packet_t *rp; + + /* Discard it, then. + * Unlink from the list. + */ + + rp = conn->reliable_packets; + conn->reliable_packets = rp->next; + + net_free_packet(rp->packet); + free(rp); + } +} + +/* Process the header of a reliable packet + * + * Returns true if the packet should be discarded (incorrect sequence) + */ + +static boolean net_connect_reliable_packet(net_connection_t *conn, + net_packet_t *packet) +{ + unsigned int seq; + net_packet_t *reply; + boolean result; + + /* Read the sequence number */ + + if (!net_read_int8(packet, &seq)) + { + return true; + } + + if (seq != (unsigned int)(conn->reliable_recv_seq & 0xff)) + { + /* This is not the next expected packet in the sequence! + * + * Discard the packet. If we were smart, we would use a proper + * sliding window protocol to do this, but I'm lazy. + */ + + result = true; + } + else + { + /* Now we can receive the next packet in the sequence. */ + + conn->reliable_recv_seq = (conn->reliable_recv_seq + 1) & 0xff; + + result = false; + } + + /* Send an acknowledgement */ + + /* Note: this is braindead. It would be much more sensible to + * include this in the next packet, rather than the overhead of + * sending a complete packet just for one byte of information. + */ + + reply = net_new_packet(10); + + net_write_int16(reply, NET_PACKET_TYPE_RELIABLE_ACK); + net_write_int8(reply, conn->reliable_recv_seq & 0xff); + + net_conn_send_packet(conn, reply); + + net_free_packet(reply); + + return result; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Initialize as a client connection */ + +void net_conn_init_client(net_connection_t *conn, net_addr_t *addr, + net_protocol_t protocol) +{ + net_conn_init(conn, addr, protocol); + conn->state = NET_CONN_STATE_CONNECTING; +} + +/* Initialize as a server connection */ + +void net_conn_init_server(net_connection_t *conn, net_addr_t *addr, + net_protocol_t protocol) +{ + net_conn_init(conn, addr, protocol); + conn->state = NET_CONN_STATE_CONNECTED; +} + +/* Send a packet to a connection + * All packets should be sent through this interface, as it maintains the + * keepalive_send_time counter. + */ + +void net_conn_send_packet(net_connection_t *conn, net_packet_t *packet) +{ + conn->keepalive_send_time = i_get_time_ms(); + net_send_packet(conn->addr, packet); +} + +/* Process a packet received by the server + * + * Returns true if eaten by common code + */ + +boolean net_conn_packet(net_connection_t *conn, net_packet_t *packet, + unsigned int *packet_type) +{ + conn->keepalive_recv_time = i_get_time_ms(); + + /* Is this a reliable packet? */ + + if (*packet_type & NET_RELIABLE_PACKET) + { + if (net_connect_reliable_packet(conn, packet)) + { + /* Invalid packet: eat it. */ + + return true; + } + + /* Remove the reliable bit */ + + *packet_type &= ~NET_RELIABLE_PACKET; + } + + switch (*packet_type) + { + case NET_PACKET_TYPE_DISCONNECT: + net_conn_parse_disconnect(conn, packet); + break; + case NET_PACKET_TYPE_DISCONNECT_ACK: + net_conn_parse_disconnect_ack(conn, packet); + break; + case NET_PACKET_TYPE_KEEPALIVE: + break; /* No special action needed. */ + case NET_PACKET_TYPE_RELIABLE_ACK: + net_conn_parse_reliable_ack(conn, packet); + break; + default: + return false; /* Not a common packet */ + } + + /* We found a packet that we found interesting, and ate it. */ + + return true; +} + +void net_conn_disconnect(net_connection_t *conn) +{ + if (conn->state != NET_CONN_STATE_DISCONNECTED && + conn->state != NET_CONN_STATE_DISCONNECTING && + conn->state != NET_CONN_STATE_DISCONNECTED_SLEEP) + { + conn->state = NET_CONN_STATE_DISCONNECTING; + conn->disconnect_reason = NET_DISCONNECT_LOCAL; + conn->last_send_time = -1; + conn->num_retries = 0; + } +} + +void net_conn_run(net_connection_t *conn) +{ + net_packet_t *packet; + unsigned int nowtime; + + nowtime = i_get_time_ms(); + + if (conn->state == NET_CONN_STATE_CONNECTED) + { + /* Check the keepalive counters */ + + if (nowtime - conn->keepalive_recv_time > + CONNECTION_TIMEOUT_LEN * 1000) + { + /* Haven't received any packets from the other end in a long + * time. Assume disconnected. + */ + + conn->state = NET_CONN_STATE_DISCONNECTED; + conn->disconnect_reason = NET_DISCONNECT_TIMEOUT; + } + + if (nowtime - conn->keepalive_send_time > KEEPALIVE_PERIOD * 1000) + { + /* We have not sent anything in a long time. + * Send a keepalive. + */ + + packet = net_new_packet(10); + net_write_int16(packet, NET_PACKET_TYPE_KEEPALIVE); + net_conn_send_packet(conn, packet); + net_free_packet(packet); + } + + /* Check the reliable packet list. Has the first packet in the + * list timed out? + * + * NB. This is braindead, we have a fixed time of one second. + */ + + if (conn->reliable_packets != NULL && + (conn->reliable_packets->last_send_time < 0 || + nowtime - conn->reliable_packets->last_send_time > 1000)) + { + /* Packet timed out, time to resend */ + + net_conn_send_packet(conn, conn->reliable_packets->packet); + conn->reliable_packets->last_send_time = nowtime; + } + } + else if (conn->state == NET_CONN_STATE_DISCONNECTING) + { + /* Waiting for a reply to our DISCONNECT request. */ + + if (conn->last_send_time < 0 || nowtime - conn->last_send_time > 1000) + { + /* it has been a second since the last disconnect packet + * was sent, and still no reply. + */ + + if (conn->num_retries < MAX_RETRIES) + { + /* send another disconnect */ + + packet = net_new_packet(10); + net_write_int16(packet, NET_PACKET_TYPE_DISCONNECT); + net_conn_send_packet(conn, packet); + net_free_packet(packet); + conn->last_send_time = nowtime; + + ++conn->num_retries; + } + else + { + /* No more retries allowed. Force disconnect. */ + + conn->state = NET_CONN_STATE_DISCONNECTED; + conn->disconnect_reason = NET_DISCONNECT_LOCAL; + } + } + } + else if (conn->state == NET_CONN_STATE_DISCONNECTED_SLEEP) + { + /* We are disconnected, waiting in case we need to send + * a DISCONNECT_ACK to the server again. + */ + + if (nowtime - conn->last_send_time > 5000) + { + /* Idle for 5 seconds, switch state */ + + conn->state = NET_CONN_STATE_DISCONNECTED; + conn->disconnect_reason = NET_DISCONNECT_REMOTE; + } + } +} + +net_packet_t *net_conn_new_reliable(net_connection_t *conn, int packet_type) +{ + net_packet_t *packet; + net_reliable_packet_t *rp; + net_reliable_packet_t **listend; + + /* Generate a packet with the right header */ + + packet = net_new_packet(100); + + net_write_int16(packet, packet_type | NET_RELIABLE_PACKET); + + /* write the low byte of the send sequence number */ + + net_write_int8(packet, conn->reliable_send_seq & 0xff); + + /* Add to the list of reliable packets */ + + rp = malloc(sizeof(net_reliable_packet_t)); + rp->packet = packet; + rp->next = NULL; + rp->seq = conn->reliable_send_seq; + rp->last_send_time = -1; + + for (listend = &conn->reliable_packets; *listend != NULL; + listend = &((*listend)->next)) + ; + + *listend = rp; + + /* Count along the sequence */ + + conn->reliable_send_seq = (conn->reliable_send_seq + 1) & 0xff; + + /* Finished */ + + return packet; +} + +/* Used to expand the least significant byte of a tic number into + * the full tic number, from the current tic number + */ + +unsigned int net_expand_tic_num(unsigned int relative, unsigned int b) +{ + unsigned int l; + unsigned int h; + unsigned int result; + + h = relative & ~0xff; + l = relative & 0xff; + + result = h | b; + + if (l < 0x40 && b > 0xb0) result -= 0x100; + if (l > 0xb0 && b < 0x40) result += 0x100; + + return result; +} + +/* Check that game settings are valid */ + +boolean net_valid_game_settings(game_mode_t mode, gamemission_t mission, + net_gamesettings_t *settings) +{ + if (settings->ticdup <= 0) + { + return false; + } + + if (settings->extratics < 0) + { + return false; + } + + if (settings->deathmatch < 0 || settings->deathmatch > 2) + { + return false; + } + + if (settings->skill < sk_noitems || settings->skill > sk_nightmare) + { + return false; + } + + if (!d_valid_game_version(mission, settings->gameversion)) + { + return false; + } + + if (!d_valid_episode_map(mission, mode, settings->episode, settings->map)) + { + return false; + } + + return true; +} + +#ifdef CONFIG_GAMES_NXDOOM_NET_LOGS +void net_log_packet(net_packet_t *packet) +{ + int i; + int bytes; + + bytes = packet->len - packet->pos; + if (bytes == 0) + { + return; + } + + fprintf(stderr, "\t%02x", packet->data[packet->pos]); + + for (i = 1; i < bytes; ++i) + { + if ((i % 16) == 0) + { + fprintf(stderr, "\n\t"); + } + else + { + fprintf(stderr, " "); + } + + fprintf(stderr, "%02x", packet->data[packet->pos + i]); + } + + fprintf(stderr, "\n"); +} +#endif /* CONFIG_GAMES_NXDOOM_NET_LOGS */ diff --git a/games/NXDoom/src/net_common.h b/games/NXDoom/src/net_common.h new file mode 100644 index 00000000000..09770a1b2ba --- /dev/null +++ b/games/NXDoom/src/net_common.h @@ -0,0 +1,145 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_common.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Common code shared between the client and server + * + ****************************************************************************/ + +#ifndef NET_COMMON_H +#define NET_COMMON_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "d_mode.h" +#include "net_defs.h" +#include "net_packet.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_RETRIES 5 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef enum +{ + /* Client has sent a SYN, is waiting for a SYN in response. */ + + NET_CONN_STATE_CONNECTING, + + /* Successfully connected. */ + + NET_CONN_STATE_CONNECTED, + + /* Sent a DISCONNECT packet, waiting for a DISCONNECT_ACK reply */ + + NET_CONN_STATE_DISCONNECTING, + + /* Client successfully disconnected */ + + NET_CONN_STATE_DISCONNECTED, + + /* We are disconnected, but in a sleep state, waiting for several + * seconds. This is in case the DISCONNECT_ACK we sent failed + * to arrive, and we need to send another one. We keep this as + * a valid connection for a few seconds until we are sure that + * the other end has successfully disconnected as well. + */ + + NET_CONN_STATE_DISCONNECTED_SLEEP, +} net_connstate_t; + +/* Reason a connection was terminated */ + +typedef enum +{ + /* As the result of a local disconnect request */ + + NET_DISCONNECT_LOCAL, + + /* As the result of a remote disconnect request */ + + NET_DISCONNECT_REMOTE, + + /* Timeout (no data received in a long time) */ + + NET_DISCONNECT_TIMEOUT, +} net_disconnect_reason_t; + +typedef struct net_reliable_packet_s net_reliable_packet_t; + +typedef struct +{ + net_connstate_t state; + net_disconnect_reason_t disconnect_reason; + net_addr_t *addr; + net_protocol_t protocol; + int last_send_time; + int num_retries; + int keepalive_send_time; + int keepalive_recv_time; + net_reliable_packet_t *reliable_packets; + int reliable_send_seq; + int reliable_recv_seq; +} net_connection_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void net_conn_send_packet(net_connection_t *conn, net_packet_t *packet); +void net_conn_init_client(net_connection_t *conn, net_addr_t *addr, + net_protocol_t protocol); +void net_conn_init_server(net_connection_t *conn, net_addr_t *addr, + net_protocol_t protocol); +boolean net_conn_packet(net_connection_t *conn, net_packet_t *packet, + unsigned int *packet_type); +void net_conn_disconnect(net_connection_t *conn); +void net_conn_run(net_connection_t *conn); +net_packet_t *net_conn_new_reliable(net_connection_t *conn, int packet_type); + +/* Other miscellaneous common functions */ + +unsigned int net_expand_tic_num(unsigned int relative, unsigned int b); +boolean net_valid_game_settings(game_mode_t mode, gamemission_t mission, + net_gamesettings_t *settings); + +/* Conditional logging */ + +#ifdef CONFIG_GAMES_NXDOOM_NET_LOGS +#define net_log_info(fmt, ...) syslog(LOG_USER | LOG_INFO, fmt, ##__VA_ARGS__) +#define net_log_warn(fmt, ...) \ + syslog(LOG_USER | LOG_WARNING, fmt, ##__VA_ARGS__) +#define net_log_err(fmt, ...) syslog(LOG_USER | LOG_ERR, fmt, ##__VA_ARGS__) + +void net_log_packet(net_packet_t *packet); +#else +#define net_log_info(fmt, ...) +#define net_log_warn(fmt, ...) +#define net_log_err(fmt, ...) + +#define net_log_packet(pkt) +#endif /* CONFIG_GAMES_NXDOOM_NET_LOGS */ + +#endif /* NET_COMMON_H */ diff --git a/games/NXDoom/src/net_dedicated.c b/games/NXDoom/src/net_dedicated.c new file mode 100644 index 00000000000..8c59fb7bbf8 --- /dev/null +++ b/games/NXDoom/src/net_dedicated.c @@ -0,0 +1,101 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_dedicated.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Dedicated server code. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" + +#include "i_system.h" +#include "i_timer.h" + +#include "m_argv.h" + +#include "net_common.h" +#include "net_sdl.h" +#include "net_server.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* People can become confused about how dedicated servers work. Game + * options are specified to the controlling player who is the first to + * join a game. Bomb out with an error message if game options are + * specified to a dedicated server. + */ + +static const char *not_dedicated_options[] = +{ + "-deh", "-iwad", "-cdrom", "-gameversion", "-nomonsters", + "-respawn", "-fast", "-altdeath", "-deathmatch", "-turbo", + "-merge", "-af", "-as", "-aa", "-file", + "-wart", "-skill", "-episode", "-timer", "-avg", + "-warp", "-loadgame", "-longtics", "-extratics", "-dup", + "-shorttics", NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void check_for_client_options(void) +{ + int i; + + for (i = 0; not_dedicated_options[i] != NULL; ++i) + { + if (m_check_parm(not_dedicated_options[i]) > 0) + { + i_error("The command line parameter '%s' was specified to a " + "dedicated server.\nGame parameters should be specified " + "to the first player to join a server, \nnot to the " + "server itself. ", + not_dedicated_options[i]); + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void net_dedicated_server(void) +{ + check_for_client_options(); + + net_sv_init(); + net_sv_add_module(&net_sdl_module); + net_sv_register_with_master(); + + while (true) + { + net_sv_run(); + + /* TODO: Block on socket instead of polling. */ + + usleep(1000); + } +} diff --git a/games/NXDoom/src/net_dedicated.h b/games/NXDoom/src/net_dedicated.h new file mode 100644 index 00000000000..2f87bc5d59e --- /dev/null +++ b/games/NXDoom/src/net_dedicated.h @@ -0,0 +1,32 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_dedicated.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Dedicated server code. + * + ****************************************************************************/ + +#ifndef NET_DEDICATED_H +#define NET_DEDICATED_H + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void net_dedicated_server(void); + +#endif /* NET_DEDICATED_H */ diff --git a/games/NXDoom/src/net_defs.h b/games/NXDoom/src/net_defs.h new file mode 100644 index 00000000000..be735863029 --- /dev/null +++ b/games/NXDoom/src/net_defs.h @@ -0,0 +1,323 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_defs.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Definitions for use in networking code. + * + ****************************************************************************/ + +#ifndef NET_DEFS_H +#define NET_DEFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "d_ticcmd.h" +#include "doomtype.h" +#include "sha1.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Absolute maximum number of "nodes" in the game. This is different to + * NET_MAXPLAYERS, as there may be observers that are not participating + * (eg. left/right monitors) + */ + +#define MAXNETNODES 16 + +#ifndef CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME + +/* We don't need player names. TODO: I should be smarter about this + * and avoid compiling ANY networking related stuff (i.e. player name + * members in the structs below) at all when net stuff is not enabled. + */ + +#define CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME (1) +#endif /* CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME */ + +/* The maximum number of players, multiplayer/networking. + * This is the maximum supported by the networking code; individual games + * have their own values for MAXPLAYERS that can be smaller. + */ + +#ifndef CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS + +/* No players if not set up for networking */ + +#define CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS (1) +#endif /* CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS */ + +/* Networking and tick handling related. */ + +#ifndef CONFIG_GAMES_NXDOOM_NET_BACKUPTICS + +/* Surely we don't need any for a local game? */ + +#define CONFIG_GAMES_NXDOOM_NET_BACKUPTICS (1) +#endif /* CONFIG_GAMES_NXDOOM_NET_BACKUPTICS */ + +/* Magic number sent when connecting to check this is a valid client */ + +#define NET_MAGIC_NUMBER 1454104972U + +/* Old magic number used by Chocolate Doom versions before v3.0: */ + +#define NET_OLD_MAGIC_NUMBER 3436803284U + +/* header field value indicating that the packet is a reliable packet */ + +#define NET_RELIABLE_PACKET (1 << 15) + +#define NET_TICDIFF_FORWARD (1 << 0) +#define NET_TICDIFF_SIDE (1 << 1) +#define NET_TICDIFF_TURN (1 << 2) +#define NET_TICDIFF_BUTTONS (1 << 3) +#define NET_TICDIFF_CONSISTANCY (1 << 4) +#define NET_TICDIFF_CHATCHAR (1 << 5) +#define NET_TICDIFF_RAVEN (1 << 6) +#define NET_TICDIFF_STRIFE (1 << 7) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct _net_module_s net_module_t; +typedef struct _net_packet_s net_packet_t; +typedef struct _net_addr_s net_addr_t; +typedef struct _net_context_s net_context_t; + +struct _net_packet_s +{ + byte *data; + size_t len; + size_t alloced; + unsigned int pos; +}; + +struct _net_module_s +{ + /* Initialize this module for use as a client */ + + boolean (*init_client)(void); + + /* Initialize this module for use as a server */ + + boolean (*init_server)(void); + + /* Send a packet */ + + void (*send_packet)(net_addr_t *addr, net_packet_t *packet); + + /* Check for new packets to receive + * + * Returns true if packet received + */ + + boolean (*recv_packet)(net_addr_t **addr, net_packet_t **packet); + + /* Converts an address to a string */ + + void (*addr_to_string)(net_addr_t *addr, char *buffer, int buffer_len); + + /* Free back an address when no longer in use */ + + void (*free_address)(net_addr_t *addr); + + /* Try to resolve a name to an address */ + + net_addr_t *(*resolve_address)(const char *addr); +}; + +/* net_addr_t */ + +struct _net_addr_s +{ + net_module_t *module; + int refcount; + void *handle; +}; + +/* Supported protocols. If you're developing a fork of Chocolate + * Doom, you can add your own entry to this list while maintaining + * compatibility with Chocolate Doom servers. Higher-numbered enum values + * will be preferred when negotiating a protocol for the client and server + * to use, so the order matters. + * NOTE: The values in this enum do not have any special value outside of + * the program they're compiled in. What matters is the string + * representation. + */ + +typedef enum +{ + /* Protocol introduced with Chocolate Doom v3.0. Each compatibility- + * breaking change to the network protocol will produce a new protocol + * number in this enum. + */ + + NET_PROTOCOL_CHOCOLATE_DOOM_0, + + /* Add your own protocol here; be sure to add a name for it to the list + * in net_common.c too. + */ + + NET_NUM_PROTOCOLS, + NET_PROTOCOL_UNKNOWN, +} net_protocol_t; + +/* packet types */ + +typedef enum +{ + NET_PACKET_TYPE_SYN, + NET_PACKET_TYPE_ACK, /* deprecated */ + NET_PACKET_TYPE_REJECTED, + NET_PACKET_TYPE_KEEPALIVE, + NET_PACKET_TYPE_WAITING_DATA, + NET_PACKET_TYPE_GAMESTART, + NET_PACKET_TYPE_GAMEDATA, + NET_PACKET_TYPE_GAMEDATA_ACK, + NET_PACKET_TYPE_DISCONNECT, + NET_PACKET_TYPE_DISCONNECT_ACK, + NET_PACKET_TYPE_RELIABLE_ACK, + NET_PACKET_TYPE_GAMEDATA_RESEND, + NET_PACKET_TYPE_CONSOLE_MESSAGE, + NET_PACKET_TYPE_QUERY, + NET_PACKET_TYPE_QUERY_RESPONSE, + NET_PACKET_TYPE_LAUNCH, + NET_PACKET_TYPE_NAT_HOLE_PUNCH, +} net_packet_type_t; + +typedef enum +{ + NET_MASTER_PACKET_TYPE_ADD, + NET_MASTER_PACKET_TYPE_ADD_RESPONSE, + NET_MASTER_PACKET_TYPE_QUERY, + NET_MASTER_PACKET_TYPE_QUERY_RESPONSE, + NET_MASTER_PACKET_TYPE_GET_METADATA, + NET_MASTER_PACKET_TYPE_GET_METADATA_RESPONSE, + NET_MASTER_PACKET_TYPE_SIGN_START, + NET_MASTER_PACKET_TYPE_SIGN_START_RESPONSE, + NET_MASTER_PACKET_TYPE_SIGN_END, + NET_MASTER_PACKET_TYPE_SIGN_END_RESPONSE, + NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH, + NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH_ALL, +} net_master_packet_type_t; + +/* Settings specified when the client connects to the server. */ + +typedef struct +{ + int gamemode; + int gamemission; + int lowres_turn; + int drone; + int max_players; + int is_freedoom; + sha1_digest_t wad_sha1sum; + sha1_digest_t deh_sha1sum; + int player_class; +} net_connect_data_t; + +/* Game settings sent by client to server when initiating game start, + * and received from the server by clients when the game starts. + */ + +typedef struct +{ + int ticdup; + int extratics; + int deathmatch; + int episode; + int nomonsters; + int fast_monsters; + int respawn_monsters; + int map; + int skill; + int gameversion; + int lowres_turn; + int new_sync; + int timelimit; + int loadgame; + int random; /* [Strife only] */ + + /* These fields are only used by the server when sending a game + * start message: + */ + + int num_players; + int consoleplayer; + + /* Hexen player classes: */ + + int player_classes[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +} net_gamesettings_t; + +typedef struct +{ + unsigned int diff; + ticcmd_t cmd; +} net_ticdiff_t; + +/* Complete set of ticcmds from all players */ + +typedef struct +{ + signed int latency; + unsigned int seq; + boolean playeringame[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; + net_ticdiff_t cmds[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +} net_full_ticcmd_t; + +/* Data sent in response to server queries */ + +typedef struct +{ + const char *version; + int server_state; + int num_players; + int max_players; + int gamemode; + int gamemission; + const char *description; + net_protocol_t protocol; +} net_querydata_t; + +/* Data sent by the server while waiting for the game to start. */ + +typedef struct +{ + int num_players; + int num_drones; + int ready_players; + int max_players; + int is_controller; + int consoleplayer; + char player_names[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS] + [CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME]; + char player_addrs[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS] + [CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME]; + sha1_digest_t wad_sha1sum; + sha1_digest_t deh_sha1sum; + int is_freedoom; +} net_waitdata_t; + +#endif /* NET_DEFS_H */ diff --git a/games/NXDoom/src/net_gui.c b/games/NXDoom/src/net_gui.c new file mode 100644 index 00000000000..f731693caf7 --- /dev/null +++ b/games/NXDoom/src/net_gui.c @@ -0,0 +1,469 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_gui.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Graphical stuff related to the networking code: + * + * * The client waiting screen when we are waiting for the server to + * start the game. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "config.h" +#include "doomkeys.h" + +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_misc.h" + +#include "net_client.h" +#include "net_gui.h" +#include "net_query.h" +#include "net_server.h" + +#include "textscreen.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static txt_window_t *window; +static int old_max_players; +static txt_label_t *player_labels[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +static txt_label_t *ip_labels[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +static txt_label_t *drone_label; +static txt_label_t *master_msg_label; +static boolean had_warning; + +/* Number of players we expect to be in the game. When the number is + * reached, we auto-start the game (if we're the controller). If + * zero, do not autostart. + */ + +static int expected_nodes; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void escape_pressed(TXT_UNCAST_ARG(widget), void *unused) +{ + txt_shutdown(); + i_quit(); +} + +static void start_game(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) +{ + net_cl_launch_game(); +} + +static void open_wait_dialog(void) +{ + txt_window_action_t *cancel; + + txt_set_desktop_title(PACKAGE_STRING); + + window = txt_new_window("Waiting for game start..."); + + txt_add_widget(window, txt_new_label("\nPlease wait...\n\n")); + + cancel = txt_new_window_action(KEY_ESCAPE, "Cancel"); + txt_signal_connect(cancel, "pressed", escape_pressed, NULL); + + txt_set_window_action(window, TXT_HORIZ_LEFT, cancel); + txt_set_window_position(window, TXT_HORIZ_CENTER, TXT_VERT_BOTTOM, + TXT_SCREEN_W / 2, TXT_SCREEN_H - 9); + + old_max_players = 0; +} + +static void build_window(void) +{ + char buf[50]; + txt_table_t *table; + int i; + + txt_clear_table(window); + table = txt_new_table(3); + txt_add_widget(window, table); + + /* Add spacers */ + + txt_add_widget(table, NULL); + txt_add_widget(table, txt_new_strut(25, 1)); + txt_add_widget(table, txt_new_strut(17, 1)); + + /* Player labels */ + + for (i = 0; i < net_client_wait_data.max_players; ++i) + { + snprintf(buf, sizeof(buf), " %i. ", i + 1); + txt_add_widget(table, txt_new_label(buf)); + player_labels[i] = txt_new_label(""); + ip_labels[i] = txt_new_label(""); + txt_add_widget(table, player_labels[i]); + txt_add_widget(table, ip_labels[i]); + } + + drone_label = txt_new_label(""); + + txt_add_widget(window, drone_label); +} + +static void update_gui(void) +{ + txt_window_action_t *startgame; + char buf[50]; + unsigned int i; + + /* If the value of max_players changes, we must rebuild the + * contents of the window. This includes when the first + * waiting data packet is received. + */ + + if (net_client_received_wait_data) + { + if (net_client_wait_data.max_players != old_max_players) + { + build_window(); + } + } + else + { + return; + } + + for (i = 0; i < net_client_wait_data.max_players; ++i) + { + txt_color_t color = TXT_COLOR_BRIGHT_WHITE; + + if ((signed)i == net_client_wait_data.consoleplayer) + { + color = TXT_COLOR_YELLOW; + } + + txt_set_fg_colour(player_labels[i], color); + txt_set_fg_colour(ip_labels[i], color); + + if (i < net_client_wait_data.num_players) + { + txt_set_label(player_labels[i], + net_client_wait_data.player_names[i]); + txt_set_label(ip_labels[i], net_client_wait_data.player_addrs[i]); + } + else + { + txt_set_label(player_labels[i], ""); + txt_set_label(ip_labels[i], ""); + } + } + + if (net_client_wait_data.num_drones > 0) + { + snprintf(buf, sizeof(buf), " (+%i observer clients)", + net_client_wait_data.num_drones); + txt_set_label(drone_label, buf); + } + else + { + txt_set_label(drone_label, ""); + } + + if (net_client_wait_data.is_controller) + { + startgame = txt_new_window_action(' ', "Start game"); + txt_signal_connect(startgame, "pressed", start_game, NULL); + } + else + { + startgame = NULL; + } + + txt_set_window_action(window, TXT_HORIZ_RIGHT, startgame); +} + +static void build_master_status_window(void) +{ + txt_window_t *master_window; + + master_window = txt_new_window(NULL); + master_msg_label = txt_new_label(""); + txt_add_widget(master_window, master_msg_label); + + /* This window is here purely for information, so it should be + * in the background. + */ + + txt_lower_window(master_window); + txt_set_window_position(master_window, TXT_HORIZ_CENTER, TXT_VERT_CENTER, + TXT_SCREEN_W / 2, TXT_SCREEN_H - 4); + txt_set_window_action(master_window, TXT_HORIZ_LEFT, NULL); + txt_set_window_action(master_window, TXT_HORIZ_CENTER, NULL); + txt_set_window_action(master_window, TXT_HORIZ_RIGHT, NULL); +} + +static void check_master_status(void) +{ + boolean added; + + if (!net_query_check_added_to_master(&added)) + { + return; + } + + if (master_msg_label == NULL) + { + build_master_status_window(); + } + + if (added) + { + txt_set_label( + master_msg_label, + "Your server is now registered with the global master server.\n" + "Other players can find your server online."); + } + else + { + txt_set_label( + master_msg_label, + "Failed to register with the master server. Your server is not\n" + "publicly accessible. You may need to reconfigure your Internet\n" + "router to add a port forward for UDP port 2342. Look up\n" + "information on port forwarding online."); + } +} + +static void print_sha1_digest(const char *s, const byte *digest) +{ + unsigned int i; + + printf("%s: ", s); + + for (i = 0; i < sizeof(sha1_digest_t); ++i) + { + printf("%02x", digest[i]); + } + + printf("\n"); +} + +static void close_window(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(p_window)) +{ + TXT_CAST_ARG(txt_window_t, p_window); + + txt_close_window(p_window); +} + +static void check_sha1_sums(void) +{ + boolean correct_wad; + boolean correct_deh; + boolean same_freedoom; + txt_window_t *l_window; + txt_window_action_t *cont_button; + + if (!net_client_received_wait_data || had_warning) + { + return; + } + + correct_wad = + memcmp(net_local_wad_sha1sum, net_client_wait_data.wad_sha1sum, + sizeof(sha1_digest_t)) == 0; + correct_deh = + memcmp(net_local_deh_sha1sum, net_client_wait_data.deh_sha1sum, + sizeof(sha1_digest_t)) == 0; + same_freedoom = net_client_wait_data.is_freedoom == net_local_is_freedoom; + + if (correct_wad && correct_deh && same_freedoom) + { + return; + } + + if (!correct_wad) + { + printf("Warning: WAD SHA1 does not match server:\n"); + print_sha1_digest("Local", net_local_wad_sha1sum); + print_sha1_digest("Server", net_client_wait_data.wad_sha1sum); + } + + if (!same_freedoom) + { + printf("Warning: Mixing Freedoom with non-Freedoom\n"); + printf("Local: %u Server: %i\n", net_local_is_freedoom, + net_client_wait_data.is_freedoom); + } + + if (!correct_deh) + { + printf("Warning: Dehacked SHA1 does not match server:\n"); + print_sha1_digest("Local", net_local_deh_sha1sum); + print_sha1_digest("Server", net_client_wait_data.deh_sha1sum); + } + + l_window = txt_new_window("WARNING!"); + + cont_button = txt_new_window_action(KEY_ENTER, "Continue"); + txt_signal_connect(cont_button, "pressed", close_window, l_window); + + txt_set_window_action(l_window, TXT_HORIZ_LEFT, NULL); + txt_set_window_action(l_window, TXT_HORIZ_CENTER, cont_button); + txt_set_window_action(l_window, TXT_HORIZ_RIGHT, NULL); + + if (!same_freedoom) + { + /* If Freedoom and Doom IWADs are mixed, the WAD directory + * will be wrong, but this is not necessarily a problem. + * Display a different message to the WAD directory message. + */ + + if (net_local_is_freedoom) + { + txt_add_widget( + l_window, + txt_new_label( + "You are using the Freedoom IWAD to play with players\n" + "using an official Doom IWAD. Make sure that you are\n" + "playing the same levels as other players.\n")); + } + else + { + txt_add_widget( + l_window, + txt_new_label( + "You are using an official IWAD to play with players\n" + "using the Freedoom IWAD. Make sure that you are\n" + "playing the same levels as other players.\n")); + } + } + else if (!correct_wad) + { + txt_add_widget( + l_window, + txt_new_label( + "Your WAD directory does not match other players in the game.\n" + "Check that you have loaded the exact same WAD files as other\n" + "players.\n") + ); + } + + if (!correct_deh) + { + txt_add_widget( + l_window, + txt_new_label( + "Your dehacked signature does not match other players in the\n" + "game. Check that you have loaded the same dehacked patches\n" + "as other players.\n")); + } + + txt_add_widget( + l_window, + txt_new_label("If you continue, this may cause your game to desync.")); + + had_warning = true; +} + +static void parse_command_line_args(void) +{ + int i; + + /* @arg + * @category net + * + * Autostart the netgame when n nodes (clients) have joined the server. + */ + + i = m_check_parm_with_args("-nodes", 1); + if (i > 0) + { + expected_nodes = atoi(myargv[i + 1]); + } +} + +static void check_auto_latch(void) +{ + int nodes; + + if (net_client_received_wait_data && net_client_wait_data.is_controller && + expected_nodes > 0) + { + nodes = + net_client_wait_data.num_players + net_client_wait_data.num_drones; + + if (nodes >= expected_nodes) + { + start_game(NULL, NULL); + expected_nodes = 0; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void net_wait_for_launch(void) +{ + if (!txt_init()) + { + fprintf(stderr, "Failed to initialize GUI\n"); + exit(-1); + } + + /* Romero's "funky blue" color */ + + txt_set_colour(TXT_COLOR_BLUE, 0x04, 0x14, 0x40); + + parse_command_line_args(); + open_wait_dialog(); + had_warning = false; + + while (net_waiting_for_launch) + { + update_gui(); + check_auto_latch(); + check_sha1_sums(); + check_master_status(); + + txt_dispatch_events(); + txt_draw_desktop(); + + net_cl_run(); + net_sv_run(); + + if (!net_client_connected) + { + i_error("Lost connection to server"); + } + + txt_sleep(100); + } + + txt_shutdown(); +} diff --git a/games/NXDoom/src/net_gui.h b/games/NXDoom/src/net_gui.h new file mode 100644 index 00000000000..377b6646c67 --- /dev/null +++ b/games/NXDoom/src/net_gui.h @@ -0,0 +1,40 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_gui.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Graphical stuff related to the networking code: + * + * * The client waiting screen when we are waiting for the server to + * start the game. + * + ****************************************************************************/ + +#ifndef NET_GUI_H +#define NET_GUI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +extern void net_wait_for_launch(void); + +#endif /* NET_GUI_H */ diff --git a/games/NXDoom/src/net_io.c b/games/NXDoom/src/net_io.c new file mode 100644 index 00000000000..0c9c4a4fb12 --- /dev/null +++ b/games/NXDoom/src/net_io.c @@ -0,0 +1,170 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_io.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Network packet I/O. Base layer for sending/receiving packets, + * through the network module system + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "i_system.h" +#include "net_defs.h" +#include "net_io.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_MODULES 16 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct _net_context_s +{ + net_module_t *modules[MAX_MODULES]; + int num_modules; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +net_addr_t net_broadcast_addr; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +net_context_t *net_new_context(void) +{ + net_context_t *context; + + context = z_malloc(sizeof(net_context_t), PU_STATIC, 0); + context->num_modules = 0; + + return context; +} + +void net_add_module(net_context_t *context, net_module_t *module) +{ + if (context->num_modules >= MAX_MODULES) + { + i_error("net_add_module: No more modules for context"); + } + + context->modules[context->num_modules] = module; + ++context->num_modules; +} + +net_addr_t *net_resolve_address(net_context_t *context, const char *addr) +{ + int i; + net_addr_t *result; + + for (i = 0; i < context->num_modules; ++i) + { + result = context->modules[i]->resolve_address(addr); + + if (result != NULL) + { + net_reference_address(result); + return result; + } + } + + return NULL; +} + +void net_send_packet(net_addr_t *addr, net_packet_t *packet) +{ + addr->module->send_packet(addr, packet); +} + +void net_send_broadcast(net_context_t *context, net_packet_t *packet) +{ + int i; + + for (i = 0; i < context->num_modules; ++i) + { + context->modules[i]->send_packet(&net_broadcast_addr, packet); + } +} + +boolean net_recv_packet(net_context_t *context, net_addr_t **addr, + net_packet_t **packet) +{ + int i; + + /* check all modules for new packets */ + + for (i = 0; i < context->num_modules; ++i) + { + if (context->modules[i]->recv_packet(addr, packet)) + { + net_reference_address(*addr); + return true; + } + } + + return false; +} + +/* Note: this prints into a static buffer, calling again overwrites + * the first result + */ + +char *net_addr_to_string(net_addr_t *addr) +{ + static char buf[128]; + + addr->module->addr_to_string(addr, buf, sizeof(buf) - 1); + + return buf; +} + +void net_reference_address(net_addr_t *addr) +{ + if (addr == NULL) + { + return; + } + + ++addr->refcount; +} + +void net_release_address(net_addr_t *addr) +{ + if (addr == NULL) + { + return; + } + + --addr->refcount; + if (addr->refcount <= 0) + { + addr->module->free_address(addr); + } +} diff --git a/games/NXDoom/src/net_io.h b/games/NXDoom/src/net_io.h new file mode 100644 index 00000000000..d83d3ace369 --- /dev/null +++ b/games/NXDoom/src/net_io.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_io.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Network packet manipulation (net_packet_t) + * + ****************************************************************************/ + +#ifndef NET_IO_H +#define NET_IO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_defs.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern net_addr_t net_broadcast_addr; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Create a new network context. */ + +net_context_t *net_new_context(void); + +/* Add a network module to a context. */ + +void net_add_module(net_context_t *context, net_module_t *module); + +/* Send a packet to the given address. */ + +void net_send_packet(net_addr_t *addr, net_packet_t *packet); + +/* Send a broadcast using all modules in the given context. */ + +void net_send_broadcast(net_context_t *context, net_packet_t *packet); + +/* Check all modules in the given context and receive a packet, returning + * true if a packet was received. The result is stored in *packet and the + * source is stored in *addr, with an implicit reference added. The packet + * must be freed by the caller and the reference released. + */ + +boolean net_recv_packet(net_context_t *context, net_addr_t **addr, + net_packet_t **packet); + +/* Return a string representation of the given address. The result points + * to a static buffer and will become invalid with the next call. + */ + +char *net_addr_to_string(net_addr_t *addr); + +/* Add a reference to the given address. */ + +void net_reference_address(net_addr_t *addr); + +/* Release a reference to the given address. When there are no more + * references, the address will be freed. + */ + +void net_release_address(net_addr_t *addr); + +/* Resolve a string representation of an address. If successful, a net_addr_t + * pointer is received with an implicit reference that must be freed by the + * caller when it is no longer needed. + */ + +net_addr_t *net_resolve_address(net_context_t *context, const char *address); + +#endif /* NET_IO_H */ diff --git a/games/NXDoom/src/net_loop.c b/games/NXDoom/src/net_loop.c new file mode 100644 index 00000000000..d31d29cfc33 --- /dev/null +++ b/games/NXDoom/src/net_loop.c @@ -0,0 +1,272 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_loop.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Loopback network module for server compiled into the client + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_misc.h" +#include "net_defs.h" +#include "net_loop.h" +#include "net_packet.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_QUEUE_SIZE 16 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + net_packet_t *packets[MAX_QUEUE_SIZE]; + int head; + int tail; +} packet_queue_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static packet_queue_t client_queue; +static packet_queue_t server_queue; +static net_addr_t client_addr; +static net_addr_t server_addr; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static boolean net_cl_init_client(void); +static boolean net_cl_init_server(void); +static void net_cl_send_packet(net_addr_t *addr, net_packet_t *packet); +static boolean net_cl_recv_packet(net_addr_t **addr, net_packet_t **packet); +static void net_cl_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len); +static void net_cl_free_address(net_addr_t *addr); +static net_addr_t *net_cl_resolve_address(const char *address); + +static boolean net_sv_init_client(void); +static boolean net_sv_init_server(void); +static void net_sv_send_packet(net_addr_t *addr, net_packet_t *packet); +static boolean net_sv_recv_packet(net_addr_t **addr, net_packet_t **packet); +static void net_sv_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len); +static void net_sv_free_address(net_addr_t *addr); +static net_addr_t *net_sv_resolve_address(const char *address); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +net_module_t net_loop_client_module = +{ + net_cl_init_client, net_cl_init_server, net_cl_send_packet, + net_cl_recv_packet, net_cl_addr_to_string, net_cl_free_address, + net_cl_resolve_address, +}; + +net_module_t net_loop_server_module = +{ + net_sv_init_client, net_sv_init_server, net_sv_send_packet, + net_sv_recv_packet, net_sv_addr_to_string, net_sv_free_address, + net_sv_resolve_address, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void queue_init(packet_queue_t *queue) +{ + queue->head = queue->tail = 0; +} + +static void queue_push(packet_queue_t *queue, net_packet_t *packet) +{ + int new_tail; + + new_tail = (queue->tail + 1) % MAX_QUEUE_SIZE; + + if (new_tail == queue->head) + { + /* queue is full */ + + return; + } + + queue->packets[queue->tail] = packet; + queue->tail = new_tail; +} + +static net_packet_t *queue_pop(packet_queue_t *queue) +{ + net_packet_t *packet; + + if (queue->tail == queue->head) + { + /* queue empty */ + + return NULL; + } + + packet = queue->packets[queue->head]; + queue->head = (queue->head + 1) % MAX_QUEUE_SIZE; + + return packet; +} + +/**************************************************************************** + * Client-end code + ****************************************************************************/ + +static boolean net_cl_init_client(void) +{ + queue_init(&client_queue); + + return true; +} + +static boolean net_cl_init_server(void) +{ + i_error("NET_CL_init_server: attempted to initialize client pipe end as a " + "server!"); + return false; +} + +static void net_cl_send_packet(net_addr_t *addr, net_packet_t *packet) +{ + queue_push(&server_queue, net_packet_dup(packet)); +} + +static boolean net_cl_recv_packet(net_addr_t **addr, net_packet_t **packet) +{ + net_packet_t *popped; + + popped = queue_pop(&client_queue); + + if (popped != NULL) + { + *packet = popped; + *addr = &client_addr; + client_addr.module = &net_loop_client_module; + + return true; + } + + return false; +} + +static void net_cl_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len) +{ + snprintf(buffer, buffer_len, "local server"); +} + +static void net_cl_free_address(net_addr_t *addr) +{ +} + +static net_addr_t *net_cl_resolve_address(const char *address) +{ + if (address == NULL) + { + client_addr.module = &net_loop_client_module; + + return &client_addr; + } + else + { + return NULL; + } +} + +/**************************************************************************** + * Server-end code + ****************************************************************************/ + +static boolean net_sv_init_client(void) +{ + i_error("net_sv_initClient: attempted to initialize server pipe end as a " + "client!"); + return false; +} + +static boolean net_sv_init_server(void) +{ + queue_init(&server_queue); + return true; +} + +static void net_sv_send_packet(net_addr_t *addr, net_packet_t *packet) +{ + queue_push(&client_queue, net_packet_dup(packet)); +} + +static boolean net_sv_recv_packet(net_addr_t **addr, net_packet_t **packet) +{ + net_packet_t *popped; + + popped = queue_pop(&server_queue); + + if (popped != NULL) + { + *packet = popped; + *addr = &server_addr; + server_addr.module = &net_loop_server_module; + + return true; + } + + return false; +} + +static void net_sv_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len) +{ + snprintf(buffer, buffer_len, "local client"); +} + +static void net_sv_free_address(net_addr_t *addr) +{ +} + +static net_addr_t *net_sv_resolve_address(const char *address) +{ + if (address == NULL) + { + server_addr.module = &net_loop_server_module; + return &server_addr; + } + else + { + return NULL; + } +} diff --git a/games/NXDoom/src/net_loop.h b/games/NXDoom/src/net_loop.h new file mode 100644 index 00000000000..222660ffc94 --- /dev/null +++ b/games/NXDoom/src/net_loop.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_loop.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Loopback network module for server compiled into the client + * + ****************************************************************************/ + +#ifndef NET_LOOP_H +#define NET_LOOP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_defs.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern net_module_t net_loop_client_module; +extern net_module_t net_loop_server_module; + +#endif /* NET_LOOP_H */ diff --git a/games/NXDoom/src/net_packet.c b/games/NXDoom/src/net_packet.c new file mode 100644 index 00000000000..d7a57e1e2b3 --- /dev/null +++ b/games/NXDoom/src/net_packet.c @@ -0,0 +1,357 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_packet.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Network packet manipulation (net_packet_t) + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_packet.h" +#include "m_misc.h" +#include "z_zone.h" +#include +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_total_packet_memory = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Dynamically increases the size of a packet */ + +static void net_increase_packet(net_packet_t *packet) +{ + byte *newdata; + + g_total_packet_memory -= packet->alloced; + + packet->alloced *= 2; + + newdata = z_malloc(packet->alloced, PU_STATIC, 0); + + memcpy(newdata, packet->data, packet->len); + + z_free(packet->data); + packet->data = newdata; + + g_total_packet_memory += packet->alloced; +} + +#if 0 /* Unused */ +static boolean net_read_sint32(net_packet_t *packet, signed int *data) +{ + if (net_read_int32(packet, (unsigned int *)data)) + { + if (*data & (1U << 31)) + { + *data &= ~(1U << 31); + *data -= (1U << 31); + } + + return true; + } + else + { + return false; + } +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +net_packet_t *net_new_packet(int initial_size) +{ + net_packet_t *packet; + + packet = (net_packet_t *)z_malloc(sizeof(net_packet_t), PU_STATIC, 0); + + if (initial_size == 0) initial_size = 256; + + packet->alloced = initial_size; + packet->data = z_malloc(initial_size, PU_STATIC, 0); + packet->len = 0; + packet->pos = 0; + + g_total_packet_memory += sizeof(net_packet_t) + initial_size; + + /* printf("total packet memory: %i bytes\n", total_packet_memory); + * printf("%p: allocated\n", packet); + */ + + return packet; +} + +/* duplicates an existing packet */ + +net_packet_t *net_packet_dup(net_packet_t *packet) +{ + net_packet_t *newpacket; + + newpacket = net_new_packet(packet->len); + memcpy(newpacket->data, packet->data, packet->len); + newpacket->len = packet->len; + + return newpacket; +} + +void net_free_packet(net_packet_t *packet) +{ + /* printf("%p: destroyed\n", packet); */ + + g_total_packet_memory -= sizeof(net_packet_t) + packet->alloced; + z_free(packet->data); + z_free(packet); +} + +/* Read a byte from the packet, returning true if read + * successfully + */ + +boolean net_read_int8(net_packet_t *packet, unsigned int *data) +{ + if (packet->pos + 1 > packet->len) return false; + + *data = packet->data[packet->pos]; + + packet->pos += 1; + + return true; +} + +/* Read a 16-bit integer from the packet, returning true if read + * successfully + */ + +boolean net_read_int16(net_packet_t *packet, unsigned int *data) +{ + byte *p; + + if (packet->pos + 2 > packet->len) return false; + + p = packet->data + packet->pos; + + *data = (p[0] << 8) | p[1]; + packet->pos += 2; + + return true; +} + +/* Read a 32-bit integer from the packet, returning true if read + * successfully + */ + +boolean net_read_int32(net_packet_t *packet, unsigned int *data) +{ + byte *p; + + if (packet->pos + 4 > packet->len) return false; + + p = packet->data + packet->pos; + + *data = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + packet->pos += 4; + + return true; +} + +/* Signed read functions */ + +boolean net_read_sint8(net_packet_t *packet, signed int *data) +{ + if (net_read_int8(packet, (unsigned int *)data)) + { + if (*data & (1 << 7)) + { + *data &= ~(1 << 7); + *data -= (1 << 7); + } + + return true; + } + else + { + return false; + } +} + +boolean net_read_sint16(net_packet_t *packet, signed int *data) +{ + if (net_read_int16(packet, (unsigned int *)data)) + { + if (*data & (1 << 15)) + { + *data &= ~(1 << 15); + *data -= (1 << 15); + } + + return true; + } + else + { + return false; + } +} + +/* Read a string from the packet. Returns NULL if a terminating + * NUL character was not found before the end of the packet. + */ + +char *net_read_string(net_packet_t *packet) +{ + char *start; + + start = (char *)packet->data + packet->pos; + + /* Search forward for a NUL character */ + + while (packet->pos < packet->len && packet->data[packet->pos] != '\0') + { + ++packet->pos; + } + + if (packet->pos >= packet->len) + { + /* Reached the end of the packet */ + + return NULL; + } + + /* packet->data[packet->pos] == '\0': We have reached a terminating + * NULL. Skip past this NULL and continue reading immediately + * after it. + */ + + ++packet->pos; + + return start; +} + +/* Read a string from the packet, but (potentially) modify it to strip + * out any unprintable characters which could be malicious control codes. + * Note that this may modify the original packet contents. + */ + +char *net_read_safe_string(net_packet_t *packet) +{ + char *r; + char *w; + char *result; + + result = net_read_string(packet); + if (result == NULL) + { + return NULL; + } + + /* w is always <= r, so we never produce a longer string than the original. + */ + + w = result; + for (r = result; *r != '\0'; ++r) + { + /* TODO: This is a very naive way of producing a safe string; only + * ASCII characters are allowed. Probably this should really support + * UTF-8 characters as well. + */ + + if (isprint(*r) || *r == '\n') + { + *w = *r; + ++w; + } + } + + *w = '\0'; + + return result; +} + +/* Write a single byte to the packet */ + +void net_write_int8(net_packet_t *packet, unsigned int i) +{ + if (packet->len + 1 > packet->alloced) net_increase_packet(packet); + + packet->data[packet->len] = i; + packet->len += 1; +} + +/* Write a 16-bit integer to the packet */ + +void net_write_int16(net_packet_t *packet, unsigned int i) +{ + byte *p; + + if (packet->len + 2 > packet->alloced) net_increase_packet(packet); + + p = packet->data + packet->len; + + p[0] = (i >> 8) & 0xff; + p[1] = i & 0xff; + + packet->len += 2; +} + +/* Write a single byte to the packet */ + +void net_write_int32(net_packet_t *packet, unsigned int i) +{ + byte *p; + + if (packet->len + 4 > packet->alloced) net_increase_packet(packet); + + p = packet->data + packet->len; + + p[0] = (i >> 24) & 0xff; + p[1] = (i >> 16) & 0xff; + p[2] = (i >> 8) & 0xff; + p[3] = i & 0xff; + + packet->len += 4; +} + +void net_write_string(net_packet_t *packet, const char *string) +{ + byte *p; + size_t string_size; + + string_size = strlen(string) + 1; + + /* Increase the packet size until large enough to hold the string */ + + while (packet->len + string_size > packet->alloced) + { + net_increase_packet(packet); + } + + p = packet->data + packet->len; + + m_str_copy((char *)p, string, string_size); + + packet->len += string_size; +} diff --git a/games/NXDoom/src/net_packet.h b/games/NXDoom/src/net_packet.h new file mode 100644 index 00000000000..f2d412b95e3 --- /dev/null +++ b/games/NXDoom/src/net_packet.h @@ -0,0 +1,56 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_packet.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Definitions for use in networking code. + * + ****************************************************************************/ + +#ifndef NET_PACKET_H +#define NET_PACKET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_defs.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +net_packet_t *net_new_packet(int initial_size); +net_packet_t *net_packet_dup(net_packet_t *packet); +void net_free_packet(net_packet_t *packet); + +boolean net_read_int8(net_packet_t *packet, unsigned int *data); +boolean net_read_int16(net_packet_t *packet, unsigned int *data); +boolean net_read_int32(net_packet_t *packet, unsigned int *data); + +boolean net_read_sint8(net_packet_t *packet, signed int *data); +boolean net_read_sint16(net_packet_t *packet, signed int *data); + +char *net_read_string(net_packet_t *packet); +char *net_read_safe_string(net_packet_t *packet); + +void net_write_int8(net_packet_t *packet, unsigned int i); +void net_write_int16(net_packet_t *packet, unsigned int i); +void net_write_int32(net_packet_t *packet, unsigned int i); + +void net_write_string(net_packet_t *packet, const char *string); + +#endif /* NET_PACKET_H */ diff --git a/games/NXDoom/src/net_query.c b/games/NXDoom/src/net_query.c new file mode 100644 index 00000000000..ea80d8ccd14 --- /dev/null +++ b/games/NXDoom/src/net_query.c @@ -0,0 +1,1063 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_query.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Querying servers to find their current status. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "i_system.h" +#include "i_timer.h" +#include "m_misc.h" + +#include "net_common.h" +#include "net_defs.h" +#include "net_io.h" +#include "net_packet.h" +#include "net_query.h" +#include "net_sdl.h" +#include "net_structrw.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* DNS address of the Internet master server. */ + +#define MASTER_SERVER_ADDRESS "master.chocolate-doom.org:2342" + +/* Time to wait for a response before declaring a timeout. */ + +#define QUERY_TIMEOUT_SECS 2 + +/* Time to wait for secure demo signatures before declaring a timeout. */ + +#define SIGNATURE_TIMEOUT_SECS 5 + +/* Number of query attempts to make before giving up on a server. */ + +#define QUERY_MAX_ATTEMPTS 3 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + QUERY_TARGET_SERVER, /* Normal server target. */ + QUERY_TARGET_MASTER, /* The master server. */ + QUERY_TARGET_BROADCAST /* Send a broadcast query */ +} query_target_type_t; + +typedef enum +{ + QUERY_TARGET_QUEUED, /* Query not yet sent */ + QUERY_TARGET_QUERIED, /* Query sent, waiting response */ + QUERY_TARGET_RESPONDED, /* Response received */ + QUERY_TARGET_NO_RESPONSE +} query_target_state_t; + +typedef struct +{ + query_target_type_t type; + query_target_state_t state; + net_addr_t *addr; + net_querydata_t data; + unsigned int ping_time; + unsigned int query_time; + unsigned int query_attempts; + boolean printed; +} query_target_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static boolean g_registered_with_master = false; +static boolean g_got_master_response = false; + +static net_context_t *g_query_context; +static query_target_t *g_targets; +static int g_num_targets; + +static boolean g_query_loop_running = false; +static boolean g_printed_header = false; +static int g_last_query_time = 0; + +#if 0 +static char *g_securedemo_start_message = NULL; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void formatted_printf(int wide, const char *s, ...) PRINTF_ATTR(2, 3); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Send a query to the master server. */ + +static void net_query_send_master_query(net_addr_t *addr) +{ + net_packet_t *packet; + + packet = net_new_packet(4); + net_write_int16(packet, NET_MASTER_PACKET_TYPE_QUERY); + net_send_packet(addr, packet); + net_free_packet(packet); + + /* We also send a NAT_HOLE_PUNCH_ALL packet so that servers behind + * NAT gateways will open themselves up to us. + */ + + packet = net_new_packet(4); + net_write_int16(packet, NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH_ALL); + net_send_packet(addr, packet); + net_free_packet(packet); +} + +/* Given the specified address, find the target associated. If no + * target is found, and 'create' is true, a new target is created. + */ + +static query_target_t *get_target_for_addr(net_addr_t *addr, boolean create) +{ + query_target_t *target; + int i; + + for (i = 0; i < g_num_targets; ++i) + { + if (g_targets[i].addr == addr) + { + return &g_targets[i]; + } + } + + if (!create) + { + return NULL; + } + + g_targets = + i_realloc(g_targets, sizeof(query_target_t) * (g_num_targets + 1)); + + target = &g_targets[g_num_targets]; + target->type = QUERY_TARGET_SERVER; + target->state = QUERY_TARGET_QUEUED; + target->printed = false; + target->query_attempts = 0; + target->addr = addr; + net_reference_address(addr); + ++g_num_targets; + + return target; +} + +static void free_targets(void) +{ + int i; + + for (i = 0; i < g_num_targets; ++i) + { + net_release_address(g_targets[i].addr); + } + + free(g_targets); + g_targets = NULL; + g_num_targets = 0; +} + +/* Transmit a query packet */ + +static void net_query_send_query(net_addr_t *addr) +{ + net_packet_t *request; + + request = net_new_packet(10); + net_write_int16(request, NET_PACKET_TYPE_QUERY); + + if (addr == NULL) + { + net_send_broadcast(g_query_context, request); + } + else + { + net_send_packet(addr, request); + } + + net_free_packet(request); +} + +static void net_query_parse_response(net_addr_t *addr, net_packet_t *packet, + net_query_callback_t callback, + void *user_data) +{ + unsigned int packet_type; + net_querydata_t querydata; + query_target_t *target; + + /* Read the header */ + + if (!net_read_int16(packet, &packet_type) || + packet_type != NET_PACKET_TYPE_QUERY_RESPONSE) + { + return; + } + + /* Read query data */ + + if (!net_read_query_data(packet, &querydata)) + { + return; + } + + /* Find the target that responded. */ + + target = get_target_for_addr(addr, false); + + /* If the target is not found, it may be because we are doing + * a LAN broadcast search, in which case we need to create a + * target for the new responder. + */ + + if (target == NULL) + { + query_target_t *broadcast_target; + + broadcast_target = get_target_for_addr(NULL, false); + + /* Not in broadcast mode, unexpected response that came out + * of nowhere. Ignore. + */ + + if (broadcast_target == NULL || + broadcast_target->state != QUERY_TARGET_QUERIED) + { + return; + } + + /* Create new target. */ + + target = get_target_for_addr(addr, true); + broadcast_target = get_target_for_addr(NULL, false); + target->state = QUERY_TARGET_QUERIED; + target->query_time = broadcast_target->query_time; + } + + if (target->state != QUERY_TARGET_RESPONDED) + { + target->state = QUERY_TARGET_RESPONDED; + memcpy(&target->data, &querydata, sizeof(net_querydata_t)); + + /* Calculate RTT. */ + + target->ping_time = i_get_time_ms() - target->query_time; + + /* Invoke callback to signal that we have a new address. */ + + callback(addr, &target->data, target->ping_time, user_data); + } +} + +/* Parse a response packet from the master server. */ + +static void net_query_parse_master_response(net_addr_t *master_addr, + net_packet_t *packet) +{ + unsigned int packet_type; + query_target_t *target; + char *addr_str; + net_addr_t *addr; + + /* Read the header. We are only interested in query responses. */ + + if (!net_read_int16(packet, &packet_type) || + packet_type != NET_MASTER_PACKET_TYPE_QUERY_RESPONSE) + { + return; + } + + /* Read a list of strings containing the addresses of servers + * that the master knows about. + */ + + for (; ; ) + { + addr_str = net_read_string(packet); + + if (addr_str == NULL) + { + break; + } + + /* Resolve address and add to targets list if it is not already + * there. + */ + + addr = net_resolve_address(g_query_context, addr_str); + if (addr != NULL) + { + get_target_for_addr(addr, true); + net_release_address(addr); + } + } + + /* Mark the master as having responded. */ + + target = get_target_for_addr(master_addr, true); + target->state = QUERY_TARGET_RESPONDED; +} + +static void net_query_parse_packet(net_addr_t *addr, net_packet_t *packet, + net_query_callback_t callback, + void *user_data) +{ + query_target_t *target; + + /* This might be the master server responding. */ + + target = get_target_for_addr(addr, false); + + if (target != NULL && target->type == QUERY_TARGET_MASTER) + { + net_query_parse_master_response(addr, packet); + } + else + { + net_query_parse_response(addr, packet, callback, user_data); + } +} + +static void net_query_get_response(net_query_callback_t callback, + void *user_data) +{ + net_addr_t *addr; + net_packet_t *packet; + + if (net_recv_packet(g_query_context, &addr, &packet)) + { + net_query_parse_packet(addr, packet, callback, user_data); + net_release_address(addr); + net_free_packet(packet); + } +} + +/* Find a target we have not yet queried and send a query. */ + +static void send_one_query(void) +{ + unsigned int now; + unsigned int i; + + now = i_get_time_ms(); + + /* Rate limit - only send one query every 50ms. */ + + if (now - g_last_query_time < 50) + { + return; + } + + for (i = 0; i < g_num_targets; ++i) + { + /* Not queried yet? + * Or last query timed out without a response? + */ + + if (g_targets[i].state == QUERY_TARGET_QUEUED || + (g_targets[i].state == QUERY_TARGET_QUERIED && + now - g_targets[i].query_time > QUERY_TIMEOUT_SECS * 1000)) + { + break; + } + } + + if (i >= g_num_targets) + { + return; + } + + /* Found a target to query. Send a query; how to do this depends on + * the target type. + */ + + switch (g_targets[i].type) + { + case QUERY_TARGET_SERVER: + net_query_send_query(g_targets[i].addr); + break; + + case QUERY_TARGET_BROADCAST: + net_query_send_query(NULL); + break; + + case QUERY_TARGET_MASTER: + net_query_send_master_query(g_targets[i].addr); + break; + } + + /* printf("Queried %s\n", net_addr_to_string(targets[i].addr)); */ + + g_targets[i].state = QUERY_TARGET_QUERIED; + g_targets[i].query_time = now; + ++g_targets[i].query_attempts; + + g_last_query_time = now; +} + +/* Time out servers that have been queried and not responded. */ + +static void check_target_timeouts(void) +{ + unsigned int i; + unsigned int now; + + now = i_get_time_ms(); + + for (i = 0; i < g_num_targets; ++i) + { + /* printf("target %i: state %i, queries %i, query time %i\n", + * i, targets[i].state, targets[i].query_attempts, + * now - targets[i].query_time); + */ + + /* We declare a target to be "no response" when we've sent + * multiple query packets to it (QUERY_MAX_ATTEMPTS) and + * received no response to any of them. + */ + + if (g_targets[i].state == QUERY_TARGET_QUERIED && + g_targets[i].query_attempts >= QUERY_MAX_ATTEMPTS && + now - g_targets[i].query_time > QUERY_TIMEOUT_SECS * 1000) + { + g_targets[i].state = QUERY_TARGET_NO_RESPONSE; + + if (g_targets[i].type == QUERY_TARGET_MASTER) + { + fprintf(stderr, "net_master_query: no response " + "from master server.\n"); + } + } + } +} + +/* If all targets have responded or timed out, returns true. */ + +static boolean all_targets_done(void) +{ + unsigned int i; + + for (i = 0; i < g_num_targets; ++i) + { + if (g_targets[i].state != QUERY_TARGET_RESPONDED && + g_targets[i].state != QUERY_TARGET_NO_RESPONSE) + { + return false; + } + } + + return true; +} + +/* Stop the query loop */ + +static void net_query_exit_loop(void) +{ + g_query_loop_running = false; +} + +/* Loop waiting for responses. + * The specified callback is invoked when a new server responds. + */ + +static void net_query_query_loop(net_query_callback_t callback, + void *user_data) +{ + g_query_loop_running = true; + + while (g_query_loop_running && net_query_poll(callback, user_data)) + { + /* Don't thrash the CPU */ + + usleep(1000); + } +} + +static void net_query_init(void) +{ + if (g_query_context == NULL) + { + g_query_context = net_new_context(); + net_add_module(g_query_context, &net_sdl_module); + net_sdl_module.init_client(); + } + + free(g_targets); + g_targets = NULL; + g_num_targets = 0; + + g_printed_header = false; +} + +/* Callback that exits the query loop when the first server is found. */ + +static void net_query_exit_callback(net_addr_t *addr, net_querydata_t *data, + unsigned int ping_time, void *user_data) +{ + net_query_exit_loop(); +} + +/* Search the targets list and find a target that has responded. + * If none have responded, returns NULL. + */ + +static query_target_t *find_first_responder(void) +{ + unsigned int i; + + for (i = 0; i < g_num_targets; ++i) + { + if (g_targets[i].type == QUERY_TARGET_SERVER && + g_targets[i].state == QUERY_TARGET_RESPONDED) + { + return &g_targets[i]; + } + } + + return NULL; +} + +/* Return a count of the number of responses. */ + +static int get_num_responses(void) +{ + unsigned int i; + int result; + + result = 0; + + for (i = 0; i < g_num_targets; ++i) + { + if (g_targets[i].type == QUERY_TARGET_SERVER && + g_targets[i].state == QUERY_TARGET_RESPONDED) + { + ++result; + } + } + + return result; +} + +static void formatted_printf(int wide, const char *s, ...) +{ + va_list args; + int i; + + va_start(args, s); + i = vprintf(s, args); + va_end(args); + + while (i < wide) + { + putchar(' '); + ++i; + } +} + +static const char *game_description(game_mode_t mode, gamemission_t mission) +{ + switch (mission) + { + case doom: + if (mode == shareware) + return "swdoom"; + else if (mode == registered) + return "regdoom"; + else if (mode == retail) + return "ultdoom"; + else + return "doom"; + case doom2: + return "doom2"; + case pack_tnt: + return "tnt"; + case pack_plut: + return "plutonia"; + case pack_chex: + return "chex"; + case pack_hacx: + return "hacx"; + case heretic: + return "heretic"; + case hexen: + return "hexen"; + case strife: + return "strife"; + default: + return "?"; + } +} + +static void print_header(void) +{ + int i; + + putchar('\n'); + formatted_printf(5, "Ping"); + formatted_printf(18, "Address"); + formatted_printf(8, "Players"); + puts("Description"); + + for (i = 0; i < 70; ++i) + putchar('='); + putchar('\n'); +} + +/* Callback function that just prints information in a table. */ + +static void net_query_print_callback(net_addr_t *addr, net_querydata_t *data, + unsigned int ping_time, void *user_data) +{ + /* If this is the first server, print the header. */ + + if (!g_printed_header) + { + print_header(); + g_printed_header = true; + } + + formatted_printf(5, "%4i", ping_time); + formatted_printf(22, "%s", net_addr_to_string(addr)); + formatted_printf(4, "%i/%i ", data->num_players, data->max_players); + + if (data->gamemode != indetermined) + { + printf("(%s) ", game_description(data->gamemode, data->gamemission)); + } + + if (data->server_state) + { + printf("(game running) "); + } + + printf("%s\n", data->description); +} + +#if 0 /* UNUSED */ + +/* Block until a packet of the given type is received from the given + * address. + */ + +static net_packet_t *block_for_packet(net_addr_t *addr, + unsigned int packet_type, + unsigned int timeout_ms) +{ + net_packet_t *packet; + net_addr_t *packet_src; + unsigned int read_packet_type; + unsigned int start_time; + + start_time = i_get_time_ms(); + + while (i_get_time_ms() < start_time + timeout_ms) + { + if (!net_recv_packet(g_query_context, &packet_src, &packet)) + { + usleep(20000); + continue; + } + + /* Caller doesn't need additional reference. */ + + net_release_address(packet_src); + + if (packet_src == addr && net_read_int16(packet, &read_packet_type) && + packet_type == read_packet_type) + { + return packet; + } + + net_free_packet(packet); + } + + /* Timeout - no response. */ + + return NULL; +} + +/* Query master server for secure demo start seed value. */ + +static boolean net_start_secure_demo(prng_seed_t seed) +{ + net_packet_t *request, *response; + net_addr_t *master_addr; + char *signature; + boolean result; + + net_query_init(); + master_addr = net_query_resolve_master(g_query_context); + + /* Send request packet to master server. */ + + request = net_new_packet(10); + net_write_int16(request, NET_MASTER_PACKET_TYPE_SIGN_START); + net_send_packet(master_addr, request); + net_free_packet(request); + + /* Block for response and read contents. + * The signed start message will be saved for later. + */ + + response = block_for_packet(master_addr, + NET_MASTER_PACKET_TYPE_SIGN_START_RESPONSE, + SIGNATURE_TIMEOUT_SECS * 1000); + + result = false; + + if (response != NULL) + { + if (net_read_prng_seed(response, seed)) + { + signature = net_read_string(response); + + if (signature != NULL) + { + g_securedemo_start_message = m_string_duplicate(signature); + result = true; + } + } + + net_free_packet(response); + } + + return result; +} + +/* Query master server for secure demo end signature. */ + +static char *net_end_secure_demo(sha1_digest_t demo_hash) +{ + net_packet_t *request, *response; + net_addr_t *master_addr; + char *signature; + + master_addr = net_query_resolve_master(g_query_context); + + /* Construct end request and send to master server. */ + + request = net_new_packet(10); + net_write_int16(request, NET_MASTER_PACKET_TYPE_SIGN_END); + net_write_sha1_sum(request, demo_hash); + net_write_string(request, g_securedemo_start_message); + net_send_packet(master_addr, request); + net_free_packet(request); + + /* Block for response. The response packet simply contains a string + * with the ASCII signature. + */ + + response = + block_for_packet(master_addr, NET_MASTER_PACKET_TYPE_SIGN_END_RESPONSE, + SIGNATURE_TIMEOUT_SECS * 1000); + + if (response == NULL) + { + return NULL; + } + + signature = net_read_string(response); + + net_free_packet(response); + + return signature; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Resolve the master server address. */ + +net_addr_t *net_query_resolve_master(net_context_t *context) +{ + net_addr_t *addr; + + addr = net_resolve_address(context, MASTER_SERVER_ADDRESS); + + if (addr == NULL) + { + fprintf(stderr, + "Warning: Failed to resolve address " + "for master server: %s\n", + MASTER_SERVER_ADDRESS); + } + + return addr; +} + +/* Send a registration packet to the master server to register + * ourselves with the global list. + */ + +void net_query_add_to_master(net_addr_t *master_addr) +{ + net_packet_t *packet; + + packet = net_new_packet(10); + net_write_int16(packet, NET_MASTER_PACKET_TYPE_ADD); + net_send_packet(master_addr, packet); + net_free_packet(packet); +} + +/* Process a packet received from the master server. */ + +void net_query_add_response(net_packet_t *packet) +{ + unsigned int result; + + if (!net_read_int16(packet, &result)) + { + return; + } + + if (result != 0) + { + /* Only show the message once. */ + + if (!g_registered_with_master) + { + printf("Registered with master server at %s\n", + MASTER_SERVER_ADDRESS); + g_registered_with_master = true; + } + } + else + { + /* Always show rejections. */ + + printf("Failed to register with master server at %s\n", + MASTER_SERVER_ADDRESS); + } + + g_got_master_response = true; +} + +boolean net_query_check_added_to_master(boolean *result) +{ + /* Got response from master yet? */ + + if (!g_got_master_response) + { + return false; + } + + *result = g_registered_with_master; + return true; +} + +/* Send a hole punch request to the master server for the server at the + * given address. + */ + +void net_request_hole_punch(net_context_t *context, net_addr_t *addr) +{ + net_addr_t *master_addr; + net_packet_t *packet; + + master_addr = net_query_resolve_master(context); + if (master_addr == NULL) + { + return; + } + + packet = net_new_packet(32); + net_write_int16(packet, NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH); + net_write_string(packet, net_addr_to_string(addr)); + net_send_packet(master_addr, packet); + + net_free_packet(packet); + net_release_address(master_addr); +} + +/* Polling function, invoked periodically to send queries and + * interpret new responses received from remote servers. + * Returns zero when the query sequence has completed and all targets + * have returned responses or timed out. + */ + +int net_query_poll(net_query_callback_t callback, void *user_data) +{ + check_target_timeouts(); + + /* Send a query. This will only send a single query at once. */ + + send_one_query(); + + /* Check for a response */ + + net_query_get_response(callback, user_data); + + return !all_targets_done(); +} + +int net_start_lan_query(void) +{ + query_target_t *target; + + net_query_init(); + + /* Add a broadcast target to the list. */ + + target = get_target_for_addr(NULL, true); + target->type = QUERY_TARGET_BROADCAST; + + return 1; +} + +int net_start_master_query(void) +{ + net_addr_t *master; + query_target_t *target; + + net_query_init(); + + /* Resolve master address and add to targets list. */ + + master = net_query_resolve_master(g_query_context); + + if (master == NULL) + { + return 0; + } + + target = get_target_for_addr(master, true); + target->type = QUERY_TARGET_MASTER; + net_release_address(master); + + return 1; +} + +void net_lan_query(void) +{ + if (net_start_lan_query()) + { + printf("\nSearching for servers on local LAN ...\n"); + + net_query_query_loop(net_query_print_callback, NULL); + + printf("\n%i server(s) found.\n", get_num_responses()); + free_targets(); + } +} + +void net_master_query(void) +{ + if (net_start_master_query()) + { + printf("\nSearching for servers on Internet ...\n"); + + net_query_query_loop(net_query_print_callback, NULL); + + printf("\n%i server(s) found.\n", get_num_responses()); + free_targets(); + } +} + +void net_query_address(const char *addr_str) +{ + net_addr_t *addr; + query_target_t *target; + + net_query_init(); + + addr = net_resolve_address(g_query_context, addr_str); + + if (addr == NULL) + { + i_error("net_query_address: Host '%s' not found!", addr_str); + } + + /* Add the address to the list of targets. */ + + target = get_target_for_addr(addr, true); + + printf("\nQuerying '%s'...\n", addr_str); + + /* Run query loop. */ + + net_query_query_loop(net_query_exit_callback, NULL); + + /* Check if the target responded. */ + + if (target->state == QUERY_TARGET_RESPONDED) + { + net_query_print_callback(addr, &target->data, target->ping_time, NULL); + net_release_address(addr); + free_targets(); + } + else + { + i_error("No response from '%s'", addr_str); + } +} + +net_addr_t *net_find_lan_server(void) +{ + query_target_t *target; + query_target_t *responder; + net_addr_t *result; + + net_query_init(); + + /* Add a broadcast target to the list. */ + + target = get_target_for_addr(NULL, true); + target->type = QUERY_TARGET_BROADCAST; + + /* Run the query loop, and stop at the first target found. */ + + net_query_query_loop(net_query_exit_callback, NULL); + + responder = find_first_responder(); + + if (responder != NULL) + { + result = responder->addr; + net_reference_address(result); + } + else + { + result = NULL; + } + + free_targets(); + return result; +} diff --git a/games/NXDoom/src/net_query.h b/games/NXDoom/src/net_query.h new file mode 100644 index 00000000000..70dacfecd88 --- /dev/null +++ b/games/NXDoom/src/net_query.h @@ -0,0 +1,61 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_query.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Querying servers to find their current status. + * + ****************************************************************************/ + +#ifndef NET_QUERY_H +#define NET_QUERY_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_defs.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*net_query_callback_t)(net_addr_t *addr, + net_querydata_t *querydata, + unsigned int ping_time, + void *user_data); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +extern int net_start_lan_query(void); +extern int net_start_master_query(void); + +extern void net_lan_query(void); +extern void net_master_query(void); +extern void net_query_address(const char *addr); +extern net_addr_t *net_find_lan_server(void); + +extern int net_query_poll(net_query_callback_t callback, void *user_data); + +extern net_addr_t *net_query_resolve_master(net_context_t *context); +extern void net_query_add_to_master(net_addr_t *master_addr); +extern boolean net_query_check_added_to_master(boolean *result); +extern void net_query_add_response(net_packet_t *packet); +extern void net_request_hole_punch(net_context_t *context, net_addr_t *addr); + +#endif /* NET_QUERY_H */ diff --git a/games/NXDoom/src/net_sdl.c b/games/NXDoom/src/net_sdl.c new file mode 100644 index 00000000000..b57f4d20625 --- /dev/null +++ b/games/NXDoom/src/net_sdl.c @@ -0,0 +1,102 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_sdl.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Networking module which uses SDL_net + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_misc.h" +#include "net_defs.h" +#include "net_io.h" +#include "net_packet.h" +#include "net_sdl.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static boolean net_null_init_client(void); +static boolean net_null_init_server(void); +static void net_null_send_packet(net_addr_t *addr, net_packet_t *packet); +static boolean net_null_recv_packet(net_addr_t **addr, + net_packet_t **packet); +static void net_null_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len); +static void net_null_free_address(net_addr_t *addr); +static net_addr_t *net_null_resolve_address(const char *address); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +net_module_t net_sdl_module = +{ + net_null_init_client, net_null_init_server, net_null_send_packet, + net_null_recv_packet, net_null_addr_to_string, net_null_free_address, + net_null_resolve_address, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static net_addr_t *net_null_resolve_address(const char *address) +{ + return NULL; +} + +static boolean net_null_init_client(void) +{ + return false; +} + +static boolean net_null_init_server(void) +{ + return false; +} + +static void net_null_send_packet(net_addr_t *addr, net_packet_t *packet) +{ +} + +static boolean net_null_recv_packet(net_addr_t **addr, + net_packet_t **packet) +{ + return false; +} + +static void net_null_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len) +{ +} + +static void net_null_free_address(net_addr_t *addr) +{ +} diff --git a/games/NXDoom/src/net_sdl.h b/games/NXDoom/src/net_sdl.h new file mode 100644 index 00000000000..6f972fd1d3f --- /dev/null +++ b/games/NXDoom/src/net_sdl.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_sdl.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Networking module which uses SDL_net + * + * + ****************************************************************************/ + +#ifndef NET_SDL_H +#define NET_SDL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_defs.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern net_module_t net_sdl_module; + +#endif /* NET_SDL_H */ diff --git a/games/NXDoom/src/net_server.c b/games/NXDoom/src/net_server.c new file mode 100644 index 00000000000..61b5246f17c --- /dev/null +++ b/games/NXDoom/src/net_server.c @@ -0,0 +1,2210 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_server.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Network server code + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "config.h" + +#include "d_mode.h" +#include "doomtype.h" +#include "i_system.h" +#include "i_timer.h" +#include "m_argv.h" +#include "m_misc.h" + +#include "net_client.h" +#include "net_common.h" +#include "net_defs.h" +#include "net_io.h" +#include "net_loop.h" +#include "net_packet.h" +#include "net_query.h" +#include "net_sdl.h" +#include "net_server.h" +#include "net_structrw.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* How often to refresh our registration with the master server. */ + +#define MASTER_REFRESH_PERIOD 30 /* twice per minute */ + +/* How often to re-resolve the address of the master server? */ + +#define MASTER_RESOLVE_PERIOD 8 * 60 * 60 /* 8 hours */ + +#define net_sv_expand_ticnum(b) net_expand_tic_num(recvwindow_start, (b)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + /* waiting for the game to be "launched" (key player to press the start + * button) + */ + + SERVER_WAITING_LAUNCH, + + /* game has been launched, we are waiting for all players to be ready + * so the game can start. + */ + + SERVER_WAITING_START, + + /* in a game */ + + SERVER_IN_GAME, +} net_server_state_t; + +typedef struct +{ + boolean active; + int player_number; + net_addr_t *addr; + net_connection_t connection; + int last_send_time; + char *name; + + /* If true, the client has sent the NET_PACKET_TYPE_GAMESTART + * message indicating that it is ready for the game to start. + */ + + boolean ready; + + /* Time that this client connected to the server. + * This is used to determine the controller (oldest client). + */ + + unsigned int connect_time; + + /* Last time new gamedata was received from this client */ + + int last_gamedata_time; + + /* recording a demo without -longtics */ + + boolean recording_lowres; + + /* send queue: items to send to the client + * this is a circular buffer + */ + + int sendseq; + net_full_ticcmd_t sendqueue[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + /* Latest acknowledged by the client */ + + unsigned int acknowledged; + + /* Value of max_players specified by the client on connect. */ + + int max_players; + + /* Observer: receives data but does not participate in the game. */ + + boolean drone; + + /* SHA1 hash sums of the client's WAD directory and dehacked data */ + + sha1_digest_t wad_sha1sum; + sha1_digest_t deh_sha1sum; + + /* Is this client is playing with the Freedoom IWAD? */ + + unsigned int is_freedoom; + + /* Player class (for Hexen) */ + + int player_class; +} net_client_t; + +/* structure used for the recv window */ + +typedef struct +{ + /* Whether this tic has been received yet */ + + boolean active; + + /* Latency value received from the client */ + + signed int latency; + + /* Last time we sent a resend request for this tic */ + + unsigned int resend_time; + + /* Tic data itself */ + + net_ticdiff_t diff; +} net_client_recv_t; + +typedef enum +{ + RANGE_LOCALHOST, /* Same process or 127.x */ + RANGE_PRIVATE, /* RFC 1918 */ + RANGE_PUBLIC, /* The public Internet */ +} ip_range_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static net_server_state_t server_state; +static boolean server_initialized = false; +static net_client_t clients[MAXNETNODES]; +static net_client_t *sv_players[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +static net_context_t *server_context; +static unsigned int sv_gamemode; +static unsigned int sv_gamemission; +static net_gamesettings_t sv_settings; + +/* For registration with master server: */ + +static net_addr_t *master_server = NULL; +static unsigned int master_refresh_time; +static unsigned int master_resolve_time; + +/* receive window */ + +static unsigned int recvwindow_start; +static net_client_recv_t recvwindow[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS] + [CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void net_sv_send_console_message(net_client_t *client, const char *s, + ...) PRINTF_ATTR(2, 3); +static void net_sv_broadcast_message(const char *s, ...) PRINTF_ATTR(1, 2); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void net_sv_disconnect_client(net_client_t *client) +{ + if (client->active) + { + net_conn_disconnect(&client->connection); + } +} + +static boolean client_connected(net_client_t *client) +{ + /* Check that the client is properly connected: ie. not in the + * process of connecting or disconnecting + */ + + return client->active && + client->connection.state == NET_CONN_STATE_CONNECTED; +} + +/* Send a message to be displayed on a client's console */ + +static void net_sv_send_console_message(net_client_t *client, const char *s, + ...) +{ + char buf[1024]; + va_list args; + net_packet_t *packet; + + va_start(args, s); + vsnprintf(buf, sizeof(buf), s, args); + va_end(args); + + packet = net_conn_new_reliable(&client->connection, + NET_PACKET_TYPE_CONSOLE_MESSAGE); + + net_write_string(packet, buf); +} + +/* Send a message to all clients */ + +static void net_sv_broadcast_message(const char *s, ...) +{ + char buf[1024]; + va_list args; + int i; + + va_start(args, s); + vsnprintf(buf, sizeof(buf), s, args); + va_end(args); + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i])) + { + net_sv_send_console_message(&clients[i], "%s", buf); + } + } + + printf("%s\n", buf); +} + +/* Assign player numbers to connected clients */ + +static void net_sv_assign_players(void) +{ + int i; + int pl; + + pl = 0; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i])) + { + if (!clients[i].drone) + { + sv_players[pl] = &clients[i]; + sv_players[pl]->player_number = pl; + ++pl; + } + else + { + clients[i].player_number = -1; + } + } + } + + for (; pl < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++pl) + { + sv_players[pl] = NULL; + } +} + +/* Returns the number of players currently connected. */ + +static int net_sv_num_players(void) +{ + int i; + int result; + + result = 0; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] != NULL && client_connected(sv_players[i])) + { + result += 1; + } + } + + return result; +} + +/* Returns the number of players ready to start the game. */ + +static int net_sv_num_ready_players(void) +{ + int result = 0; + int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i]) && !clients[i].drone && + clients[i].ready) + { + ++result; + } + } + + return result; +} + +/* Returns the maximum number of players that can play. */ + +static int net_sv_max_players(void) +{ + int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i])) + { + return clients[i].max_players; + } + } + + return CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; +} + +/* Returns the number of drones currently connected. */ + +static int net_sv_num_drones(void) +{ + int i; + int result; + + result = 0; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i]) && clients[i].drone) + { + result += 1; + } + } + + return result; +} + +/* returns the number of clients connected */ + +static int net_sv_num_clients(void) +{ + int count; + int i; + + count = 0; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i])) + { + ++count; + } + } + + return count; +} + +/* returns a pointer to the client which controls the server */ + +static net_client_t *net_sv_controller(void) +{ + net_client_t *best; + int i; + + /* Find the oldest client (first to connect). */ + + best = NULL; + + for (i = 0; i < MAXNETNODES; ++i) + { + /* Can't be controller? */ + + if (!client_connected(&clients[i]) || clients[i].drone) + { + continue; + } + + if (best == NULL || clients[i].connect_time < best->connect_time) + { + best = &clients[i]; + } + } + + return best; +} + +static ip_range_t client_address_range(const char *addr) +{ + if (!strcmp(addr, "local client") || m_string_starts_with(addr, "127.")) + { + return RANGE_LOCALHOST; + } + + if (m_string_starts_with(addr, "10.") || + m_string_starts_with(addr, "192.168.")) + { + return RANGE_PRIVATE; + } + + return RANGE_PUBLIC; +} + +static void net_sv_send_waiting_data(net_client_t *client) +{ + net_waitdata_t wait_data; + net_packet_t *packet; + net_client_t *controller; + ip_range_t client_range, player_range; + const char *addr; + int i; + + net_sv_assign_players(); + + controller = net_sv_controller(); + + wait_data.num_players = net_sv_num_players(); + wait_data.num_drones = net_sv_num_drones(); + wait_data.ready_players = net_sv_num_ready_players(); + wait_data.max_players = net_sv_max_players(); + wait_data.is_controller = (client == controller); + wait_data.consoleplayer = client->player_number; + + /* Send the WAD and dehacked checksums of the controlling client. + * If no controller found (?), send the details that the client + * is expecting anyway. + */ + + if (controller == NULL) + { + controller = client; + } + + memcpy(&wait_data.wad_sha1sum, &controller->wad_sha1sum, + sizeof(sha1_digest_t)); + memcpy(&wait_data.deh_sha1sum, &controller->deh_sha1sum, + sizeof(sha1_digest_t)); + wait_data.is_freedoom = controller->is_freedoom; + + /* We only send IP addresses to locally-connected clients (including + * the 127.* loopback range): + */ + + addr = net_addr_to_string(client->connection.addr); + client_range = client_address_range(addr); + + /* set name and address of each player: */ + + for (i = 0; i < wait_data.num_players; ++i) + { + m_str_copy(wait_data.player_names[i], sv_players[i]->name, + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME); + + /* For privacy, only local clients or those on a LAN get to see + * addresses. Public clients only get to see their own address, + * though we do reveal localhost addresses since they're harmless, + * and we do reveal when a client is connected via LAN. + */ + + addr = net_addr_to_string(sv_players[i]->addr); + player_range = client_address_range(addr); + if (client_range == RANGE_LOCALHOST || client_range == RANGE_PRIVATE || + i == wait_data.consoleplayer || player_range == RANGE_LOCALHOST) + { + m_str_copy(wait_data.player_addrs[i], addr, + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME); + } + else if (player_range == RANGE_PRIVATE) + { + snprintf(wait_data.player_addrs[i], + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME, + "[LAN player]"); + } + else + { + snprintf(wait_data.player_addrs[i], + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME, + "[address hidden]"); + } + } + + /* Construct packet: */ + + packet = net_new_packet(10); + net_write_int16(packet, NET_PACKET_TYPE_WAITING_DATA); + net_write_wait_data(packet, &wait_data); + + /* Send packet to client and free */ + + net_conn_send_packet(&client->connection, packet); + net_free_packet(packet); +} + +/* Find the latest tic which has been acknowledged as received by + * all clients. + */ + +static unsigned int net_sv_latest_acknowledged(void) +{ + unsigned int lowtic = UINT_MAX; + int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i])) + { + if (clients[i].acknowledged < lowtic) + { + lowtic = clients[i].acknowledged; + } + } + } + + return lowtic; +} + +/* Possibly advance the recv window if all connected clients have + * used the data in the window + */ + +static void net_sv_advance_window(void) +{ + unsigned int lowtic; + int i; + + if (net_sv_num_players() <= 0) + { + return; + } + + lowtic = net_sv_latest_acknowledged(); + + /* Advance the recv window until it catches up with lowtic */ + + while (recvwindow_start < lowtic) + { + boolean should_advance; + + /* Check we have tics from all players for first tic in + * the recv window + */ + + should_advance = true; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] == NULL || !client_connected(sv_players[i])) + { + continue; + } + + if (!recvwindow[0][i].active) + { + should_advance = false; + break; + } + } + + if (!should_advance) + { + /* The first tic is not complete: ie. we have not + * received tics from all connected players. This can + * happen if only one player is in the game. + */ + + break; + } + + /* Advance the window */ + + memmove(recvwindow, recvwindow + 1, + sizeof(*recvwindow) * + (CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1)); + memset(&recvwindow[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1], + 0, sizeof(*recvwindow)); + ++recvwindow_start; + net_log_info("server: advanced receive window to %d", + recvwindow_start); + } +} + +/* Given an address, find the corresponding client */ + +static net_client_t *net_sv_find_client(net_addr_t *addr) +{ + int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (clients[i].active && clients[i].addr == addr) + { + /* found the client */ + + return &clients[i]; + } + } + + return NULL; +} + +/* send a rejection packet to a client */ + +static void net_sv_send_reject(net_addr_t *addr, const char *msg) +{ + net_packet_t *packet; + + net_log_info("server: sending reject to %s", net_addr_to_string(addr)); + + packet = net_new_packet(10); + net_write_int16(packet, NET_PACKET_TYPE_REJECTED); + net_write_string(packet, msg); + net_send_packet(addr, packet); + net_free_packet(packet); +} + +static void net_sv_init_new_client(net_client_t *client, net_addr_t *addr, + net_protocol_t protocol) +{ + client->active = true; + client->connect_time = i_get_time_ms(); + net_conn_init_server(&client->connection, addr, protocol); + client->addr = addr; + net_reference_address(addr); + client->last_send_time = -1; + + /* init the ticcmd send queue */ + + client->sendseq = 0; + client->acknowledged = 0; + client->drone = false; + client->ready = false; + + client->last_gamedata_time = 0; + + memset(client->sendqueue, 0xff, sizeof(client->sendqueue)); + + net_log_info("server: initialized new client from %s", + net_addr_to_string(addr)); +} + +/* parse a SYN from a client(initiating a connection) */ + +static void net_sv_parse_syn(net_packet_t *packet, net_client_t *client, + net_addr_t *addr) +{ + unsigned int magic; + net_connect_data_t data; + net_packet_t *reply; + net_protocol_t protocol; + char *player_name; + char *client_version; + int num_players; + int i; + + net_log_info("server: processing SYN packet"); + + /* Read the magic number and check it is the expected one. */ + + if (!net_read_int32(packet, &magic)) + { + net_log_err("server: no magic number for SYN"); + return; + } + + switch (magic) + { + case NET_MAGIC_NUMBER: + break; + + case NET_OLD_MAGIC_NUMBER: + net_log_err("server: client using old magic number: %d", magic); + net_sv_send_reject( + addr, + "You are using an old client version that is not supported by " + "this server. This server is running " PACKAGE_STRING "."); + return; + + default: + net_log_err("server: wrong magic number: %d", magic); + return; + } + + /* Read the client version string. We actually now only use this when + * sending a reject message, as we only reject if we can't negotiate a + * common protocol (below). + */ + + client_version = net_read_string(packet); + if (client_version == NULL) + { + net_log_err("server: no version from client"); + return; + } + + /* Read the client's list of accepted protocols. Net play between forks + * of Chocolate Doom is accepted provided that they can negotiate a + * common accepted protocol. + */ + + protocol = net_read_protocol_list(packet); + if (protocol == NET_PROTOCOL_UNKNOWN) + { + char reject_msg[256]; + + snprintf(reject_msg, sizeof(reject_msg), + "Version mismatch: server version is: " PACKAGE_STRING "; " + "client is: %s. No common compatible protocol could be " + "negotiated.", + client_version); + net_sv_send_reject(addr, reject_msg); + net_log_err("server: no common protocol"); + return; + } + + /* Read connect data, and check that the game mode/mission are valid + * and the max_players value is in a sensible range. + */ + + if (!net_read_connect_data(packet, &data)) + { + net_log_err("server: failed to read connect data"); + return; + } + + if (!d_valid_game_mode(data.gamemission, data.gamemode) || + data.max_players > CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS) + { + net_log_err("server: invalid connect data, max_players=%d, " + "gamemission=%d, gamemode=%d", + data.max_players, data.gamemission, data.gamemode); + return; + } + + /* Read the player's name */ + + player_name = net_read_string(packet); + if (player_name == NULL) + { + net_log_err("server: failed to read player name"); + return; + } + + /* At this point we have received a valid SYN. */ + + /* Not accepting new connections? */ + + if (server_state != SERVER_WAITING_LAUNCH) + { + net_log_err("server: not in waiting launch state, server_state=%d", + server_state); + net_sv_send_reject(addr, + "Server is not currently accepting connections"); + return; + } + + /* Before accepting a new client, check that there is a slot free. */ + + net_sv_assign_players(); + num_players = net_sv_num_players(); + + if ((!data.drone && num_players >= net_sv_max_players()) || + net_sv_num_clients() >= MAXNETNODES) + { + net_log_info("server: no more players, num_players=%d, max=%d", + num_players, net_sv_max_players()); + net_sv_send_reject(addr, "Server is full!"); + return; + } + + /* TODO: Add server option to allow rejecting clients which set + * lowres_turn. This is potentially desirable as the presence of such + * clients affects turning resolution. + */ + + /* Adopt the game mode and mission of the first connecting client: */ + + if (num_players == 0 && !data.drone) + { + sv_gamemode = data.gamemode; + sv_gamemission = data.gamemission; + net_log_info("server: new game, mode=%d, mission=%d", sv_gamemode, + sv_gamemission); + } + + /* Check the connecting client is playing the same game as all + * the other clients + */ + + if (data.gamemode != sv_gamemode || data.gamemission != sv_gamemission) + { + char msg[128]; + net_log_warn("server: wrong mode/mission, %d != %d || %d != %d", + data.gamemode, sv_gamemode, data.gamemission, + sv_gamemission); + snprintf(msg, sizeof(msg), + "Game mismatch: server is %s (%s), client is %s (%s)", + d_game_mission_string(sv_gamemission), + d_game_mode_string(sv_gamemode), + d_game_mission_string(data.gamemission), + d_game_mode_string(data.gamemode)); + + net_sv_send_reject(addr, msg); + return; + } + + /* Allocate a client slot if there isn't one already */ + + if (client == NULL) + { + /* find a slot, or return if none found */ + + for (i = 0; i < MAXNETNODES; ++i) + { + if (!clients[i].active) + { + client = &clients[i]; + break; + } + } + + if (client == NULL) + { + return; + } + } + else + { + /* If this is a recently-disconnected client, deactivate + * to allow immediate reconnection + */ + + if (client->connection.state == NET_CONN_STATE_DISCONNECTED) + { + client->active = false; + } + } + + /* Client already connected? */ + + if (client->active) + { + net_log_warn("server: client is already initialized (duplicate SYN?)"); + return; + } + + /* Activate, initialize connection */ + + net_sv_init_new_client(client, addr, protocol); + + /* Save the SHA1 checksums and other details. */ + + memcpy(client->wad_sha1sum, data.wad_sha1sum, sizeof(sha1_digest_t)); + memcpy(client->deh_sha1sum, data.deh_sha1sum, sizeof(sha1_digest_t)); + client->is_freedoom = data.is_freedoom; + client->max_players = data.max_players; + client->name = m_string_duplicate(player_name); + client->recording_lowres = data.lowres_turn; + client->drone = data.drone; + client->player_class = data.player_class; + + /* Send a reply back to the client, indicating a successful connection + * and specifying the protocol that will be used for communications. + */ + + reply = net_conn_new_reliable(&client->connection, NET_PACKET_TYPE_SYN); + net_write_string(reply, PACKAGE_STRING); + net_write_protocol(reply, protocol); +} + +/* Parse a launch packet. This is sent by the key player when the "start" + * button is pressed, and causes the startup process to continue. + */ + +static void net_sv_parse_launch(net_packet_t *packet, net_client_t *client) +{ + net_packet_t *launchpacket; + int num_players; + unsigned int i; + + net_log_info("server: processing launch packet"); + + /* Only the controller can launch the game. */ + + if (client != net_sv_controller()) + { + net_log_err("server: this client isn't the controller, %p != %p", + client, net_sv_controller()); + return; + } + + /* Can only launch when we are in the waiting state. */ + + if (server_state != SERVER_WAITING_LAUNCH) + { + net_log_err("server: not in waiting launch state, state=%d", + server_state); + return; + } + + /* Forward launch on to all clients. */ + + net_log_info("server: sending launch to all clients"); + net_sv_assign_players(); + num_players = net_sv_num_players(); + + for (i = 0; i < MAXNETNODES; ++i) + { + if (!client_connected(&clients[i])) continue; + + launchpacket = net_conn_new_reliable(&clients[i].connection, + NET_PACKET_TYPE_LAUNCH); + net_write_int8(launchpacket, num_players); + } + + /* Now in launch state. */ + + server_state = SERVER_WAITING_START; +} + +/* Transition to the in-game state and send all players the start game + * message. Invoked once all players have indicated they are ready to + * start the game. + */ + +static void start_game(void) +{ + net_packet_t *startpacket; + unsigned int i; + int nowtime; + + /* Assign player numbers */ + + net_sv_assign_players(); + + /* Check if anyone is recording a demo and set lowres_turn if so. */ + + sv_settings.lowres_turn = false; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] != NULL && sv_players[i]->recording_lowres) + { + sv_settings.lowres_turn = true; + } + } + + sv_settings.num_players = net_sv_num_players(); + + /* Copy player classes: */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] != NULL) + { + sv_settings.player_classes[i] = sv_players[i]->player_class; + } + else + { + sv_settings.player_classes[i] = 0; + } + } + + nowtime = i_get_time_ms(); + + /* Send start packets to each connected node */ + + for (i = 0; i < MAXNETNODES; ++i) + { + if (!client_connected(&clients[i])) continue; + + clients[i].last_gamedata_time = nowtime; + + startpacket = net_conn_new_reliable(&clients[i].connection, + NET_PACKET_TYPE_GAMESTART); + + sv_settings.consoleplayer = clients[i].player_number; + + net_write_settings(startpacket, &sv_settings); + } + + /* Change server state */ + + net_log_info("server: beginning game state"); + server_state = SERVER_IN_GAME; + + memset(recvwindow, 0, sizeof(recvwindow)); + recvwindow_start = 0; +} + +/* Returns true when all nodes have indicated readiness to start the game. */ + +static boolean all_nodes_ready(void) +{ + unsigned int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i]) && !clients[i].ready) + { + return false; + } + } + + return true; +} + +/* Check if the game should start, and if so, start it. */ + +static void check_start_game(void) +{ + if (!all_nodes_ready()) + { + net_log_warn("server: not all clients ready to start yet"); + return; + } + + net_log_info("server: all clients ready, starting game"); + start_game(); +} + +/* Send waiting data with current status to all nodes that are ready to + * start the game. + */ + +static void send_all_waiting_data(void) +{ + unsigned int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i]) && clients[i].ready) + { + net_sv_send_waiting_data(&clients[i]); + } + } +} + +/* Parse a game start packet */ + +static void net_sv_parse_game_start(net_packet_t *packet, + net_client_t *client) +{ + net_gamesettings_t settings; + + net_log_info("server: processing game start packet"); + + /* Can only start a game if we are in the waiting start state. */ + + if (server_state != SERVER_WAITING_START) + { + net_log_info( + "server: error: not in waiting start state, server_state=%d", + server_state); + return; + } + + if (client == net_sv_controller()) + { + if (!net_read_settings(packet, &settings)) + { + /* Malformed packet */ + + net_log_err("server: no settings from controller"); + return; + } + + /* Check the game settings are valid */ + + if (!net_valid_game_settings(sv_gamemode, sv_gamemission, &settings)) + { + net_log_err("server: invalid game settings"); + return; + } + + sv_settings = settings; + } + + client->ready = true; + + check_start_game(); + + /* Update all ready clients with the current state (number of players + * ready, etc.). This is used by games that show startup progress + * (eg. Hexen's spinal loading) + */ + + send_all_waiting_data(); +} + +/* Send a resend request to a client */ + +static void net_sv_send_resend_request(net_client_t *client, int start, + int end) +{ + net_packet_t *packet; + net_client_recv_t *recvobj; + int i; + unsigned int nowtime; + int index; + + net_log_info("server: send resend to %s for tics %d-%d", + net_addr_to_string(client->addr), start, end); + + packet = net_new_packet(20); + + net_write_int16(packet, NET_PACKET_TYPE_GAMEDATA_RESEND); + net_write_int32(packet, start); + net_write_int8(packet, end - start + 1); + + net_conn_send_packet(&client->connection, packet); + net_free_packet(packet); + + /* Store the time we send the resend request */ + + nowtime = i_get_time_ms(); + + for (i = start; i <= end; ++i) + { + index = i - recvwindow_start; + + if (index >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + /* Outside the range */ + + continue; + } + + recvobj = &recvwindow[index][client->player_number]; + + recvobj->resend_time = nowtime; + } +} + +/* Check for expired resend requests */ + +static void net_sv_check_resends(net_client_t *client) +{ + int i; + int player; + int resend_start; + int resend_end; + unsigned int nowtime; + + nowtime = i_get_time_ms(); + + player = client->player_number; + resend_start = -1; + resend_end = -1; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_BACKUPTICS; ++i) + { + net_client_recv_t *recvobj; + boolean need_resend; + + recvobj = &recvwindow[i][player]; + + /* if need_resend is true, this tic needs another retransmit + * request (300ms timeout) + */ + + need_resend = !recvobj->active && recvobj->resend_time != 0 && + nowtime > recvobj->resend_time + 300; + + if (need_resend) + { + /* Start a new run of resend tics? */ + + if (resend_start < 0) + { + resend_start = i; + } + + resend_end = i; + } + else if (resend_start >= 0) + { + /* End of a run of resend tics */ + + net_log_warn( + "server: resend request to %s timed out for %d-%d (%p)", + net_addr_to_string(client->addr), + recvwindow_start + resend_start, + recvwindow_start + resend_end, + &recvwindow[resend_start][player].resend_time); + + net_sv_send_resend_request(client, recvwindow_start + resend_start, + recvwindow_start + resend_end); + + resend_start = -1; + } + } + + if (resend_start >= 0) + { + net_log_warn("server: resend request to %s timed out for %d-%d (%p)", + net_addr_to_string(client->addr), + recvwindow_start + resend_start, + recvwindow_start + resend_end, + &recvwindow[resend_start][player].resend_time); + net_sv_send_resend_request(client, recvwindow_start + resend_start, + recvwindow_start + resend_end); + } +} + +/* Process game data from a client */ + +static void net_sv_parse_game_data(net_packet_t *packet, + net_client_t *client) +{ + net_client_recv_t *recvobj; + unsigned int seq; + unsigned int ackseq; + unsigned int num_tics; + unsigned int nowtime; + size_t i; + int player; + int resend_start; + int resend_end; + int index; + + if (server_state != SERVER_IN_GAME) + { + net_log_err("server: not in game state: server_state=%d", + server_state); + return; + } + + if (client->drone) + { + /* Drones do not contribute any game data. */ + + net_log_err("server: game data from a drone?"); + return; + } + + player = client->player_number; + + /* Read header */ + + if (!net_read_int8(packet, &ackseq) || !net_read_int8(packet, &seq) || + !net_read_int8(packet, &num_tics)) + { + net_log_err("server: failed to read header"); + return; + } + + net_log_info("server: got game data, seq=%d, num_tics=%d, ackseq=%d", seq, + num_tics, ackseq); + + /* Get the current time */ + + nowtime = i_get_time_ms(); + + /* Expand 8-bit values to the full sequence number */ + + ackseq = net_sv_expand_ticnum(ackseq); + seq = net_sv_expand_ticnum(seq); + + /* Sanity checks */ + + for (i = 0; i < num_tics; ++i) + { + net_ticdiff_t diff; + signed int latency; + + if (!net_read_sint16(packet, &latency) || + !net_read_ticcmd_diff(packet, &diff, sv_settings.lowres_turn)) + { + return; + } + + index = seq + i - recvwindow_start; + + if (index < 0 || index >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + /* Not in range of the recv window */ + + continue; + } + + recvobj = &recvwindow[index][player]; + recvobj->active = true; + recvobj->diff = diff; + recvobj->latency = latency; + + client->last_gamedata_time = nowtime; + net_log_info("server: stored tic %zu for player %d", seq + i, player); + } + + /* Higher acknowledgement point? */ + + if (ackseq > client->acknowledged) + { + net_log_info("server: acknowledged up to %d", ackseq); + client->acknowledged = ackseq; + } + + /* Has this been received out of sequence, ie. have we not received + * all tics before the first tic in this packet? If so, send a + * resend request. + */ + + resend_end = seq - recvwindow_start; + + if (resend_end <= 0) return; + + if (resend_end >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + resend_end = CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1; + } + + index = resend_end - 1; + resend_start = resend_end; + + while (index >= 0) + { + recvobj = &recvwindow[index][player]; + + if (recvobj->active) + { + /* ended our run of unreceived tics */ + + break; + } + + if (recvobj->resend_time != 0) + { + /* Already sent a resend request for this tic */ + + break; + } + + resend_start = index; + --index; + } + + /* Possibly send a resend request */ + + if (resend_start < resend_end) + { + net_log_info("server: request resend for %d-%d before %d", + recvwindow_start + resend_start, + recvwindow_start + resend_end - 1, seq); + net_sv_send_resend_request(client, recvwindow_start + resend_start, + recvwindow_start + resend_end - 1); + } +} + +static void net_sv_parse_game_data_ack(net_packet_t *packet, + net_client_t *client) +{ + unsigned int ackseq; + + net_log_info("server: processing game data ack packet"); + + if (server_state != SERVER_IN_GAME) + { + net_log_err("server: not in game state, server_state=%d", + server_state); + return; + } + + /* Read header */ + + if (!net_read_int8(packet, &ackseq)) + { + net_log_err("server: missing acknowledgement field"); + return; + } + + /* Expand 8-bit values to the full sequence number */ + + ackseq = net_sv_expand_ticnum(ackseq); + + /* Higher acknowledgement point than we already have? */ + + if (ackseq > client->acknowledged) + { + net_log_info("server: acknowledged up to %d", ackseq); + client->acknowledged = ackseq; + } +} + +static void net_sv_send_tics(net_client_t *client, unsigned int start, + unsigned int end) +{ + net_packet_t *packet; + unsigned int i; + + packet = net_new_packet(500); + + net_write_int16(packet, NET_PACKET_TYPE_GAMEDATA); + + /* Send the start tic and number of tics */ + + net_write_int8(packet, start & 0xff); + net_write_int8(packet, end - start + 1); + + /* Write the tics */ + + for (i = start; i <= end; ++i) + { + net_full_ticcmd_t *cmd; + + cmd = &client->sendqueue[i % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + if (i != cmd->seq) + { + i_error("Wanted to send %i, but %i is in its place", i, cmd->seq); + } + + /* Add command */ + + net_write_full_ticcmd(packet, cmd, sv_settings.lowres_turn); + } + + /* Send packet */ + + net_conn_send_packet(&client->connection, packet); + + net_free_packet(packet); +} + +/* Parse a retransmission request from a client */ + +static void net_sv_parse_resend_request(net_packet_t *packet, + net_client_t *client) +{ + unsigned int start; + unsigned int last; + unsigned int num_tics; + unsigned int i; + + net_log_info("server: processing resend request"); + + /* Read the starting tic and number of tics */ + + if (!net_read_int32(packet, &start) || !net_read_int8(packet, &num_tics)) + { + net_log_err("server: missing fields for resend"); + return; + } + + /* Check we have all the requested tics */ + + last = start + num_tics - 1; + + for (i = start; i <= last; ++i) + { + net_full_ticcmd_t *cmd; + + cmd = &client->sendqueue[i % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + if (i != cmd->seq) + { + /* We do not have the requested tic (any more) + * This is pretty fatal. We could disconnect the client, + * but then again this could be a spoofed packet. Just + * ignore it. + */ + + net_log_err("server: don't have tic %d any more, " + "can't resend", + i); + return; + } + } + + /* Resend those tics */ + + net_log_info("server: resending tics %d-%d", start, last); + net_sv_send_tics(client, start, last); +} + +static void net_sv_parse_hole_punch(net_packet_t *packet) +{ + const char *addr_string; + net_packet_t *sendpacket; + net_addr_t *addr; + + addr_string = net_read_string(packet); + if (addr_string == NULL) + { + net_log_err("server: hole punch request but no address provided"); + return; + } + + addr = net_resolve_address(server_context, addr_string); + if (addr == NULL) + { + net_log_err("server: failed to resolve address: %s", addr_string); + return; + } + + sendpacket = net_new_packet(16); + net_write_int16(sendpacket, NET_PACKET_TYPE_NAT_HOLE_PUNCH); + net_send_packet(addr, sendpacket); + net_free_packet(sendpacket); + net_release_address(addr); + net_log_info("server: sent hole punch to %s", addr_string); +} + +static void net_sv_master_packet(net_packet_t *packet) +{ + unsigned int packet_type; + + /* Read the packet type */ + + if (!net_read_int16(packet, &packet_type)) + { + net_log_err("server: no packet type in master server message"); + return; + } + + net_log_info("server: packet from master server; type %d", packet_type); + net_log_packet(packet); + + switch (packet_type) + { + case NET_MASTER_PACKET_TYPE_ADD_RESPONSE: + net_query_add_response(packet); + break; + + case NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH: + net_sv_parse_hole_punch(packet); + break; + } +} + +/* Send a response back to the client */ + +static void net_sv_send_query_response(net_addr_t *addr) +{ + net_packet_t *reply; + net_querydata_t querydata; + int p; + + /* Version */ + + querydata.version = PACKAGE_STRING; + + /* Server state */ + + querydata.server_state = server_state; + + /* Number of players/maximum players */ + + querydata.num_players = net_sv_num_players(); + querydata.max_players = net_sv_max_players(); + + /* Game mode/mission */ + + querydata.gamemode = sv_gamemode; + querydata.gamemission = sv_gamemission; + + /* @category net + * @arg + * + * When starting a network server, specify a name for the server. + */ + + p = m_check_parm_with_args("-servername", 1); + + if (p > 0) + { + querydata.description = myargv[p + 1]; + } + else + { + querydata.description = "Unnamed server"; + } + + /* Send it and we're done. */ + + net_log_info("server: sending query response to %s", + net_addr_to_string(addr)); + reply = net_new_packet(64); + net_write_int16(reply, NET_PACKET_TYPE_QUERY_RESPONSE); + net_write_query_data(reply, &querydata); + net_send_packet(addr, reply); + net_free_packet(reply); +} + +/* Process a packet received by the server */ + +static void net_sv_packet(net_packet_t *packet, net_addr_t *addr) +{ + net_client_t *client; + unsigned int packet_type; + + /* Response from master server? */ + + if (addr != NULL && addr == master_server) + { + net_sv_master_packet(packet); + return; + } + + /* Find which client this packet came from */ + + client = net_sv_find_client(addr); + + /* Read the packet type */ + + if (!net_read_int16(packet, &packet_type)) + { + /* no packet type */ + + return; + } + + net_log_info("server: packet from %s; type %d", net_addr_to_string(addr), + packet_type & ~NET_RELIABLE_PACKET); + net_log_packet(packet); + + if (packet_type == NET_PACKET_TYPE_SYN) + { + net_sv_parse_syn(packet, client, addr); + } + else if (packet_type == NET_PACKET_TYPE_QUERY) + { + net_sv_send_query_response(addr); + } + else if (client == NULL) + { + /* Must come from a valid client; ignore otherwise */ + } + else if (net_conn_packet(&client->connection, packet, &packet_type)) + { + /* Packet was eaten by the common connection code */ + } + else + { + switch (packet_type) + { + case NET_PACKET_TYPE_GAMESTART: + net_sv_parse_game_start(packet, client); + break; + case NET_PACKET_TYPE_LAUNCH: + net_sv_parse_launch(packet, client); + break; + case NET_PACKET_TYPE_GAMEDATA: + net_sv_parse_game_data(packet, client); + break; + case NET_PACKET_TYPE_GAMEDATA_ACK: + net_sv_parse_game_data_ack(packet, client); + break; + case NET_PACKET_TYPE_GAMEDATA_RESEND: + net_sv_parse_resend_request(packet, client); + break; + default: + break; /* unknown packet type */ + } + } +} + +static void net_sv_pump_send_queue(net_client_t *client) +{ + net_full_ticcmd_t cmd; + int recv_index; + int num_players; + int i; + int starttic; + int endtic; + + /* If a client has not sent any acknowledgments for a while, + * wait until they catch up. + */ + + if (client->sendseq - net_sv_latest_acknowledged() > 40) + { + return; + } + + /* Work out the index into the receive window */ + + recv_index = client->sendseq - recvwindow_start; + + if (recv_index < 0 || recv_index >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + return; + } + + /* Check if we can generate a new entry for the send queue + * using the data in recvwindow. + */ + + num_players = 0; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] == client) + { + /* Client does not rely on itself for data */ + + continue; + } + + if (sv_players[i] == NULL || !client_connected(sv_players[i])) + { + continue; + } + + if (!recvwindow[recv_index][i].active) + { + /* We do not have this player's ticcmd, so we cannot generate a + * complete command yet. + */ + + return; + } + + ++num_players; + } + + /* If this is a game with only a single player in it, we might + * be sending a ticcmd set containing 0 ticcmds. This is fine; + * however, there's nothing to stop the game running on ahead + * and never stopping. Don't let the server get too far ahead + * of the client. + */ + + if (num_players == 0 && client->sendseq > recvwindow_start + 10) + { + return; + } + + /* We have all data we need to generate a command for this tic. */ + + cmd.seq = client->sendseq; + + /* Add ticcmds from all players */ + + cmd.latency = 0; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + net_client_recv_t *recvobj; + + if (sv_players[i] == client) + { + /* Not the player we are sending to */ + + cmd.playeringame[i] = false; + continue; + } + + if (sv_players[i] == NULL || !recvwindow[recv_index][i].active) + { + cmd.playeringame[i] = false; + continue; + } + + cmd.playeringame[i] = true; + + recvobj = &recvwindow[recv_index][i]; + + cmd.cmds[i] = recvobj->diff; + + if (recvobj->latency > cmd.latency) cmd.latency = recvobj->latency; + } + + /* Add into the queue */ + + client->sendqueue[client->sendseq % + CONFIG_GAMES_NXDOOM_NET_BACKUPTICS] = cmd; + + /* Transmit the new tic to the client */ + + starttic = client->sendseq - sv_settings.extratics; + endtic = client->sendseq; + + if (starttic < 0) starttic = 0; + + net_log_info("server: send tics %d-%d to %s", starttic, endtic, + net_addr_to_string(client->addr)); + net_sv_send_tics(client, starttic, endtic); + + ++client->sendseq; +} + +/* Called when all players have disconnected. Return to listening for + * players to start a new game, and disconnect any drones still connected. + */ + +static void net_sv_game_ended(void) +{ + int i; + + server_state = SERVER_WAITING_LAUNCH; + sv_gamemode = indetermined; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (clients[i].active) + { + net_sv_disconnect_client(&clients[i]); + } + } +} + +/* Prevent against deadlock: resend requests are usually only + * triggered if we miss a packet and receive the next one. + * If we miss a whole load of packets, we can end up in a + * deadlock situation where the client will not send any more. + * If we don't receive any game data in a while, trigger a resend + * request for the next tic we're expecting. + */ + +static void net_sv_check_deadlock(net_client_t *client) +{ + int nowtime; + int i; + + /* Don't expect game data from clients. */ + + if (client->drone) + { + return; + } + + nowtime = i_get_time_ms(); + + /* If we haven't received anything for a long time, it may be a deadlock. */ + + if (nowtime - client->last_gamedata_time > 1000) + { + net_log_warn("server: no gamedata from %s since %d - deadlock?", + net_addr_to_string(client->addr), + client->last_gamedata_time); + + /* Search the receive window for the first tic we are expecting + * from this player. + */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_BACKUPTICS; ++i) + { + if (!recvwindow[i][client->player_number].active) + { + net_log_warn( + "server: deadlock: sending resend request for %d-%d", + recvwindow_start + i, recvwindow_start + i + 5); + + /* Found a tic we haven't received. Send a resend request. */ + + net_sv_send_resend_request(client, recvwindow_start + i, + recvwindow_start + i + 5); + + client->last_gamedata_time = nowtime; + break; + } + } + + /* If we sent a resend request to break the deadlock, also trigger a + * resend of any tics we have sitting in the send queue, in case the + * client is blocked waiting on tics from us that have been lost. + * This fixes deadlock with some older clients which do not send + * resends to break deadlock. + */ + + if (i < CONFIG_GAMES_NXDOOM_NET_BACKUPTICS && + client->sendseq > client->acknowledged) + { + net_log_warn("server: also resending tics %d-%d to break deadlock", + client->acknowledged, client->sendseq - 1); + net_sv_send_tics(client, client->acknowledged, + client->sendseq - 1); + } + } +} + +/* Perform any needed action on a client */ + +static void net_sv_run_client(net_client_t *client) +{ + /* Run common code */ + + net_conn_run(&client->connection); + + if (client->connection.state == NET_CONN_STATE_DISCONNECTED && + client->connection.disconnect_reason == NET_DISCONNECT_TIMEOUT) + { + net_log_warn("server: client at %s timed out", + net_addr_to_string(client->addr)); + net_sv_broadcast_message("Client '%s' timed out and disconnected", + client->name); + } + + /* Is this client disconnected? */ + + if (client->connection.state == NET_CONN_STATE_DISCONNECTED) + { + client->active = false; + + /* If we were about to start a game, any player disconnecting + * should cause an abort. + */ + + if (server_state == SERVER_WAITING_START && !client->drone) + { + net_sv_broadcast_message("Game startup aborted because " + "player '%s' disconnected.", + client->name); + net_sv_game_ended(); + } + + free(client->name); + net_release_address(client->addr); + + /* Are there any clients left connected? If not, return the + * server to the waiting-for-players state. + * + * Disconnect any drones still connected. + */ + + if (net_sv_num_players() <= 0) + { + net_log_info("server: no player clients left, game ended"); + net_sv_game_ended(); + } + } + + if (!client_connected(client)) + { + /* client has not yet finished connecting */ + + return; + } + + if (server_state == SERVER_WAITING_LAUNCH) + { + /* Waiting for the game to start */ + + /* Send information once every second */ + + if (client->last_send_time < 0 || + i_get_time_ms() - client->last_send_time > 1000) + { + net_sv_send_waiting_data(client); + client->last_send_time = i_get_time_ms(); + } + } + + if (server_state == SERVER_IN_GAME) + { + net_sv_pump_send_queue(client); + net_sv_check_deadlock(client); + } +} + +static void update_master_server(void) +{ + unsigned int now; + + now = i_get_time_ms(); + + /* The address of the master server can change. Periodically + * re-resolve the master server to update. + */ + + if (now - master_resolve_time > MASTER_RESOLVE_PERIOD * 1000) + { + net_addr_t *new_addr; + + new_addr = net_query_resolve_master(server_context); + net_release_address(master_server); + master_server = new_addr; + + master_resolve_time = now; + } + + /* Possibly refresh our registration with the master server. */ + + if (now - master_refresh_time > MASTER_REFRESH_PERIOD * 1000) + { + net_query_add_to_master(master_server); + master_refresh_time = now; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Add a network module to the server context */ + +void net_sv_add_module(net_module_t *module) +{ + module->init_server(); + net_add_module(server_context, module); +} + +/* Initialize server and wait for connections */ + +void net_sv_init(void) +{ + int i; + + /* initialize send/receive context */ + + server_context = net_new_context(); + + /* no clients yet */ + + for (i = 0; i < MAXNETNODES; ++i) + { + clients[i].active = false; + } + + net_sv_assign_players(); + + server_state = SERVER_WAITING_LAUNCH; + sv_gamemode = indetermined; + server_initialized = true; +} + +void net_sv_register_with_master(void) +{ + /* @category net + * + * When running a server, don't register with the global master server. + * Implies -server. + */ + + if (!m_check_parm("-privateserver")) + { + master_server = net_query_resolve_master(server_context); + } + else + { + master_server = NULL; + } + + /* Send request. */ + + if (master_server != NULL) + { + net_query_add_to_master(master_server); + master_refresh_time = i_get_time_ms(); + master_resolve_time = master_refresh_time; + } +} + +/* Run server code to check for new packets/send packets as the server + * requires + */ + +void net_sv_run(void) +{ + net_addr_t *addr; + net_packet_t *packet; + int i; + + if (!server_initialized) + { + return; + } + + while (net_recv_packet(server_context, &addr, &packet)) + { + net_sv_packet(packet, addr); + net_free_packet(packet); + net_release_address(addr); + } + + if (master_server != NULL) + { + update_master_server(); + } + + /* "Run" any clients that may have things to do, independent of responses + * to received packets + */ + + for (i = 0; i < MAXNETNODES; ++i) + { + if (clients[i].active) + { + net_sv_run_client(&clients[i]); + } + } + + switch (server_state) + { + case SERVER_WAITING_LAUNCH: + break; + + case SERVER_WAITING_START: + check_start_game(); + break; + + case SERVER_IN_GAME: + net_sv_advance_window(); + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] != NULL && client_connected(sv_players[i])) + { + net_sv_check_resends(sv_players[i]); + } + } + break; + } +} + +void net_sv_shutdown(void) +{ + int i; + boolean running; + int start_time; + + if (!server_initialized) + { + return; + } + + fprintf(stderr, "SV: Shutting down server...\n"); + + /* Disconnect all clients */ + + for (i = 0; i < MAXNETNODES; ++i) + { + if (clients[i].active) + { + net_sv_disconnect_client(&clients[i]); + } + } + + /* Wait for all clients to finish disconnecting */ + + start_time = i_get_time_ms(); + running = true; + + while (running) + { + /* Check if any clients are still not finished */ + + running = false; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (clients[i].active) + { + running = true; + } + } + + /* Timed out? */ + + if (i_get_time_ms() - start_time > 5000) + { + running = false; + fprintf(stderr, + "SV: Timed out waiting for clients to disconnect.\n"); + } + + /* Run the client code in case this is a loopback client. */ + + net_cl_run(); + net_sv_run(); + + /* Don't hog the CPU */ + + usleep(1000); + } +} diff --git a/games/NXDoom/src/net_server.h b/games/NXDoom/src/net_server.h new file mode 100644 index 00000000000..9d1db98b928 --- /dev/null +++ b/games/NXDoom/src/net_server.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_server.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Network server code + * + ****************************************************************************/ + +#ifndef NET_SERVER_H +#define NET_SERVER_H + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* initialize server and wait for connections */ + +void net_sv_init(void); + +/* run server: check for new packets received etc. */ + +void net_sv_run(void); + +/* Shut down the server + * Blocks until all clients disconnect, or until a 5 second timeout + */ + +void net_sv_shutdown(void); + +/* Add a network module to the context used by the server */ + +void net_sv_add_module(net_module_t *module); + +/* Register server with master server. */ + +void net_sv_register_with_master(void); + +#endif /* NET_SERVER_H */ diff --git a/games/NXDoom/src/net_structrw.c b/games/NXDoom/src/net_structrw.c new file mode 100644 index 00000000000..371aa4f7a9e --- /dev/null +++ b/games/NXDoom/src/net_structrw.c @@ -0,0 +1,714 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_structrw.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Reading and writing various structures into packets + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_misc.h" +#include "net_packet.h" +#include "net_structrw.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct protocol_name +{ + net_protocol_t protocol; + const char *name; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* String names for the enum values in net_protocol_t, which are what is + * sent over the wire. Every enum value must have an entry in this list. + */ + +static struct protocol_name g_protocol_names[] = +{ + {NET_PROTOCOL_CHOCOLATE_DOOM_0, "CHOCOLATE_DOOM_0"}, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static boolean net_read_blob(net_packet_t *packet, uint8_t *buf, size_t len) +{ + unsigned int b; + int i; + + for (i = 0; i < len; ++i) + { + if (!net_read_int8(packet, &b)) + { + return false; + } + + buf[i] = b; + } + + return true; +} + +static void net_write_blob(net_packet_t *packet, uint8_t *buf, size_t len) +{ + int i; + + for (i = 0; i < len; ++i) + { + net_write_int8(packet, buf[i]); + } +} + +static net_protocol_t parse_protocol_name(const char *name) +{ + int i; + + for (i = 0; i < arrlen(g_protocol_names); ++i) + { + if (!strcmp(g_protocol_names[i].name, name)) + { + return g_protocol_names[i].protocol; + } + } + + return NET_PROTOCOL_UNKNOWN; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void net_write_connect_data(net_packet_t *packet, net_connect_data_t *data) +{ + net_write_int8(packet, data->gamemode); + net_write_int8(packet, data->gamemission); + net_write_int8(packet, data->lowres_turn); + net_write_int8(packet, data->drone); + net_write_int8(packet, data->max_players); + net_write_int8(packet, data->is_freedoom); + net_write_sha1_sum(packet, data->wad_sha1sum); + net_write_sha1_sum(packet, data->deh_sha1sum); + net_write_int8(packet, data->player_class); +} + +boolean net_read_connect_data(net_packet_t *packet, net_connect_data_t *data) +{ + return net_read_int8(packet, (unsigned int *)&data->gamemode) && + net_read_int8(packet, (unsigned int *)&data->gamemission) && + net_read_int8(packet, (unsigned int *)&data->lowres_turn) && + net_read_int8(packet, (unsigned int *)&data->drone) && + net_read_int8(packet, (unsigned int *)&data->max_players) && + net_read_int8(packet, (unsigned int *)&data->is_freedoom) && + net_read_sha1_sum(packet, data->wad_sha1sum) && + net_read_sha1_sum(packet, data->deh_sha1sum) && + net_read_int8(packet, (unsigned int *)&data->player_class); +} + +void net_write_settings(net_packet_t *packet, net_gamesettings_t *settings) +{ + int i; + + net_write_int8(packet, settings->ticdup); + net_write_int8(packet, settings->extratics); + net_write_int8(packet, settings->deathmatch); + net_write_int8(packet, settings->nomonsters); + net_write_int8(packet, settings->fast_monsters); + net_write_int8(packet, settings->respawn_monsters); + net_write_int8(packet, settings->episode); + net_write_int8(packet, settings->map); + net_write_int8(packet, settings->skill); + net_write_int8(packet, settings->gameversion); + net_write_int8(packet, settings->lowres_turn); + net_write_int8(packet, settings->new_sync); + net_write_int32(packet, settings->timelimit); + net_write_int8(packet, settings->loadgame); + net_write_int8(packet, settings->random); + net_write_int8(packet, settings->num_players); + net_write_int8(packet, settings->consoleplayer); + + for (i = 0; i < settings->num_players; ++i) + { + net_write_int8(packet, settings->player_classes[i]); + } +} + +boolean net_read_settings(net_packet_t *packet, net_gamesettings_t *settings) +{ + boolean success; + int i; + + success = + net_read_int8(packet, (unsigned int *)&settings->ticdup) && + net_read_int8(packet, (unsigned int *)&settings->extratics) && + net_read_int8(packet, (unsigned int *)&settings->deathmatch) && + net_read_int8(packet, (unsigned int *)&settings->nomonsters) && + net_read_int8(packet, (unsigned int *)&settings->fast_monsters) && + net_read_int8(packet, (unsigned int *)&settings->respawn_monsters) && + net_read_int8(packet, (unsigned int *)&settings->episode) && + net_read_int8(packet, (unsigned int *)&settings->map) && + net_read_sint8(packet, &settings->skill) && + net_read_int8(packet, (unsigned int *)&settings->gameversion) && + net_read_int8(packet, (unsigned int *)&settings->lowres_turn) && + net_read_int8(packet, (unsigned int *)&settings->new_sync) && + net_read_int32(packet, (unsigned int *)&settings->timelimit) && + net_read_sint8(packet, (signed int *)&settings->loadgame) && + net_read_int8(packet, (unsigned int *)&settings->random) && + net_read_int8(packet, (unsigned int *)&settings->num_players) && + net_read_sint8(packet, (signed int *)&settings->consoleplayer); + + if (!success) + { + return false; + } + + for (i = 0; + i < settings->num_players && i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; + ++i) + { + if (!net_read_int8(packet, + (unsigned int *)&settings->player_classes[i])) + { + return false; + } + } + + return true; +} + +boolean net_read_query_data(net_packet_t *packet, net_querydata_t *query) +{ + boolean success; + + query->version = net_read_safe_string(packet); + + success = query->version != NULL && + net_read_int8(packet, (unsigned int *)&query->server_state) && + net_read_int8(packet, (unsigned int *)&query->num_players) && + net_read_int8(packet, (unsigned int *)&query->max_players) && + net_read_int8(packet, (unsigned int *)&query->gamemode) && + net_read_int8(packet, (unsigned int *)&query->gamemission); + + if (!success) + { + return false; + } + + query->description = net_read_safe_string(packet); + + /* We read the list of protocols supported by the server. However, + * old versions of Chocolate Doom do not support this field; it is + * okay if it cannot be successfully read. + */ + + query->protocol = net_read_protocol_list(packet); + + return query->description != NULL; +} + +void net_write_query_data(net_packet_t *packet, net_querydata_t *query) +{ + net_write_string(packet, query->version); + net_write_int8(packet, query->server_state); + net_write_int8(packet, query->num_players); + net_write_int8(packet, query->max_players); + net_write_int8(packet, query->gamemode); + net_write_int8(packet, query->gamemission); + net_write_string(packet, query->description); + + /* Write a list of all supported protocols. Note that the query->protocol + * field is ignored here; it is only used when receiving. + */ + + net_write_protocol_list(packet); +} + +void net_write_ticcmd_diff(net_packet_t *packet, net_ticdiff_t *diff, + boolean lowres_turn) +{ + /* Header */ + + net_write_int8(packet, diff->diff); + + /* Write the fields which are enabled: */ + + if (diff->diff & NET_TICDIFF_FORWARD) + net_write_int8(packet, diff->cmd.forwardmove); + if (diff->diff & NET_TICDIFF_SIDE) + net_write_int8(packet, diff->cmd.sidemove); + if (diff->diff & NET_TICDIFF_TURN) + { + if (lowres_turn) + { + net_write_int8(packet, diff->cmd.angleturn / 256); + } + else + { + net_write_int16(packet, diff->cmd.angleturn); + } + } + + if (diff->diff & NET_TICDIFF_BUTTONS) + net_write_int8(packet, diff->cmd.buttons); + if (diff->diff & NET_TICDIFF_CONSISTANCY) + net_write_int8(packet, diff->cmd.consistency); + if (diff->diff & NET_TICDIFF_CHATCHAR) + net_write_int8(packet, diff->cmd.chatchar); + if (diff->diff & NET_TICDIFF_RAVEN) + { + net_write_int8(packet, diff->cmd.lookfly); + net_write_int8(packet, diff->cmd.arti); + } + + if (diff->diff & NET_TICDIFF_STRIFE) + { + net_write_int8(packet, diff->cmd.buttons2); + net_write_int16(packet, diff->cmd.inventory); + } +} + +boolean net_read_ticcmd_diff(net_packet_t *packet, net_ticdiff_t *diff, + boolean lowres_turn) +{ + unsigned int val; + signed int sval; + + /* Read header */ + + if (!net_read_int8(packet, &diff->diff)) return false; + + /* Read fields */ + + if (diff->diff & NET_TICDIFF_FORWARD) + { + if (!net_read_sint8(packet, &sval)) return false; + diff->cmd.forwardmove = sval; + } + + if (diff->diff & NET_TICDIFF_SIDE) + { + if (!net_read_sint8(packet, &sval)) return false; + diff->cmd.sidemove = sval; + } + + if (diff->diff & NET_TICDIFF_TURN) + { + if (lowres_turn) + { + if (!net_read_sint8(packet, &sval)) return false; + diff->cmd.angleturn = sval * 256; + } + else + { + if (!net_read_sint16(packet, &sval)) return false; + diff->cmd.angleturn = sval; + } + } + + if (diff->diff & NET_TICDIFF_BUTTONS) + { + if (!net_read_int8(packet, &val)) return false; + diff->cmd.buttons = val; + } + + if (diff->diff & NET_TICDIFF_CONSISTANCY) + { + if (!net_read_int8(packet, &val)) return false; + diff->cmd.consistency = val; + } + + if (diff->diff & NET_TICDIFF_CHATCHAR) + { + if (!net_read_int8(packet, &val)) return false; + diff->cmd.chatchar = val; + } + + if (diff->diff & NET_TICDIFF_RAVEN) + { + if (!net_read_int8(packet, &val)) return false; + diff->cmd.lookfly = val; + + if (!net_read_int8(packet, &val)) return false; + diff->cmd.arti = val; + } + + if (diff->diff & NET_TICDIFF_STRIFE) + { + if (!net_read_int8(packet, &val)) return false; + diff->cmd.buttons2 = val; + + if (!net_read_int16(packet, &val)) return false; + diff->cmd.inventory = val; + } + + return true; +} + +void net_ticcmd_diff(ticcmd_t *tic1, ticcmd_t *tic2, net_ticdiff_t *diff) +{ + diff->diff = 0; + diff->cmd = *tic2; + + if (tic1->forwardmove != tic2->forwardmove) + diff->diff |= NET_TICDIFF_FORWARD; + if (tic1->sidemove != tic2->sidemove) diff->diff |= NET_TICDIFF_SIDE; + if (tic1->angleturn != tic2->angleturn) diff->diff |= NET_TICDIFF_TURN; + if (tic1->buttons != tic2->buttons) diff->diff |= NET_TICDIFF_BUTTONS; + if (tic1->consistency != tic2->consistency) + diff->diff |= NET_TICDIFF_CONSISTANCY; + if (tic2->chatchar != 0) diff->diff |= NET_TICDIFF_CHATCHAR; + + /* Heretic/Hexen-specific */ + + if (tic1->lookfly != tic2->lookfly || tic2->arti != 0) + diff->diff |= NET_TICDIFF_RAVEN; + + /* Strife-specific */ + + if (tic1->buttons2 != tic2->buttons2 || tic2->inventory != 0) + diff->diff |= NET_TICDIFF_STRIFE; +} + +void net_ticcmd_patch(ticcmd_t *src, net_ticdiff_t *diff, ticcmd_t *dest) +{ + memmove(dest, src, sizeof(ticcmd_t)); + + /* Apply the diff */ + + if (diff->diff & NET_TICDIFF_FORWARD) + dest->forwardmove = diff->cmd.forwardmove; + if (diff->diff & NET_TICDIFF_SIDE) dest->sidemove = diff->cmd.sidemove; + if (diff->diff & NET_TICDIFF_TURN) dest->angleturn = diff->cmd.angleturn; + if (diff->diff & NET_TICDIFF_BUTTONS) dest->buttons = diff->cmd.buttons; + if (diff->diff & NET_TICDIFF_CONSISTANCY) + dest->consistency = diff->cmd.consistency; + + if (diff->diff & NET_TICDIFF_CHATCHAR) + dest->chatchar = diff->cmd.chatchar; + else + dest->chatchar = 0; + + /* Heretic/Hexen specific: */ + + if (diff->diff & NET_TICDIFF_RAVEN) + { + dest->lookfly = diff->cmd.lookfly; + dest->arti = diff->cmd.arti; + } + else + { + dest->arti = 0; + } + + /* Strife-specific: */ + + if (diff->diff & NET_TICDIFF_STRIFE) + { + dest->buttons2 = diff->cmd.buttons2; + dest->inventory = diff->cmd.inventory; + } + else + { + dest->inventory = 0; + } +} + +/* net_full_ticcmd_t */ + +boolean net_read_full_ticcmd(net_packet_t *packet, net_full_ticcmd_t *cmd, + boolean lowres_turn) +{ + unsigned int bitfield; + int i; + + /* Latency */ + + if (!net_read_sint16(packet, &cmd->latency)) + { + return false; + } + + /* Regenerate playeringame from the "header" bitfield */ + + if (!net_read_int8(packet, &bitfield)) + { + return false; + } + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + cmd->playeringame[i] = (bitfield & (1 << i)) != 0; + } + + /* Read cmds */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (cmd->playeringame[i]) + { + if (!net_read_ticcmd_diff(packet, &cmd->cmds[i], lowres_turn)) + { + return false; + } + } + } + + return true; +} + +void net_write_full_ticcmd(net_packet_t *packet, net_full_ticcmd_t *cmd, + boolean lowres_turn) +{ + unsigned int bitfield; + int i; + + /* Write the latency */ + + net_write_int16(packet, cmd->latency); + + /* Write "header" byte indicating which players are active + * in this ticcmd + */ + + bitfield = 0; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (cmd->playeringame[i]) + { + bitfield |= 1 << i; + } + } + + net_write_int8(packet, bitfield); + + /* Write player ticcmds */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (cmd->playeringame[i]) + { + net_write_ticcmd_diff(packet, &cmd->cmds[i], lowres_turn); + } + } +} + +void net_write_wait_data(net_packet_t *packet, net_waitdata_t *data) +{ + int i; + + net_write_int8(packet, data->num_players); + net_write_int8(packet, data->num_drones); + net_write_int8(packet, data->ready_players); + net_write_int8(packet, data->max_players); + net_write_int8(packet, data->is_controller); + net_write_int8(packet, data->consoleplayer); + + for (i = 0; + i < data->num_players && i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; + ++i) + { + net_write_string(packet, data->player_names[i]); + net_write_string(packet, data->player_addrs[i]); + } + + net_write_sha1_sum(packet, data->wad_sha1sum); + net_write_sha1_sum(packet, data->deh_sha1sum); + net_write_int8(packet, data->is_freedoom); +} + +boolean net_read_wait_data(net_packet_t *packet, net_waitdata_t *data) +{ + int i; + char *s; + + if (!net_read_int8(packet, (unsigned int *)&data->num_players) || + !net_read_int8(packet, (unsigned int *)&data->num_drones) || + !net_read_int8(packet, (unsigned int *)&data->ready_players) || + !net_read_int8(packet, (unsigned int *)&data->max_players) || + !net_read_int8(packet, (unsigned int *)&data->is_controller) || + !net_read_sint8(packet, &data->consoleplayer)) + { + return false; + } + + for (i = 0; + i < data->num_players && i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; + ++i) + { + s = net_read_string(packet); + + if (s == NULL || strlen(s) >= CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME) + { + return false; + } + + m_str_copy(data->player_names[i], s, + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME); + + s = net_read_string(packet); + + if (s == NULL || strlen(s) >= CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME) + { + return false; + } + + m_str_copy(data->player_addrs[i], s, + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME); + } + + return net_read_sha1_sum(packet, data->wad_sha1sum) && + net_read_sha1_sum(packet, data->deh_sha1sum) && + net_read_int8(packet, (unsigned int *)&data->is_freedoom); +} + +boolean net_read_sha1_sum(net_packet_t *packet, sha1_digest_t digest) +{ + return net_read_blob(packet, digest, sizeof(sha1_digest_t)); +} + +void net_write_sha1_sum(net_packet_t *packet, sha1_digest_t digest) +{ + net_write_blob(packet, digest, sizeof(sha1_digest_t)); +} + +boolean net_read_prng_seed(net_packet_t *packet, prng_seed_t seed) +{ + return net_read_blob(packet, seed, sizeof(prng_seed_t)); +} + +void net_write_prng_seed(net_packet_t *packet, prng_seed_t seed) +{ + net_write_blob(packet, seed, sizeof(prng_seed_t)); +} + +/* net_read_protocol reads a single string-format protocol name from the + * given packet, returning NET_PROTOCOL_UNKNOWN if the string describes an + * unknown protocol. + */ + +net_protocol_t net_read_protocol(net_packet_t *packet) +{ + const char *name; + + name = net_read_string(packet); + if (name == NULL) + { + return NET_PROTOCOL_UNKNOWN; + } + + return parse_protocol_name(name); +} + +/* net_write_protocol writes a single string-format protocol name to a + * packet. + */ + +void net_write_protocol(net_packet_t *packet, net_protocol_t protocol) +{ + int i; + + for (i = 0; i < arrlen(g_protocol_names); ++i) + { + if (g_protocol_names[i].protocol == protocol) + { + net_write_string(packet, g_protocol_names[i].name); + return; + } + } + + /* If you add an entry to the net_protocol_t enum, a corresponding entry + * must be added to the protocol_names list. + */ + + i_error("net_write_protocol: protocol %d missing from protocol_names " + "list; please add it.", + protocol); +} + +/* net_read_protocol_list reads a list of string-format protocol names from + * the given packet, returning a single protocol number. The protocol that is + * returned is the last protocol in the list that is a supported protocol. If + * no recognized protocols are read, NET_PROTOCOL_UNKNOWN is returned. + */ + +net_protocol_t net_read_protocol_list(net_packet_t *packet) +{ + net_protocol_t result; + unsigned int num_protocols; + int i; + + if (!net_read_int8(packet, &num_protocols)) + { + return NET_PROTOCOL_UNKNOWN; + } + + result = NET_PROTOCOL_UNKNOWN; + + for (i = 0; i < num_protocols; ++i) + { + net_protocol_t p; + const char *name; + + name = net_read_string(packet); + if (name == NULL) + { + return NET_PROTOCOL_UNKNOWN; + } + + p = parse_protocol_name(name); + if (p != NET_PROTOCOL_UNKNOWN) + { + result = p; + } + } + + return result; +} + +/* net_write_protocol_list writes a list of string-format protocol names into + * the given packet, all the supported protocols in the net_protocol_t enum. + * This is slightly different to other functions in this file, in that there + * is nothing the caller can "choose" to write; the built-in list of all + * protocols is always sent. + */ + +void net_write_protocol_list(net_packet_t *packet) +{ + int i; + + net_write_int8(packet, NET_NUM_PROTOCOLS); + + for (i = 0; i < NET_NUM_PROTOCOLS; ++i) + { + net_write_protocol(packet, i); + } +} diff --git a/games/NXDoom/src/net_structrw.h b/games/NXDoom/src/net_structrw.h new file mode 100644 index 00000000000..2a924173cac --- /dev/null +++ b/games/NXDoom/src/net_structrw.h @@ -0,0 +1,80 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_structrw.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef NET_STRUCTRW_H +#define NET_STRUCTRW_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "aes_prng.h" +#include "net_defs.h" +#include "net_packet.h" +#include "sha1.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void net_write_connect_data(net_packet_t *packet, net_connect_data_t *data); +boolean net_read_connect_data(net_packet_t *packet, + net_connect_data_t *data); + +extern void net_write_settings(net_packet_t *packet, + net_gamesettings_t *settings); +extern boolean net_read_settings(net_packet_t *packet, + net_gamesettings_t *settings); + +extern void net_write_query_data(net_packet_t *packet, + net_querydata_t *querydata); +extern boolean net_read_query_data(net_packet_t *packet, + net_querydata_t *querydata); + +extern void net_write_ticcmd_diff(net_packet_t *packet, net_ticdiff_t *diff, + boolean lowres_turn); +extern boolean net_read_ticcmd_diff(net_packet_t *packet, + net_ticdiff_t *diff, boolean lowres_turn); +extern void net_ticcmd_diff(ticcmd_t *tic1, ticcmd_t *tic2, + net_ticdiff_t *diff); +extern void net_ticcmd_patch(ticcmd_t *src, net_ticdiff_t *diff, + ticcmd_t *dest); + +boolean net_read_full_ticcmd(net_packet_t *packet, net_full_ticcmd_t *cmd, + boolean lowres_turn); +void net_write_full_ticcmd(net_packet_t *packet, net_full_ticcmd_t *cmd, + boolean lowres_turn); + +boolean net_read_sha1_sum(net_packet_t *packet, sha1_digest_t digest); +void net_write_sha1_sum(net_packet_t *packet, sha1_digest_t digest); + +void net_write_wait_data(net_packet_t *packet, net_waitdata_t *data); +boolean net_read_wait_data(net_packet_t *packet, net_waitdata_t *data); + +boolean net_read_prng_seed(net_packet_t *packet, prng_seed_t seed); +void net_write_prng_seed(net_packet_t *packet, prng_seed_t seed); + +/* Protocol list exchange. */ + +net_protocol_t net_read_protocol(net_packet_t *packet); +void net_write_protocol(net_packet_t *packet, net_protocol_t protocol); +net_protocol_t net_read_protocol_list(net_packet_t *packet); +void net_write_protocol_list(net_packet_t *packet); + +#endif /* NET_STRUCTRW_H */ diff --git a/games/NXDoom/src/p_rejectpad.c b/games/NXDoom/src/p_rejectpad.c new file mode 100644 index 00000000000..3099d59f92b --- /dev/null +++ b/games/NXDoom/src/p_rejectpad.c @@ -0,0 +1,96 @@ +/**************************************************************************** + * apps/games/NXDoom/src/p_rejectpad.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Padding of Reject Lump + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "m_argv.h" +#include "p_rejectpad.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Pad the REJECT lump with extra data when the lump is too small, + * to simulate a REJECT buffer overflow in Vanilla Doom. + */ + +void pad_reject_array(byte *array, unsigned int len, int totallines) +{ + unsigned int i; + unsigned int byte_num; + byte *dest; + unsigned int padvalue; + + /* Values to pad the REJECT array with: */ + + unsigned int rejectpad[4] = + { + 0, /* Size */ + 0, /* Part of z_zone block header */ + 50, /* PU_LEVEL */ + 0x1d4a11 /* DOOM_CONST_ZONEID */ + }; + + rejectpad[0] = ((totallines * 4 + 3) & ~3) + 24; + + /* Copy values from rejectpad into the destination array. */ + + dest = array; + + for (i = 0; i < len && i < sizeof(rejectpad); ++i) + { + byte_num = i % 4; + *dest = (rejectpad[i / 4] >> (byte_num * 8)) & 0xff; + ++dest; + } + + /* We only have a limited pad size. Print a warning if the + * REJECT lump is too small. + */ + + if (len > sizeof(rejectpad)) + { + fprintf(stderr, + "pad_reject_array: REJECT lump too short to pad! (%u > %i)\n", + len, (int)sizeof(rejectpad)); + + /* Pad remaining space with 0 (or 0xff, if specified on command line). + */ + + if (m_check_parm("-reject_pad_with_ff")) + { + padvalue = 0xff; + } + else + { + padvalue = 0x00; + } + + memset(array + sizeof(rejectpad), padvalue, len - sizeof(rejectpad)); + } +} diff --git a/games/NXDoom/src/p_rejectpad.h b/games/NXDoom/src/p_rejectpad.h new file mode 100644 index 00000000000..adb0372dd85 --- /dev/null +++ b/games/NXDoom/src/p_rejectpad.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * apps/games/NXDoom/src/p_rejectpad.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Padding of Reject Lump + * + ****************************************************************************/ + +#ifndef __P_REJECTPAD__ +#define __P_REJECTPAD__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Padding of Reject Lump */ + +void pad_reject_array(byte *array, unsigned int len, int totallines); + +#endif /* __P_REJECTPAD__ */ diff --git a/games/NXDoom/src/sha1.h b/games/NXDoom/src/sha1.h new file mode 100644 index 00000000000..eb0f7c360bc --- /dev/null +++ b/games/NXDoom/src/sha1.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * apps/games/NXDoom/src/sha1.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * SHA-1 digest. + * + ****************************************************************************/ + +#ifndef __SHA1_H__ +#define __SHA1_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define sha1_updatestring(ctx, str) \ + sha1update((ctx), (byte *)(str), strlen((str)) + 1) + +#define sha1_updateint32(ctx, val) \ + do \ + { \ + byte __sha1__tmp__buf[4]; \ + __sha1__tmp__buf[0] = ((val) >> 24) & 0xff; \ + __sha1__tmp__buf[1] = ((val) >> 16) & 0xff; \ + __sha1__tmp__buf[2] = ((val) >> 8) & 0xff; \ + __sha1__tmp__buf[3] = (val) & 0xff; \ + sha1update((ctx), __sha1__tmp__buf, 4); \ + } \ + while (0) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef byte sha1_digest_t[SHA1_DIGEST_LENGTH]; + +#endif /* __SHA1_H__ */ diff --git a/games/NXDoom/src/tables.c b/games/NXDoom/src/tables.c new file mode 100644 index 00000000000..61f0198c3da --- /dev/null +++ b/games/NXDoom/src/tables.c @@ -0,0 +1,2369 @@ +/**************************************************************************** + * apps/games/NXDoom/src/tables.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "tables.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const fixed_t 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, +}; + +const fixed_t 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, +}; + +const fixed_t *finecosine = &finesine[FINEANGLES / 4]; + +const 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, +}; + +/* Now where did these came from? */ + +const 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, + }, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* 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. The +1 size is to handle + * the case when x==y without additional checking. + */ + +int slope_div(unsigned int num, unsigned int den) +{ + unsigned answer; + + if (den < 512) + { + return SLOPERANGE; + } + else + { + answer = (num << 3) / (den >> 8); + + if (answer <= SLOPERANGE) + { + return answer; + } + else + { + return SLOPERANGE; + } + } +} diff --git a/games/NXDoom/src/tables.h b/games/NXDoom/src/tables.h new file mode 100644 index 00000000000..1a30ee74ef2 --- /dev/null +++ b/games/NXDoom/src/tables.h @@ -0,0 +1,125 @@ +/**************************************************************************** + * apps/games/NXDoom/src/tables.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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. + * + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __TABLES__ +#define __TABLES__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +#include "m_fixed.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define FINEANGLES 8192 +#define FINEMASK (FINEANGLES - 1) + +/* 0x100000000 to 0x2000 */ + +#define ANGLETOFINESHIFT 19 + +/* Binary Angle Measurement, BAM. */ + +#define ANG45 0x20000000 +#define ANG90 0x40000000 +#define ANG180 0x80000000 +#define ANG270 0xc0000000 +#define ANG_MAX 0xffffffff + +#define ANG1 (ANG45 / 45) +#define ANG60 (ANG180 / 3) + +/* Heretic code uses this definition as though it represents one + * degree, but it is not! This is actually ~1.40 degrees. + */ + +#define ANG1_X 0x01000000 + +#define SLOPERANGE 2048 +#define SLOPEBITS 11 +#define DBITS (FRACBITS - SLOPEBITS) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef unsigned int angle_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Effective size is 10240. */ + +extern const fixed_t finesine[5 * FINEANGLES / 4]; + +/* Reuse data, is just PI/2 pahse shift. */ + +extern const fixed_t *finecosine; + +/* Effective size is 4096. */ + +extern const fixed_t finetangent[FINEANGLES / 2]; + +/* Gamma correction tables. */ + +extern const byte gammatable[5][256]; + +/* Effective size is 2049; + * The +1 size is to handle the case when x==y without additional checking. + */ + +extern const angle_t tantoangle[SLOPERANGE + 1]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Utility function, called by r_point_to_angle. */ + +int slope_div(unsigned int num, unsigned int den); + +#endif /* __TABLES__ */ diff --git a/games/NXDoom/src/v_diskicon.c b/games/NXDoom/src/v_diskicon.c new file mode 100644 index 00000000000..755d281a93a --- /dev/null +++ b/games/NXDoom/src/v_diskicon.c @@ -0,0 +1,185 @@ +/**************************************************************************** + * apps/games/NXDoom/src/v_diskicon.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Disk load indicator. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_str.h" +#include "doomtype.h" +#include "i_swap.h" +#include "i_video.h" +#include "m_argv.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "v_diskicon.h" + +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Only display the disk icon if more then this much bytes have been read + * during the previous tic. + */ + +static const int diskicon_threshold = 20 * 1024; + +/* Two buffers: disk_data contains the data representing the disk icon + * (raw, not a patch_t) while saved_background is an equivalently-sized + * buffer where we save the background data while the disk is on screen. + */ + +static pixel_t *disk_data; +static pixel_t *saved_background; + +static int loading_disk_xoffs = 0; +static int loading_disk_yoffs = 0; + +/* Number of bytes read since the last call to v_draw_disk_icon(). */ + +static size_t recent_bytes_read = 0; +static boolean disk_drawn; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void copy_region(pixel_t *dest, int dest_pitch, pixel_t *src, + int src_pitch, int w, int h) +{ + pixel_t *s, *d; + int y; + + s = src; + d = dest; + for (y = 0; y < h; ++y) + { + memcpy(d, s, w * sizeof(*d)); + s += src_pitch; + d += dest_pitch; + } +} + +static void save_disk_data(const char *disk_lump, int xoffs, int yoffs) +{ + pixel_t *tmpscreen; + patch_t *disk; + + /* Allocate a complete temporary screen where we'll draw the patch. */ + + tmpscreen = z_malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*tmpscreen), + PU_STATIC, NULL); + memset(tmpscreen, 0, SCREENWIDTH * SCREENHEIGHT * sizeof(*tmpscreen)); + v_use_buffer(tmpscreen); + + /* Buffer where we'll save the disk data. */ + + if (disk_data != NULL) + { + z_free(disk_data); + disk_data = NULL; + } + + disk_data = z_malloc(LOADING_DISK_W * LOADING_DISK_H * sizeof(*disk_data), + PU_STATIC, NULL); + + /* Draw the patch and save the result to disk_data. */ + + disk = w_cache_lump_name(disk_lump, PU_STATIC); + v_draw_patch(loading_disk_xoffs, loading_disk_yoffs, disk); + copy_region(disk_data, LOADING_DISK_W, + tmpscreen + yoffs * SCREENWIDTH + xoffs, SCREENWIDTH, + LOADING_DISK_W, LOADING_DISK_H); + w_release_lump_name(disk_lump); + + v_restore_buffer(); + z_free(tmpscreen); +} + +static pixel_t *disk_region_pointer(void) +{ + return i_video_buffer + loading_disk_yoffs * SCREENWIDTH + + loading_disk_xoffs; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void v_enable_loading_disk(const char *lump_name, int xoffs, int yoffs) +{ + loading_disk_xoffs = xoffs; + loading_disk_yoffs = yoffs; + + if (saved_background != NULL) + { + z_free(saved_background); + saved_background = NULL; + } + + saved_background = + z_malloc(LOADING_DISK_W * LOADING_DISK_H * sizeof(*saved_background), + PU_STATIC, NULL); + save_disk_data(lump_name, xoffs, yoffs); +} + +void v_begin_read(size_t nbytes) +{ + recent_bytes_read += nbytes; +} + +void v_draw_disk_icon(void) +{ + if (disk_data != NULL && recent_bytes_read > diskicon_threshold) + { + /* Save the background behind the disk before we draw it. */ + + copy_region(saved_background, LOADING_DISK_W, disk_region_pointer(), + SCREENWIDTH, LOADING_DISK_W, LOADING_DISK_H); + + /* Write the disk to the screen buffer. */ + + copy_region(disk_region_pointer(), SCREENWIDTH, disk_data, + LOADING_DISK_W, LOADING_DISK_W, LOADING_DISK_H); + disk_drawn = true; + } + + recent_bytes_read = 0; +} + +void v_restore_disk_background(void) +{ + if (disk_drawn) + { + /* Restore the background. */ + + copy_region(disk_region_pointer(), SCREENWIDTH, saved_background, + LOADING_DISK_W, LOADING_DISK_W, LOADING_DISK_H); + + disk_drawn = false; + } +} diff --git a/games/NXDoom/src/v_diskicon.h b/games/NXDoom/src/v_diskicon.h new file mode 100644 index 00000000000..4bd9b54aa80 --- /dev/null +++ b/games/NXDoom/src/v_diskicon.h @@ -0,0 +1,46 @@ +/**************************************************************************** + * apps/games/NXDoom/src/v_diskicon.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Disk load indicator. + * + ****************************************************************************/ + +#ifndef __V_DISKICON__ +#define __V_DISKICON__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Dimensions of the flashing "loading" disk icon */ + +#define LOADING_DISK_W 16 +#define LOADING_DISK_H 16 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +extern void v_enable_loading_disk(const char *lump_name, int xoffs, + int yoffs); +extern void v_begin_read(size_t nbytes); +extern void v_draw_disk_icon(void); +extern void v_restore_disk_background(void); + +#endif /* __V_DISKICON__ */ diff --git a/games/NXDoom/src/v_patch.h b/games/NXDoom/src/v_patch.h new file mode 100644 index 00000000000..5bfac559c04 --- /dev/null +++ b/games/NXDoom/src/v_patch.h @@ -0,0 +1,70 @@ +/**************************************************************************** + * apps/games/NXDoom/src/v_patch.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Refresh/rendering module, shared data struct definitions. + * + ****************************************************************************/ + +#ifndef V_PATCH_H +#define V_PATCH_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 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. + */ + +begin_packed_struct struct patch_t +{ + 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] */ +} end_packed_struct; + +typedef struct patch_t patch_t; + +/* posts are runs of non masked source pixels */ + +begin_packed_struct struct post_t +{ + byte topdelta; /* -1 is the last post in a column */ + byte length; /* length data bytes follows */ +} end_packed_struct; + +typedef struct post_t post_t; + +/* column_t is a list of 0 or more post_t, (byte)-1 terminated */ + +typedef post_t column_t; + +#endif /* V_PATCH_H */ diff --git a/games/NXDoom/src/v_video.c b/games/NXDoom/src/v_video.c new file mode 100644 index 00000000000..55f455ccee8 --- /dev/null +++ b/games/NXDoom/src/v_video.c @@ -0,0 +1,881 @@ +/**************************************************************************** + * apps/games/NXDoom/src/v_video.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Gamma correction LUT stuff. + * Functions to draw patches (by post) directly to screen. + * Functions to blit a block to the screen. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "i_system.h" + +#include "doomtype.h" + +#include "deh_str.h" +#include "i_input.h" +#include "i_swap.h" +#include "i_video.h" +#include "m_bbox.h" +#include "m_misc.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "config.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MOUSE_SPEED_BOX_WIDTH 120 +#define MOUSE_SPEED_BOX_HEIGHT 9 +#define MOUSE_SPEED_BOX_X (SCREENWIDTH - MOUSE_SPEED_BOX_WIDTH - 10) +#define MOUSE_SPEED_BOX_Y 15 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* SCREEN SHOTS */ + +begin_packed_struct struct pcx_t +{ + 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 */ +} end_packed_struct; + +typedef struct pcx_t pcx_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The screen buffer that the v_video.c code draws to. */ + +static pixel_t *dest_screen = NULL; + +/* haleyjd 08/28/10: clipping callback function for patches. + * This is needed for Chocolate Strife, which clips patches to the screen. + */ + +static vpatchclipfunc_t patchclip_callback = NULL; + +/* Highest seen mouse turn speed. We scale the range of the thermometer + * according to this value, so that it never exceeds the range. Initially + * this is set to a 1:1 setting where 1 pixel = 1 unit of speed. + */ + +static int max_seen_speed = MOUSE_SPEED_BOX_WIDTH - 1; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Blending table used for fuzzpatch, etc. Only used in Heretic/Hexen */ + +byte *tinttable = NULL; + +/* villsa [STRIFE] Blending table used for Strife */ + +byte *xlatab = NULL; + +int dirtybox[4]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void draw_accelerating_box(int speed) +{ + int red; + int white; + int yellow; + int original_speed; + int redline_x; + int linelen; + + red = i_get_palette_index(0xff, 0x00, 0x00); + white = i_get_palette_index(0xff, 0xff, 0xff); + yellow = i_get_palette_index(0xff, 0xff, 0x00); + + /* Calculate the position of the red threshold line when calibrating + * acceleration. This is 1/3 of the way along the box. + */ + + redline_x = MOUSE_SPEED_BOX_WIDTH / 3; + + if (speed >= mouse_threshold) + { + /* Undo acceleration and get back the original mouse speed */ + + original_speed = speed - mouse_threshold; + original_speed = (int)(original_speed / mouse_acceleration); + original_speed += mouse_threshold; + + linelen = (original_speed * redline_x) / mouse_threshold; + } + else + { + linelen = (speed * redline_x) / mouse_threshold; + } + + /* Horizontal "thermometer" */ + + if (linelen > MOUSE_SPEED_BOX_WIDTH - 1) + { + linelen = MOUSE_SPEED_BOX_WIDTH - 1; + } + + if (linelen < redline_x) + { + v_draw_horiz_line(MOUSE_SPEED_BOX_X + 1, + MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, + linelen, white); + } + else + { + v_draw_horiz_line(MOUSE_SPEED_BOX_X + 1, + MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, + redline_x, white); + v_draw_horiz_line(MOUSE_SPEED_BOX_X + redline_x, + MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, + linelen - redline_x, yellow); + } + + /* Draw acceleration threshold line */ + + v_draw_vert_line(MOUSE_SPEED_BOX_X + redline_x, MOUSE_SPEED_BOX_Y + 1, + MOUSE_SPEED_BOX_HEIGHT - 2, red); +} + +static void draw_non_accelerating_box(int speed) +{ + int white; + int linelen; + + white = i_get_palette_index(0xff, 0xff, 0xff); + + if (speed > max_seen_speed) + { + max_seen_speed = speed; + } + + /* Draw horizontal "thermometer": */ + + linelen = speed * (MOUSE_SPEED_BOX_WIDTH - 1) / max_seen_speed; + + v_draw_horiz_line(MOUSE_SPEED_BOX_X + 1, + MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, linelen, + white); +} + +static void write_pcx_file(char *filename, pixel_t *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, NULL); + + 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(1); + pcx->vres = SHORT(1); + memset(pcx->palette, 0, sizeof(pcx->palette)); + pcx->reserved = 0; /* PCX spec: reserved byte must be zero */ + pcx->color_planes = 1; /* chunky image */ + pcx->bytes_per_line = SHORT(width); + pcx->palette_type = SHORT(2); /* not a grey scale */ + 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 = pack - (byte *)pcx; + m_write_file(filename, pcx, length); + + z_free(pcx); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void v_mark_rect(int x, int y, int width, int height) +{ + /* If we are temporarily using an alternate screen, do not affect the + * update box. + */ + + if (dest_screen == i_video_buffer) + { + m_add_to_box(dirtybox, x, y); + m_add_to_box(dirtybox, x + width - 1, y + height - 1); + } +} + +void v_copy_rect(int srcx, int srcy, pixel_t *source, int width, int height, + int destx, int desty) +{ + pixel_t *src; + pixel_t *dest; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (srcx < 0 || srcx + width > SCREENWIDTH || srcy < 0 || + srcy + height > SCREENHEIGHT || destx < 0 || + destx + width > SCREENWIDTH || desty < 0 || + desty + height > SCREENHEIGHT) + { + i_error("Bad v_copy_rect"); + } +#endif + + v_mark_rect(destx, desty, width, height); + + src = source + SCREENWIDTH * srcy + srcx; + dest = dest_screen + SCREENWIDTH * desty + destx; + + for (; height > 0; height--) + { + memcpy(dest, src, width * sizeof(*dest)); + src += SCREENWIDTH; + dest += SCREENWIDTH; + } +} + +/* v_set_patch_clip_callback + * + * haleyjd 08/28/10: Added for Strife support. + * By calling this function, you can setup runtime error checking for patch + * clipping. Strife never caused errors by drawing patches partway + * off-screen. + * + * Some versions of vanilla DOOM also behaved differently than the default + * implementation, so this could possibly be extended to those as well for + * accurate emulation. + */ + +void v_set_patch_clip_callback(vpatchclipfunc_t func) +{ + patchclip_callback = func; +} + +/* V_DrawPatch + * Masks a column based masked pic to the screen. + */ + +void v_draw_patch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop; + pixel_t *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + /* haleyjd 08/28/10: Strife needs silent error checking here. */ + + if (patchclip_callback) + { + if (!patchclip_callback(patch, x, y)) return; + } + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || + y + SHORT(patch->height) > SCREENHEIGHT) + { + i_error("Bad V_DrawPatch"); + } +#endif + + v_mark_rect(x, y, SHORT(patch->width), SHORT(patch->height)); + + col = 0; + desttop = dest_screen + 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_draw_patch_flipped + * Masks a column based masked pic to the screen. + * Flips horizontally, e.g. to mirror face. + */ + +void v_draw_patch_flipped(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop; + pixel_t *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + /* haleyjd 08/28/10: Strife needs silent error checking here. */ + + if (patchclip_callback) + { + if (!patchclip_callback(patch, x, y)) return; + } + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || + y + SHORT(patch->height) > SCREENHEIGHT) + { + i_error("Bad v_draw_patch_flipped"); + } +#endif + + v_mark_rect(x, y, SHORT(patch->width), SHORT(patch->height)); + + col = 0; + desttop = dest_screen + 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); + } + } +} + +/* v_draw_patch_direct + * Draws directly to the screen on the pc. + */ + +void v_draw_patch_direct(int x, int y, patch_t *patch) +{ + v_draw_patch(x, y, patch); +} + +/* v_draw_tl_patch + * Masks a column based translucent masked pic to the screen. + */ + +void v_draw_tl_patch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop; + pixel_t *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || + y + SHORT(patch->height) > SCREENHEIGHT) + { + i_error("Bad v_draw_tl_patch"); + } + + col = 0; + desttop = dest_screen + 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 = tinttable[*dest + ((*source++) << 8)]; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } + } +} + +/* v_draw_xla_patch + * villsa [STRIFE] Masks a column based translucent masked pic to the screen. + */ + +void v_draw_xla_patch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop, *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (patchclip_callback) + { + if (!patchclip_callback(patch, x, y)) return; + } + + col = 0; + desttop = dest_screen + 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 = xlatab[*dest + ((*source) << 8)]; + source++; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } + } +} + +/* v_draw_alt_tl_patch + * Masks a column based translucent masked pic to the screen. + */ + +void v_draw_alt_tl_patch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop; + pixel_t *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || + y + SHORT(patch->height) > SCREENHEIGHT) + { + i_error("Bad v_draw_alt_tl_patch"); + } + + col = 0; + desttop = dest_screen + 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 = tinttable[((*dest) << 8) + *source++]; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } + } +} + +/* v_draw_shadowed_patch + * Masks a column based masked pic to the screen. + */ + +void v_draw_shadowed_patch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop; + pixel_t *dest; + byte *source; + pixel_t *desttop2; + pixel_t *dest2; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || + y + SHORT(patch->height) > SCREENHEIGHT) + { + i_error("Bad v_draw_shadowed_patch"); + } + + col = 0; + desttop = dest_screen + y * SCREENWIDTH + x; + desttop2 = dest_screen + (y + 2) * SCREENWIDTH + x + 2; + + w = SHORT(patch->width); + for (; col < w; x++, col++, desttop++, desttop2++) + { + 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; + dest2 = desttop2 + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest2 = tinttable[((*dest2) << 8)]; + dest2 += SCREENWIDTH; + *dest = *source++; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } + } +} + +/* Load tint table from TINTTAB lump. */ + +void v_load_tint_table(void) +{ + tinttable = w_cache_lump_name("TINTTAB", PU_STATIC); +} + +/* v_load_xla_table + * villsa [STRIFE] Load xla table from XLATAB lump. + */ + +void v_load_xla_table(void) +{ + xlatab = w_cache_lump_name("XLATAB", PU_STATIC); +} + +/* v_draw_block + * Draw a linear block of pixels into the view buffer. + */ + +void v_draw_block(int x, int y, int width, int height, pixel_t *src) +{ + pixel_t *dest; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (x < 0 || x + width > SCREENWIDTH || y < 0 || y + height > SCREENHEIGHT) + { + i_error("Bad v_draw_block"); + } +#endif + + v_mark_rect(x, y, width, height); + + dest = dest_screen + y * SCREENWIDTH + x; + + while (height--) + { + memcpy(dest, src, width * sizeof(*dest)); + src += width; + dest += SCREENWIDTH; + } +} + +void v_draw_filled_box(int x, int y, int w, int h, int c) +{ + pixel_t *buf, *buf1; + int x1; + int y1; + + buf = i_video_buffer + SCREENWIDTH * y + x; + + for (y1 = 0; y1 < h; ++y1) + { + buf1 = buf; + + for (x1 = 0; x1 < w; ++x1) + { + *buf1++ = c; + } + + buf += SCREENWIDTH; + } +} + +void v_draw_horiz_line(int x, int y, int w, int c) +{ + pixel_t *buf; + int x1; + + buf = i_video_buffer + SCREENWIDTH * y + x; + + for (x1 = 0; x1 < w; ++x1) + { + *buf++ = c; + } +} + +void v_draw_vert_line(int x, int y, int h, int c) +{ + pixel_t *buf; + int y1; + + buf = i_video_buffer + SCREENWIDTH * y + x; + + for (y1 = 0; y1 < h; ++y1) + { + *buf = c; + buf += SCREENWIDTH; + } +} + +void v_draw_box(int x, int y, int w, int h, int c) +{ + v_draw_horiz_line(x, y, w, c); + v_draw_horiz_line(x, y + h - 1, w, c); + v_draw_vert_line(x, y, h, c); + v_draw_vert_line(x + w - 1, y, h, c); +} + +/* Draw a "raw" screen (lump containing raw data to blit directly + * to the screen) + */ + +void v_draw_raw_screen(pixel_t *raw) +{ + memcpy(dest_screen, raw, + SCREENWIDTH * SCREENHEIGHT * sizeof(*dest_screen)); +} + +void v_init(void) +{ + /* no-op! + * There used to be separate screens that could be drawn to; these are + * now handled in the upper layers. + */ +} + +/* Set the buffer that the code draws to. */ + +void v_use_buffer(pixel_t *buffer) +{ + dest_screen = buffer; +} + +/* Restore screen buffer to the i_video screen buffer. */ + +void v_restore_buffer(void) +{ + dest_screen = i_video_buffer; +} + +void v_screenshot(const char *format) +{ + int i; + char lbmname[16]; /* haleyjd 20110213: BUG FIX - 12 is too small! */ + const char *ext; + + /* find a file name to save it to */ + + ext = "pcx"; + + for (i = 0; i <= 99; i++) + { + snprintf(lbmname, sizeof(lbmname), format, i, ext); + + if (!m_file_exists(lbmname)) + { + break; /* file doesn't exist */ + } + } + + if (i == 100) + { + i_error("v_screenshot: Couldn't create a PCX"); + } + + /* save the pcx file */ + + write_pcx_file(lbmname, i_video_buffer, SCREENWIDTH, SCREENHEIGHT, + w_cache_lump_name(("PLAYPAL"), PU_CACHE)); +} + +void v_draw_mouse_speed_box(int speed) +{ + int bgcolor; + int bordercolor; + int black; + + /* If the mouse is turned off, don't draw the box at all. */ + + if (!usemouse) + { + return; + } + + /* Get palette indices for colors for widget. These depend on the + * palette of the game being played. + */ + + bgcolor = i_get_palette_index(0x77, 0x77, 0x77); + bordercolor = i_get_palette_index(0x55, 0x55, 0x55); + black = i_get_palette_index(0x00, 0x00, 0x00); + + /* Calculate box position */ + + v_draw_filled_box(MOUSE_SPEED_BOX_X, MOUSE_SPEED_BOX_Y, + MOUSE_SPEED_BOX_WIDTH, MOUSE_SPEED_BOX_HEIGHT, bgcolor); + v_draw_box(MOUSE_SPEED_BOX_X, MOUSE_SPEED_BOX_Y, MOUSE_SPEED_BOX_WIDTH, + MOUSE_SPEED_BOX_HEIGHT, bordercolor); + v_draw_horiz_line(MOUSE_SPEED_BOX_X + 1, MOUSE_SPEED_BOX_Y + 4, + MOUSE_SPEED_BOX_WIDTH - 2, black); + + /* If acceleration is used, draw a box that helps to calibrate the + * threshold point. + */ + + if (mouse_threshold > 0 && fabs(mouse_acceleration - 1) > 0.01) + { + draw_accelerating_box(speed); + } + else + { + draw_non_accelerating_box(speed); + } +} diff --git a/games/NXDoom/src/v_video.h b/games/NXDoom/src/v_video.h new file mode 100644 index 00000000000..07ac56730d4 --- /dev/null +++ b/games/NXDoom/src/v_video.h @@ -0,0 +1,181 @@ +/**************************************************************************** + * apps/games/NXDoom/src/v_video.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/* Needed because we are referring to patches. */ + +#include "v_patch.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CENTERY (SCREENHEIGHT / 2) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int dirtybox[4]; + +extern byte *tinttable; + +/* haleyjd 08/28/10: implemented for Strife support + * haleyjd 08/28/10: Patch clipping callback, implemented to support Choco + * Strife. + */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef boolean (*vpatchclipfunc_t)(patch_t *, int, int); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void v_set_patch_clip_callback(vpatchclipfunc_t func); + +/**************************************************************************** + * Name: v_init + * + * Description: + * Allocates buffer screens, call before r_init. + * + ****************************************************************************/ + +void v_init(void); + +/**************************************************************************** + * Name: v_copy_rect + * + * Description: + * Draw a block from the specified source screen to the screen. + * + ****************************************************************************/ + +void v_copy_rect(int srcx, int srcy, pixel_t *source, int width, int height, + int destx, int desty); + +void v_draw_patch(int x, int y, patch_t *patch); +void v_draw_patch_flipped(int x, int y, patch_t *patch); +void v_draw_tl_patch(int x, int y, patch_t *patch); +void v_draw_alt_tl_patch(int x, int y, patch_t *patch); +void v_draw_shadowed_patch(int x, int y, patch_t *patch); +void v_draw_xla_patch(int x, int y, patch_t *patch); /* villsa [STRIFE] */ +void v_draw_patch_direct(int x, int y, patch_t *patch); + +/**************************************************************************** + * Name: v_draw_block + * + * Description: + * Draw a linear block of pixels into the view buffer. + * + ****************************************************************************/ + +void v_draw_block(int x, int y, int width, int height, pixel_t *src); + +void v_mark_rect(int x, int y, int width, int height); + +void v_draw_filled_box(int x, int y, int w, int h, int c); +void v_draw_horiz_line(int x, int y, int w, int c); +void v_draw_vert_line(int x, int y, int h, int c); +void v_draw_box(int x, int y, int w, int h, int c); + +/**************************************************************************** + * Name: v_draw_raw_screen + * + * Description: + * Draw a raw screen lump + * + ****************************************************************************/ + +void v_draw_raw_screen(pixel_t *raw); + +/**************************************************************************** + * Name: v_use_buffer + * + * Description: + * Temporarily switch to using a different buffer to draw graphics, etc. + * + ****************************************************************************/ + +void v_use_buffer(pixel_t *buffer); + +/**************************************************************************** + * Name: v_restore_buffer + * + * Description: + * Return to using the normal screen buffer to draw graphics. + * + ****************************************************************************/ + +void v_restore_buffer(void); + +/**************************************************************************** + * Name: v_screenshot + * + * Description: + * Save a screenshot of the current screen to a file, named in the format + * described in the string passed to the function, eg. "DOOM%02i.pcx" + * + ****************************************************************************/ + +void v_screenshot(const char *format); + +/**************************************************************************** + * Name: v_load_tint_table + * + * Description: + * Load the lookup table for translucency calculations from the TINTTAB + * lump. + * + ****************************************************************************/ + +void v_load_tint_table(void); + +/**************************************************************************** + * Name: v_load_xla_table + * + * Description: + * villsa [STRIFE] + * Load the lookup table for translucency calculations from the XLATAB lump. + * + ****************************************************************************/ + +void v_load_xla_table(void); + +void v_draw_mouse_speed_box(int speed); + +#endif diff --git a/games/NXDoom/src/w_checksum.c b/games/NXDoom/src/w_checksum.c new file mode 100644 index 00000000000..28dbf8d1676 --- /dev/null +++ b/games/NXDoom/src/w_checksum.c @@ -0,0 +1,113 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_checksum.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Generate a checksum of the WAD directory. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "i_system.h" +#include "m_misc.h" +#include "sha1.h" +#include "w_checksum.h" +#include "w_wad.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static wad_file_t **g_open_wadfiles = NULL; +static int g_num_open_wadfiles = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int get_file_number(wad_file_t *handle) +{ + int i; + int result; + + for (i = 0; i < g_num_open_wadfiles; ++i) + { + if (g_open_wadfiles[i] == handle) + { + return i; + } + } + + /* Not found in list. This is a new file we haven't seen yet. + * Allocate another slot for this file. + */ + + g_open_wadfiles = i_realloc(g_open_wadfiles, + sizeof(wad_file_t *) * (g_num_open_wadfiles + 1)); + g_open_wadfiles[g_num_open_wadfiles] = handle; + + result = g_num_open_wadfiles; + ++g_num_open_wadfiles; + + return result; +} + +static void checksum_add_lump(SHA1_CTX *sha1_context, lumpinfo_t *lump) +{ + char buf[9]; + int fileno; + + fileno = get_file_number(lump->wad_file); + + m_str_copy(buf, lump->name, sizeof(buf)); + sha1_updatestring(sha1_context, buf); + sha1_updateint32(sha1_context, fileno); + sha1_updateint32(sha1_context, lump->position); + sha1_updateint32(sha1_context, lump->size); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void w_checksum(sha1_digest_t digest) +{ + SHA1_CTX sha1_context; + unsigned int i; + + sha1init(&sha1_context); + + g_num_open_wadfiles = 0; + + /* Go through each entry in the WAD directory, adding information + * about each entry to the SHA1 hash. + */ + + for (i = 0; i < numlumps; ++i) + { + checksum_add_lump(&sha1_context, lumpinfo[i]); + } + + sha1final(digest, &sha1_context); +} diff --git a/games/NXDoom/src/w_checksum.h b/games/NXDoom/src/w_checksum.h new file mode 100644 index 00000000000..316239b706c --- /dev/null +++ b/games/NXDoom/src/w_checksum.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_checksum.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Generate a checksum of the WAD directory. + * + ****************************************************************************/ + +#ifndef W_CHECKSUM_H +#define W_CHECKSUM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +extern void w_checksum(sha1_digest_t digest); + +#endif /* W_CHECKSUM_H */ diff --git a/games/NXDoom/src/w_file.c b/games/NXDoom/src/w_file.c new file mode 100644 index 00000000000..38b4d9574be --- /dev/null +++ b/games/NXDoom/src/w_file.c @@ -0,0 +1,92 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_file.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * WAD I/O functions. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "config.h" + +#include "doomtype.h" +#include "m_argv.h" + +#include "w_file.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static wad_file_class_t *wad_file_classes[] = +{ + &stdc_wad_file, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +wad_file_t *w_open_file(const char *path) +{ + wad_file_t *result; + int i; + + /* @category obscure + * + * Use the OS's virtual memory subsystem to map WAD files + * directly into memory. + */ + + if (!m_check_parm("-mmap")) + { + return stdc_wad_file.open_file(path); + } + + /* Try all classes in order until we find one that works */ + + result = NULL; + + for (i = 0; i < arrlen(wad_file_classes); ++i) + { + result = wad_file_classes[i]->open_file(path); + + if (result != NULL) + { + break; + } + } + + return result; +} + +void w_close_file(wad_file_t *wad) +{ + wad->file_class->close_file(wad); +} + +size_t w_read(wad_file_t *wad, unsigned int offset, void *buffer, + size_t buffer_len) +{ + return wad->file_class->read(wad, offset, buffer, buffer_len); +} diff --git a/games/NXDoom/src/w_file.h b/games/NXDoom/src/w_file.h new file mode 100644 index 00000000000..32ac342226b --- /dev/null +++ b/games/NXDoom/src/w_file.h @@ -0,0 +1,111 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_file.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * WAD I/O functions. + * + ****************************************************************************/ + +#ifndef __W_FILE__ +#define __W_FILE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct _wad_file_s wad_file_t; + +typedef struct +{ + /* Open a file for reading. */ + + wad_file_t *(*open_file)(const char *path); + + /* Close the specified file. */ + + void (*close_file)(wad_file_t *file); + + /* Read data from the specified position in the file into the provided + * buffer. Returns the number of bytes read. + */ + + size_t (*read)(wad_file_t *file, unsigned int offset, void *buffer, + size_t buffer_len); +} wad_file_class_t; + +struct _wad_file_s +{ + /* Class of this file. */ + + wad_file_class_t *file_class; + + /* If this is NULL, the file cannot be mapped into memory. If this is + * non-NULL, it is a pointer to the mapped file. + */ + + byte *mapped; + + /* Length of the file, in bytes. */ + + unsigned int length; + + /* File's location on disk. */ + + const char *path; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern wad_file_class_t stdc_wad_file; + +#ifdef HAVE_MMAP +extern wad_file_class_t posix_wad_file; +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Open the specified file. Returns a pointer to a new wad_file_t + * handle for the WAD file, or NULL if it could not be opened. + */ + +wad_file_t *w_open_file(const char *path); + +/* Close the specified WAD file. */ + +void w_close_file(wad_file_t *wad); + +/* Read data from the specified file into the provided buffer. The + * data is read from the specified offset from the start of the file. + * Returns the number of bytes read. + */ + +size_t w_read(wad_file_t *wad, unsigned int offset, void *buffer, + size_t buffer_len); + +#endif /* __W_FILE__ */ diff --git a/games/NXDoom/src/w_file_stdc.c b/games/NXDoom/src/w_file_stdc.c new file mode 100644 index 00000000000..361bf206095 --- /dev/null +++ b/games/NXDoom/src/w_file_stdc.c @@ -0,0 +1,123 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_file_stdc.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * WAD I/O functions. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "m_misc.h" +#include "w_file.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + wad_file_t wad; + FILE *fstream; +} stdc_wad_file_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static wad_file_t *w_stdc_openfile(const char *path); +static void w_stdc_closefile(wad_file_t *wad); +static size_t w_stdc_read(wad_file_t *wad, unsigned int offset, void *buffer, + size_t buffer_len); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +wad_file_class_t stdc_wad_file = +{ + w_stdc_openfile, + w_stdc_closefile, + w_stdc_read, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static wad_file_t *w_stdc_openfile(const char *path) +{ + stdc_wad_file_t *result; + FILE *fstream; + + fstream = fopen(path, "rb"); + + if (fstream == NULL) + { + return NULL; + } + + /* Create a new stdc_wad_file_t to hold the file handle. */ + + result = z_malloc(sizeof(stdc_wad_file_t), PU_STATIC, 0); + result->wad.file_class = &stdc_wad_file; + result->wad.mapped = NULL; + result->wad.length = m_file_length(fstream); + result->wad.path = m_string_duplicate(path); + result->fstream = fstream; + + return &result->wad; +} + +static void w_stdc_closefile(wad_file_t *wad) +{ + stdc_wad_file_t *stdc_wad; + + stdc_wad = (stdc_wad_file_t *)wad; + + fclose(stdc_wad->fstream); + z_free(stdc_wad); +} + +/* Read data from the specified position in the file into the + * provided buffer. Returns the number of bytes read. + */ + +static size_t w_stdc_read(wad_file_t *wad, unsigned int offset, void *buffer, + size_t buffer_len) +{ + stdc_wad_file_t *stdc_wad; + size_t result; + + stdc_wad = (stdc_wad_file_t *)wad; + + /* Jump to the specified position in the file. */ + + fseek(stdc_wad->fstream, offset, SEEK_SET); + + /* Read into the buffer. */ + + result = fread(buffer, 1, buffer_len, stdc_wad->fstream); + + return result; +} diff --git a/games/NXDoom/src/w_main.c b/games/NXDoom/src/w_main.c new file mode 100644 index 00000000000..ce11765471a --- /dev/null +++ b/games/NXDoom/src/w_main.c @@ -0,0 +1,294 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_main.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Common code to parse command line, identifying WAD files to load. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "config.h" +#include "d_iwad.h" +#include "i_glob.h" +#include "i_system.h" +#include "m_argv.h" +#include "w_main.h" +#include "w_merge.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct uniquelump +{ + gamemission_t mission; + const char *lumpname; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Lump names that are unique to particular game types. This lets us check + * the user is not trying to play with the wrong executable, eg. + * chocolate-doom -iwad hexen.wad. + */ + +static const struct uniquelump g_unique_lumps[] = +{ + {doom, "POSSA1"}, + {heretic, "IMPXA1"}, + {hexen, "ETTNA1"}, + {strife, "AGRDA1"}, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Parse the command line, merging WAD files that are sppecified. + * Returns true if at least one file was added. + */ + +boolean w_parse_command_line(void) +{ + boolean modifiedgame = false; + int p; + + /* Merged PWADs are loaded first, because they are supposed to be + * modified IWADs. + */ + + /* @arg + * @category mod + * + * Simulates the behavior of deutex's -merge option, merging a PWAD + * into the main IWAD. Multiple files may be specified. + */ + + p = m_check_parm_with_args("-merge", 1); + + if (p > 0) + { + for (p = p + 1; p < myargc && myargv[p][0] != '-'; ++p) + { + char *filename; + + modifiedgame = true; + + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" merging %s\n", filename); + w_merge_file(filename); + free(filename); + } + } + + /* NWT-style merging: */ + + /* NWT's -merge option: */ + + /* @arg + * @category mod + * + * Simulates the behavior of NWT's -merge option. Multiple files + * may be specified. + */ + + p = m_check_parm_with_args("-nwtmerge", 1); + + if (p > 0) + { + for (p = p + 1; p < myargc && myargv[p][0] != '-'; ++p) + { + char *filename; + + modifiedgame = true; + + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" performing NWT-style merge of %s\n", filename); + w_nwt_dash_merge(filename); + free(filename); + } + } + + /* Add flats */ + + /* @arg + * @category mod + * + * Simulates the behavior of NWT's -af option, merging flats into + * the main IWAD directory. Multiple files may be specified. + */ + + p = m_check_parm_with_args("-af", 1); + + if (p > 0) + { + for (p = p + 1; p < myargc && myargv[p][0] != '-'; ++p) + { + char *filename; + + modifiedgame = true; + + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" merging flats from %s\n", filename); + w_nwt_merge_file(filename, W_NWT_MERGE_FLATS); + free(filename); + } + } + + /* @arg + * @category mod + * + * Simulates the behavior of NWT's -as option, merging sprites + * into the main IWAD directory. Multiple files may be specified. + */ + + p = m_check_parm_with_args("-as", 1); + + if (p > 0) + { + for (p = p + 1; p < myargc && myargv[p][0] != '-'; ++p) + { + char *filename; + + modifiedgame = true; + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" merging sprites from %s\n", filename); + w_nwt_merge_file(filename, W_NWT_MERGE_SPRITES); + free(filename); + } + } + + /* @arg + * @category mod + * + * Equivalent to "-af -as ". + */ + + p = m_check_parm_with_args("-aa", 1); + + if (p > 0) + { + for (p = p + 1; p < myargc && myargv[p][0] != '-'; ++p) + { + char *filename; + + modifiedgame = true; + + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" merging sprites and flats from %s\n", filename); + w_nwt_merge_file(filename, + W_NWT_MERGE_SPRITES | W_NWT_MERGE_FLATS); + free(filename); + } + } + + /* @arg + * @vanilla + * + * Load the specified PWAD files. Each succeeding argument is + * treated as a PWAD file name until one starts with a dash or the + * argument list is exhausted. + */ + + p = m_check_parm_with_args("-file", 1); + if (p) + { + /* the params after p are wadfile/lump names, + * until end of params or another - preceded param + */ + + modifiedgame = true; /* homebrew levels */ + while (++p != myargc && myargv[p][0] != '-') + { + char *filename; + + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" adding %s\n", filename); + w_add_file(filename); + free(filename); + } + } + + return modifiedgame; +} + +/* Load all WAD files from the given directory. */ + +void w_auto_load_wads(const char *path) +{ + glob_t *glob; + const char *filename; + + glob = i_start_multi_glob(path, GLOB_FLAG_NOCASE | GLOB_FLAG_SORTED, + "*.wad", "*.lmp", NULL); + for (; ; ) + { + filename = i_next_glob(glob); + if (filename == NULL) + { + break; + } + + printf(" [autoload] merging %s\n", filename); + w_merge_file(filename); + } + + i_end_glob(glob); +} + +void w_check_correct_iwad(gamemission_t mission) +{ + int i; + lumpindex_t lumpnum; + + for (i = 0; i < arrlen(g_unique_lumps); ++i) + { + if (mission != g_unique_lumps[i].mission) + { + lumpnum = w_check_num_for_name(g_unique_lumps[i].lumpname); + + if (lumpnum >= 0) + { + i_error( + "\nYou are trying to use a %s IWAD file with " + "the %s%s binary.\nThis isn't going to work.\n" + "You probably want to use the %s%s binary.", + d_suggest_game_name( + g_unique_lumps[i].mission, indetermined), + PROGRAM_PREFIX, d_game_mission_string(mission), + PROGRAM_PREFIX, + d_game_mission_string(g_unique_lumps[i].mission)); + } + } + } +} diff --git a/games/NXDoom/src/w_main.h b/games/NXDoom/src/w_main.h new file mode 100644 index 00000000000..beab3f29bb2 --- /dev/null +++ b/games/NXDoom/src/w_main.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_main.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Common code to parse command line, identifying WAD files to load. + * + ****************************************************************************/ + +#ifndef W_MAIN_H +#define W_MAIN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_mode.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean w_parse_command_line(void); +void w_check_correct_iwad(gamemission_t mission); + +/* Autoload all .wad files from the given directory: */ + +void w_auto_load_wads(const char *path); + +#endif /* W_MAIN_H */ diff --git a/games/NXDoom/src/w_merge.c b/games/NXDoom/src/w_merge.c new file mode 100644 index 00000000000..88a05b4bdde --- /dev/null +++ b/games/NXDoom/src/w_merge.c @@ -0,0 +1,764 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_merge.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Handles merging of PWADs, similar to deutex's -merge option + * + * Ideally this should work exactly the same as in deutex, but trying to + * read the deutex source code made my brain hurt. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_misc.h" +#include "w_merge.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + SECTION_NORMAL, + SECTION_FLATS, + SECTION_SPRITES, +} section_t; + +typedef struct +{ + lumpinfo_t **lumps; + int numlumps; +} searchlist_t; + +typedef struct +{ + char sprname[4]; + char frame; + lumpinfo_t *angle_lumps[8]; +} sprite_frame_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static searchlist_t iwad; +static searchlist_t iwad_sprites; +static searchlist_t pwad; + +static searchlist_t iwad_flats; +static searchlist_t pwad_sprites; +static searchlist_t pwad_flats; + +/* lumps with these sprites must be replaced in the IWAD */ + +static sprite_frame_t *sprite_frames; +static int num_sprite_frames; +static int sprite_frames_alloced; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Search in a list to find a lump with a particular name + * Linear search (slow!) + * + * Returns -1 if not found + */ + +static int find_in_list(searchlist_t *list, const char *name) +{ + int i; + + for (i = 0; i < list->numlumps; ++i) + { + if (!strncasecmp(list->lumps[i]->name, name, 8)) return i; + } + + return -1; +} + +static boolean setup_list(searchlist_t *list, searchlist_t *src_list, + const char *startname, const char *endname, + const char *startname2, const char *endname2) +{ + int startlump; + int endlump; + + list->numlumps = 0; + startlump = find_in_list(src_list, startname); + + if (startname2 != NULL && startlump < 0) + { + startlump = find_in_list(src_list, startname2); + } + + if (startlump >= 0) + { + endlump = find_in_list(src_list, endname); + + if (endname2 != NULL && endlump < 0) + { + endlump = find_in_list(src_list, endname2); + } + + if (endlump > startlump) + { + list->lumps = src_list->lumps + startlump + 1; + list->numlumps = endlump - startlump - 1; + return true; + } + } + + return false; +} + +/* Sets up the sprite/flat search lists */ + +static void setup_lists(void) +{ + /* IWAD */ + + if (!setup_list(&iwad_flats, &iwad, "F_START", "F_END", NULL, NULL)) + { + i_error("Flats section not found in IWAD"); + } + + if (!setup_list(&iwad_sprites, &iwad, "S_START", "S_END", NULL, NULL)) + + { + i_error("Sprites section not found in IWAD"); + } + + /* PWAD */ + + setup_list(&pwad_flats, &pwad, "F_START", "F_END", "FF_START", "FF_END"); + setup_list(&pwad_sprites, &pwad, "S_START", "S_END", "SS_START", "SS_END"); +} + +/* Initialize the replace list */ + +static void init_sprite_list(void) +{ + if (sprite_frames == NULL) + { + sprite_frames_alloced = 128; + sprite_frames = z_malloc( + sizeof(*sprite_frames) * sprite_frames_alloced, + PU_STATIC, NULL + ); + } + + num_sprite_frames = 0; +} + +static boolean valid_sprite_lump_name(char *name) +{ + if (name[0] == '\0' || name[1] == '\0' || name[2] == '\0' || + name[3] == '\0') + { + return false; + } + + /* First frame: */ + + if (name[4] == '\0' || !isdigit(name[5])) + { + return false; + } + + /* Second frame (optional): */ + + if (name[6] != '\0' && !isdigit(name[7])) + { + return false; + } + + return true; +} + +static sprite_frame_t *find_sprite_frame(char *name, int frame) +{ + sprite_frame_t *result; + int i; + + /* Search the list and try to find the frame */ + + for (i = 0; i < num_sprite_frames; ++i) + { + sprite_frame_t *cur = &sprite_frames[i]; + + if (!strncasecmp(cur->sprname, name, 4) && cur->frame == frame) + { + return cur; + } + } + + /* Not found in list; Need to add to the list */ + + /* Grow list? */ + + if (num_sprite_frames >= sprite_frames_alloced) + { + sprite_frame_t *newframes; + + newframes = z_malloc( + sprite_frames_alloced * 2 * sizeof(*sprite_frames), + PU_STATIC, NULL + ); + memcpy(newframes, sprite_frames, + sprite_frames_alloced * sizeof(*sprite_frames)); + z_free(sprite_frames); + sprite_frames_alloced *= 2; + sprite_frames = newframes; + } + + /* Add to end of list */ + + result = &sprite_frames[num_sprite_frames]; + memcpy(result->sprname, name, 4); + result->frame = frame; + + for (i = 0; i < 8; ++i) + result->angle_lumps[i] = NULL; + + ++num_sprite_frames; + + return result; +} + +/* Check if sprite lump is needed in the new wad */ + +static boolean sprite_lump_needed(lumpinfo_t *lump) +{ + sprite_frame_t *sprite; + int angle_num; + int i; + + if (!valid_sprite_lump_name(lump->name)) + { + return true; + } + + /* check the first frame */ + + sprite = find_sprite_frame(lump->name, lump->name[4]); + angle_num = lump->name[5] - '0'; + + if (angle_num == 0) + { + /* must check all frames */ + + for (i = 0; i < 8; ++i) + { + if (sprite->angle_lumps[i] == lump) return true; + } + } + else + { + /* check if this lump is being used for this frame */ + + if (sprite->angle_lumps[angle_num - 1] == lump) return true; + } + + /* second frame if any */ + + /* no second frame? */ + + if (lump->name[6] == '\0') return false; + + sprite = find_sprite_frame(lump->name, lump->name[6]); + angle_num = lump->name[7] - '0'; + + if (angle_num == 0) + { + /* must check all frames */ + + for (i = 0; i < 8; ++i) + { + if (sprite->angle_lumps[i] == lump) return true; + } + } + else + { + /* check if this lump is being used for this frame */ + + if (sprite->angle_lumps[angle_num - 1] == lump) return true; + } + + return false; +} + +static void add_sprite_lump(lumpinfo_t *lump) +{ + sprite_frame_t *sprite; + int angle_num; + int i; + + if (!valid_sprite_lump_name(lump->name)) + { + return; + } + + /* first angle */ + + sprite = find_sprite_frame(lump->name, lump->name[4]); + angle_num = lump->name[5] - '0'; + + if (angle_num == 0) + { + for (i = 0; i < 8; ++i) + sprite->angle_lumps[i] = lump; + } + else + { + sprite->angle_lumps[angle_num - 1] = lump; + } + + /* second angle */ + + /* no second angle? */ + + if (lump->name[6] == '\0') return; + + sprite = find_sprite_frame(lump->name, lump->name[6]); + angle_num = lump->name[7] - '0'; + + if (angle_num == 0) + { + for (i = 0; i < 8; ++i) + sprite->angle_lumps[i] = lump; + } + else + { + sprite->angle_lumps[angle_num - 1] = lump; + } +} + +/* Generate the list. Run at the start, before merging */ + +static void generate_sprite_list(void) +{ + int i; + + init_sprite_list(); + + /* Add all sprites from the IWAD */ + + for (i = 0; i < iwad_sprites.numlumps; ++i) + { + add_sprite_lump(iwad_sprites.lumps[i]); + } + + /* Add all sprites from the PWAD + * (replaces IWAD sprites) + */ + + for (i = 0; i < pwad_sprites.numlumps; ++i) + { + add_sprite_lump(pwad_sprites.lumps[i]); + } +} + +/* Perform the merge. + * + * The merge code creates a new lumpinfo list, adding entries from the + * IWAD first followed by the PWAD. + * + * For the IWAD: + * * Flats are added. If a flat with the same name is in the PWAD, + * it is ignored(deleted). At the end of the section, all flats in the + * PWAD are inserted. This is consistent with the behavior of + * deutex/deusf. + * * Sprites are added. The "replace list" is generated before the merge + * from the list of sprites in the PWAD. Any sprites in the IWAD found + * to match the replace list are removed. At the end of the section, + * the sprites from the PWAD are inserted. + * + * For the PWAD: + * * All Sprites and Flats are ignored, with the assumption they have + * already been merged into the IWAD's sections. + */ + +static void do_merge(void) +{ + section_t current_section; + lumpinfo_t **newlumps; + int num_newlumps; + int lumpindex; + int i; + int n; + + /* Can't ever have more lumps than we already have */ + + newlumps = calloc(numlumps, sizeof(lumpinfo_t *)); + num_newlumps = 0; + + /* Add IWAD lumps */ + + current_section = SECTION_NORMAL; + + for (i = 0; i < iwad.numlumps; ++i) + { + lumpinfo_t *lump = iwad.lumps[i]; + + switch (current_section) + { + case SECTION_NORMAL: + if (!strncasecmp(lump->name, "F_START", 8)) + { + current_section = SECTION_FLATS; + } + else if (!strncasecmp(lump->name, "S_START", 8)) + { + current_section = SECTION_SPRITES; + } + + newlumps[num_newlumps++] = lump; + + break; + + case SECTION_FLATS: + + /* Have we reached the end of the section? */ + + if (!strncasecmp(lump->name, "F_END", 8)) + { + /* Add all new flats from the PWAD to the end + * of the section + */ + + for (n = 0; n < pwad_flats.numlumps; ++n) + { + newlumps[num_newlumps++] = pwad_flats.lumps[n]; + } + + newlumps[num_newlumps++] = lump; + + /* back to normal reading */ + + current_section = SECTION_NORMAL; + } + else + { + /* If there is a flat in the PWAD with the same name, + * do not add it now. All PWAD flats are added to the + * end of the section. Otherwise, if it is only in the + * IWAD, add it now + */ + + lumpindex = find_in_list(&pwad_flats, lump->name); + + if (lumpindex < 0) + { + newlumps[num_newlumps++] = lump; + } + } + + break; + + case SECTION_SPRITES: + + /* Have we reached the end of the section? */ + + if (!strncasecmp(lump->name, "S_END", 8)) + { + /* add all the PWAD sprites */ + + for (n = 0; n < pwad_sprites.numlumps; ++n) + { + if (sprite_lump_needed(pwad_sprites.lumps[n])) + { + newlumps[num_newlumps++] = pwad_sprites.lumps[n]; + } + } + + /* copy the ending */ + + newlumps[num_newlumps++] = lump; + + /* back to normal reading */ + + current_section = SECTION_NORMAL; + } + else + { + /* Is this lump holding a sprite to be replaced in the + * PWAD? If so, wait until the end to add it. + */ + + if (sprite_lump_needed(lump)) + { + newlumps[num_newlumps++] = lump; + } + } + + break; + } + } + + /* Add PWAD lumps */ + + current_section = SECTION_NORMAL; + + for (i = 0; i < pwad.numlumps; ++i) + { + lumpinfo_t *lump = pwad.lumps[i]; + + switch (current_section) + { + case SECTION_NORMAL: + if (!strncasecmp(lump->name, "F_START", 8) || + !strncasecmp(lump->name, "FF_START", 8)) + { + current_section = SECTION_FLATS; + } + else if (!strncasecmp(lump->name, "S_START", 8) || + !strncasecmp(lump->name, "SS_START", 8)) + { + current_section = SECTION_SPRITES; + } + else + { + /* Don't include the headers of sections */ + + newlumps[num_newlumps++] = lump; + } + break; + + case SECTION_FLATS: + + /* PWAD flats are ignored (already merged) */ + + if (!strncasecmp(lump->name, "FF_END", 8) || + !strncasecmp(lump->name, "F_END", 8)) + { + /* end of section */ + + current_section = SECTION_NORMAL; + } + break; + + case SECTION_SPRITES: + + /* PWAD sprites are ignored (already merged) */ + + if (!strncasecmp(lump->name, "SS_END", 8) || + !strncasecmp(lump->name, "S_END", 8)) + { + /* end of section */ + + current_section = SECTION_NORMAL; + } + break; + } + } + + /* Switch to the new lumpinfo, and free the old one */ + + free(lumpinfo); + lumpinfo = newlumps; + numlumps = num_newlumps; +} + +/* Replace lumps in the given list with lumps from the PWAD */ + +static void w_nwt_add_lumps(searchlist_t *list) +{ + int i; + + /* Go through the IWAD list given, replacing lumps with lumps of + * the same name from the PWAD + */ + + for (i = 0; i < list->numlumps; ++i) + { + int index; + + index = find_in_list(&pwad, list->lumps[i]->name); + + if (index > 0) + { + memcpy(list->lumps[i], pwad.lumps[index], sizeof(lumpinfo_t)); + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void w_print_directory(void) +{ + unsigned int i; + unsigned int n; + + /* debug */ + + for (i = 0; i < numlumps; ++i) + { + for (n = 0; n < 8 && lumpinfo[i]->name[n] != '\0'; ++n) + putchar(lumpinfo[i]->name[n]); + putchar('\n'); + } +} + +/* Merge in a file by name */ + +void w_merge_file(const char *filename) +{ + int old_numlumps; + + old_numlumps = numlumps; + + /* Load PWAD */ + + if (w_add_file(filename) == NULL) return; + + /* IWAD is at the start, PWAD was appended to the end */ + + iwad.lumps = lumpinfo; + iwad.numlumps = old_numlumps; + + pwad.lumps = lumpinfo + old_numlumps; + pwad.numlumps = numlumps - old_numlumps; + + /* Setup sprite/flat lists */ + + setup_lists(); + + /* Generate list of sprites to be replaced by the PWAD */ + + generate_sprite_list(); + + /* Perform the merge */ + + do_merge(); +} + +/* Merge sprites and flats in the way NWT does with its -af and -as + * command-line options. + */ + +void w_nwt_merge_file(const char *filename, int flags) +{ + int old_numlumps; + + old_numlumps = numlumps; + + /* Load PWAD */ + + if (w_add_file(filename) == NULL) return; + + /* IWAD is at the start, PWAD was appended to the end */ + + iwad.lumps = lumpinfo; + iwad.numlumps = old_numlumps; + + pwad.lumps = lumpinfo + old_numlumps; + pwad.numlumps = numlumps - old_numlumps; + + /* Setup sprite/flat lists */ + + setup_lists(); + + /* Merge in flats? */ + + if (flags & W_NWT_MERGE_FLATS) + { + w_nwt_add_lumps(&iwad_flats); + } + + /* Sprites? */ + + if (flags & W_NWT_MERGE_SPRITES) + { + w_nwt_add_lumps(&iwad_sprites); + } + + /* Discard the PWAD */ + + numlumps = old_numlumps; +} + +/* Simulates the NWT -merge command line parameter. What this does is load + * a PWAD, then search the IWAD sprites, removing any sprite lumps that also + * exist in the PWAD. + */ + +void w_nwt_dash_merge(const char *filename) +{ + wad_file_t *wad_file; + int old_numlumps; + int i; + + old_numlumps = numlumps; + + /* Load PWAD */ + + wad_file = w_add_file(filename); + + if (wad_file == NULL) + { + return; + } + + /* IWAD is at the start, PWAD was appended to the end */ + + iwad.lumps = lumpinfo; + iwad.numlumps = old_numlumps; + + pwad.lumps = lumpinfo + old_numlumps; + pwad.numlumps = numlumps - old_numlumps; + + /* Setup sprite/flat lists */ + + setup_lists(); + + /* Search through the IWAD sprites list. */ + + for (i = 0; i < iwad_sprites.numlumps; ++i) + { + if (find_in_list(&pwad, iwad_sprites.lumps[i]->name) >= 0) + { + /* Replace this entry with an empty string. This is what + * nwt -merge does. + */ + + m_str_copy(iwad_sprites.lumps[i]->name, "", 8); + } + } + + /* Discard PWAD + * The PWAD must now be added in again with -file. + */ + + numlumps = old_numlumps; + + w_close_file(wad_file); +} diff --git a/games/NXDoom/src/w_merge.h b/games/NXDoom/src/w_merge.h new file mode 100644 index 00000000000..ce580afc51b --- /dev/null +++ b/games/NXDoom/src/w_merge.h @@ -0,0 +1,56 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_merge.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Handles merging of PWADs, similar to deutex's -merge option + * + * Ideally this should work exactly the same as in deutex, but trying to + * read the deutex source code made my brain hurt. + * + ****************************************************************************/ + +#ifndef W_MERGE_H +#define W_MERGE_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define W_NWT_MERGE_SPRITES 0x1 +#define W_NWT_MERGE_FLATS 0x2 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Add a new WAD and merge it into the main directory */ + +void w_merge_file(const char *filename); + +/* NWT-style merging */ + +void w_nwt_merge_file(const char *filename, int flags); + +/* Acts the same as NWT's "-merge" option. */ + +void w_nwt_dash_merge(const char *filename); + +/* Debug function that prints the WAD directory. */ + +void w_print_directory(void); + +#endif /* W_MERGE_H */ diff --git a/games/NXDoom/src/w_wad.c b/games/NXDoom/src/w_wad.c new file mode 100644 index 00000000000..88c75ada501 --- /dev/null +++ b/games/NXDoom/src/w_wad.c @@ -0,0 +1,663 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_wad.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Handles WAD file header, directory, lump I/O. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomtype.h" + +#include "i_swap.h" +#include "i_system.h" +#include "i_video.h" +#include "m_misc.h" +#include "v_diskicon.h" +#include "z_zone.h" + +#include "w_wad.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +begin_packed_struct struct wadinfo_t +{ + /* Should be "IWAD" or "PWAD". */ + + char identification[4]; + int numlumps; + int infotableofs; +} end_packed_struct; + +typedef struct wadinfo_t wadinfo_t; + +begin_packed_struct struct filelump_t +{ + int filepos; + int size; + char name[8]; +} end_packed_struct; + +typedef struct filelump_t filelump_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Hash table for fast lookups */ + +static lumpindex_t *lumphash; + +/* Variables for the reload hack: filename of the PWAD to reload, and the + * lumps from WADs before the reload file, so we can resent numlumps and + * load the file again. + */ + +static wad_file_t *reloadhandle = NULL; +static lumpinfo_t *reloadlumps = NULL; +static char *reloadname = NULL; +static int reloadlump = -1; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Location of each lump on disk. */ + +lumpinfo_t **lumpinfo; +unsigned int numlumps = 0; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Hash function used for lump names. */ + +unsigned int w_lump_name_hash(const char *s) +{ + /* This is the djb2 string hash function, modded to work on strings + * that have a maximum length of 8. + */ + + unsigned int result = 5381; + unsigned int i; + + for (i = 0; i < 8 && s[i] != '\0'; ++i) + { + result = ((result << 5) ^ result) ^ toupper(s[i]); + } + + return result; +} + +/* LUMP BASED ROUTINES. */ + +/* w_add_file + * + * 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. + */ + +wad_file_t *w_add_file(const char *filename) +{ + wadinfo_t header; + lumpindex_t i; + wad_file_t *wad_file; + int length; + int startlump; + filelump_t *fileinfo; + filelump_t *filerover; + lumpinfo_t *filelumps; + int numfilelumps; + + /* If the filename begins with a ~, it indicates that we should use the + * reload hack. + */ + + if (filename[0] == '~') + { + if (reloadname != NULL) + { + i_error("Prefixing a WAD filename with '~' indicates that the " + "WAD should be reloaded\n" + "on each level restart, for use by level authors for " + "rapid development. You\n" + "can only reload one WAD file, and it must be the last " + "file in the -file list."); + } + + reloadname = strdup(filename); + reloadlump = numlumps; + ++filename; + } + + /* Open the file and add to directory */ + + wad_file = w_open_file(filename); + + if (wad_file == NULL) + { + printf(" couldn't open %s\n", filename); + return NULL; + } + + if (strcasecmp(filename + strlen(filename) - 3, "wad")) + { + /* single lump file */ + + /* fraggle: Swap the filepos and size here. The WAD directory + * parsing code expects a little-endian directory, so will swap + * them back. Effectively we're constructing a "fake WAD directory" + * here, as it would appear on disk. + */ + + fileinfo = z_malloc(sizeof(filelump_t), PU_STATIC, 0); + fileinfo->filepos = LONG(0); + fileinfo->size = LONG(wad_file->length); + + /* Name the lump after the base of the filename (without the + * extension). + */ + + m_extract_file_base(filename, fileinfo->name); + numfilelumps = 1; + } + else + { + /* WAD file */ + + w_read(wad_file, 0, &header, sizeof(header)); + + if (strncmp(header.identification, "IWAD", 4)) + { + /* Homebrew levels? */ + + if (strncmp(header.identification, "PWAD", 4)) + { + w_close_file(wad_file); + i_error("Wad file %s doesn't have IWAD " + "or PWAD id\n", + filename); + } + + /* ???modifiedgame = true; */ + } + + header.numlumps = LONG(header.numlumps); + + /* Vanilla Doom doesn't like WADs with more than 4046 lumps + * https://www.doomworld.com/vb/post/1010985 + */ + + if (!strncmp(header.identification, "PWAD", 4) && + header.numlumps > 4046) + { + w_close_file(wad_file); + i_error("Error: Vanilla limit for lumps in a WAD is 4046, " + "PWAD %s has %d", + filename, header.numlumps); + } + + header.infotableofs = LONG(header.infotableofs); + length = header.numlumps * sizeof(filelump_t); + fileinfo = z_malloc(length, PU_STATIC, 0); + + w_read(wad_file, header.infotableofs, fileinfo, length); + numfilelumps = header.numlumps; + } + + /* Increase size of numlumps array to accommodate the new file. */ + + filelumps = calloc(numfilelumps, sizeof(lumpinfo_t)); + if (filelumps == NULL) + { + w_close_file(wad_file); + i_error("Failed to allocate array for lumps from new file."); + } + + startlump = numlumps; + numlumps += numfilelumps; + lumpinfo = i_realloc(lumpinfo, numlumps * sizeof(lumpinfo_t *)); + filerover = fileinfo; + + for (i = startlump; i < numlumps; ++i) + { + lumpinfo_t *lump_p = &filelumps[i - startlump]; + lump_p->wad_file = wad_file; + lump_p->position = LONG(filerover->filepos); + lump_p->size = LONG(filerover->size); + lump_p->cache = NULL; + strncpy(lump_p->name, filerover->name, 8); + lumpinfo[i] = lump_p; + + ++filerover; + } + + z_free(fileinfo); + + if (lumphash != NULL) + { + z_free(lumphash); + lumphash = NULL; + } + + /* If this is the reload file, we need to save some details about the + * file so that we can close it later on when we do a reload. + */ + + if (reloadname) + { + reloadhandle = wad_file; + reloadlumps = filelumps; + } + + return wad_file; +} + +/* w_check_num_for_name + * Returns -1 if name not found. + */ + +lumpindex_t w_check_num_for_name(const char *name) +{ + lumpindex_t i; + + /* Do we have a hash table yet? */ + + if (lumphash != NULL) + { + int hash; + + /* We do! Excellent. */ + + hash = w_lump_name_hash(name) % numlumps; + + for (i = lumphash[hash]; i != -1; i = lumpinfo[i]->next) + { + if (!strncasecmp(lumpinfo[i]->name, name, 8)) + { + return i; + } + } + } + else + { + /* We don't have a hash table generate yet. Linear search :-( + * + * scan backwards so patch lump files take precedence + */ + + for (i = numlumps - 1; i >= 0; --i) + { + if (!strncasecmp(lumpinfo[i]->name, name, 8)) + { + return i; + } + } + } + + /* TFB. Not found. */ + + return -1; +} + +/* w_get_num_for_name + * Calls w_check_num_for_name, but bombs out if not found. + */ + +lumpindex_t w_get_num_for_name(const char *name) +{ + lumpindex_t i; + + i = w_check_num_for_name(name); + + if (i < 0) + { + i_error("w_get_num_for_name: %s not found!", name); + } + + return i; +} + +/* w_lump_length + * Returns the buffer size needed to load the given lump. + */ + +int w_lump_length(lumpindex_t lump) +{ + if (lump >= numlumps) + { + i_error("w_lump_length: %i >= numlumps", lump); + } + + return lumpinfo[lump]->size; +} + +/* w_read_lump + * Loads the lump into the given buffer, + * which must be >= w_lump_length(). + */ + +void w_read_lump(lumpindex_t lump, void *dest) +{ + int c; + lumpinfo_t *l; + + if (lump >= numlumps) + { + i_error("w_read_lump: %i >= numlumps", lump); + } + + l = lumpinfo[lump]; + + v_begin_read(l->size); + + c = w_read(l->wad_file, l->position, dest, l->size); + + if (c < l->size) + { + i_error("w_read_lump: only read %i of %i on lump %i", + c, l->size, lump); + } +} + +/* w_cache_lump_num + * + * Load a lump into memory and return a pointer to a buffer containing + * the lump data. + * + * 'tag' is the type of zone memory buffer to allocate for the lump + * (usually PU_STATIC or PU_CACHE). If the lump is loaded as + * PU_STATIC, it should be released back using w_release_lump_num + * when no longer needed (do not use z_change_tag). + */ + +void *w_cache_lump_num(lumpindex_t lumpnum, int tag) +{ + byte *result; + lumpinfo_t *lump; + + if ((unsigned)lumpnum >= numlumps) + { + i_error("w_cache_lump_num: %i >= numlumps", lumpnum); + } + + lump = lumpinfo[lumpnum]; + + /* Get the pointer to return. If the lump is in a memory-mapped + * file, we can just return a pointer to within the memory-mapped + * region. If the lump is in an ordinary file, we may already + * have it cached; otherwise, load it into memory. + */ + + if (lump->wad_file->mapped != NULL) + { + /* Memory mapped file, return from the mmapped region. */ + + result = lump->wad_file->mapped + lump->position; + } + else if (lump->cache != NULL) + { + /* Already cached, so just switch the zone tag. */ + + result = lump->cache; + z_change_tag(lump->cache, tag); + } + else + { + /* Not yet loaded, so load it now */ + + lump->cache = z_malloc(w_lump_length(lumpnum), tag, &lump->cache); + w_read_lump(lumpnum, lump->cache); + result = lump->cache; + } + + return result; +} + +/* w_cache_lump_name */ + +void *w_cache_lump_name(const char *name, int tag) +{ + return w_cache_lump_num(w_get_num_for_name(name), tag); +} + +/* Release a lump back to the cache, so that it can be reused later + * without having to read from disk again, or alternatively, discarded + * if we run out of memory. + * + * Back in Vanilla Doom, this was just done using z_change_tag + * directly, but now that we have WAD mmap, things are a bit more + * complicated ... + */ + +void w_release_lump_num(lumpindex_t lumpnum) +{ + lumpinfo_t *lump; + + if ((unsigned)lumpnum >= numlumps) + { + i_error("w_release_lump_num: %i >= numlumps", lumpnum); + } + + lump = lumpinfo[lumpnum]; + + if (lump->wad_file->mapped != NULL) + { + /* Memory-mapped file, so nothing needs to be done here. */ + } + else + { + z_change_tag(lump->cache, PU_CACHE); + } +} + +void w_release_lump_name(const char *name) +{ + w_release_lump_num(w_get_num_for_name(name)); +} + +#if 0 + +/* w_profile */ + +int info[2500][10]; +int profilecount; + +void w_profile(void) +{ + int i; + memblock_t *block; + void *ptr; + char ch; + FILE *f; + int j; + char name[9]; + + for (i = 0; i < numlumps; i++) + { + ptr = lumpinfo[i].cache; + 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 = fopen("waddump.txt", "w"); + name[8] = 0; + + for (i = 0; i < numlumps; i++) + { + memcpy(name, lumpinfo[i].name, 8); + + for (j = 0; j < 8; j++) + { + if (!name[j]) + { + break; + } + } + + for (; j < 8; j++) + { + name[j] = ' '; + } + + fprintf(f, "%s ", name); + + for (j = 0; j < profilecount; j++) + { + fprintf(f, " %c", info[i][j]); + } + + fprintf(f, "\n"); + } + + fclose(f); +} +#endif + +/* Generate a hash table for fast lookups */ + +void w_generate_hash_table(void) +{ + lumpindex_t i; + + /* Free the old hash table, if there is one: */ + + if (lumphash != NULL) + { + z_free(lumphash); + } + + /* Generate hash table */ + + if (numlumps > 0) + { + lumphash = z_malloc(sizeof(lumpindex_t) * numlumps, PU_STATIC, NULL); + + for (i = 0; i < numlumps; ++i) + { + lumphash[i] = -1; + } + + for (i = 0; i < numlumps; ++i) + { + unsigned int hash; + + hash = w_lump_name_hash(lumpinfo[i]->name) % numlumps; + + /* Hook into the hash table */ + + lumpinfo[i]->next = lumphash[hash]; + lumphash[hash] = i; + } + } + + /* All done! */ +} + +/* The Doom reload hack. The idea here is that if you give a WAD file to + * -file prefixed with the ~ hack, that WAD file will be reloaded each time a + * new level is loaded. This lets you use a level editor in parallel and make + * incremental changes to the level you're working on without having to + * restart the game after every change. But: the reload feature is a fragile + * hack... + */ + +void w_reload(void) +{ + char *filename; + lumpindex_t i; + + if (reloadname == NULL) + { + return; + } + + /* We must free any lumps being cached from the PWAD we're about to reload: + */ + + for (i = reloadlump; i < numlumps; ++i) + { + if (lumpinfo[i]->cache != NULL) + { + z_free(lumpinfo[i]->cache); + } + } + + /* Reset numlumps to remove the reload WAD file: */ + + numlumps = reloadlump; + + /* Now reload the WAD file. */ + + filename = reloadname; + + w_close_file(reloadhandle); + free(reloadlumps); + + reloadname = NULL; + reloadlump = -1; + reloadhandle = NULL; + w_add_file(filename); + free(filename); + + /* The WAD directory has changed, so we have to regenerate the + * fast lookup hashtable: + */ + + w_generate_hash_table(); +} + +const char *w_wad_name_for_lump(const lumpinfo_t *lump) +{ + return m_base_name(lump->wad_file->path); +} + +boolean w_is_iwad_lump(const lumpinfo_t *lump) +{ + return lump->wad_file == lumpinfo[0]->wad_file; +} diff --git a/games/NXDoom/src/w_wad.h b/games/NXDoom/src/w_wad.h new file mode 100644 index 00000000000..867ac5add0f --- /dev/null +++ b/games/NXDoom/src/w_wad.h @@ -0,0 +1,91 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_wad.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * WAD I/O functions. + * + ****************************************************************************/ + +#ifndef __W_WAD__ +#define __W_WAD__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" +#include "w_file.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* WADFILE I/O related stuff. */ + +typedef struct lumpinfo_s lumpinfo_t; +typedef int lumpindex_t; + +struct lumpinfo_s +{ + char name[8]; + wad_file_t *wad_file; + int position; + int size; + void *cache; + + /* Used for hash table lookups */ + + lumpindex_t next; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern lumpinfo_t **lumpinfo; +extern unsigned int numlumps; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +wad_file_t *w_add_file(const char *filename); +void w_reload(void); + +lumpindex_t w_check_num_for_name(const char *name); +lumpindex_t w_get_num_for_name(const char *name); + +int w_lump_length(lumpindex_t lump); +void w_read_lump(lumpindex_t lump, void *dest); + +void *w_cache_lump_num(lumpindex_t lump, int tag); +void *w_cache_lump_name(const char *name, int tag); + +void w_generate_hash_table(void); + +extern unsigned int w_lump_name_hash(const char *s); + +void w_release_lump_num(lumpindex_t lump); +void w_release_lump_name(const char *name); + +const char *w_wad_name_for_lump(const lumpinfo_t *lump); +boolean w_is_iwad_lump(const lumpinfo_t *lump); + +#endif /* __W_WAD_H__ */ diff --git a/games/NXDoom/src/z_native.c b/games/NXDoom/src/z_native.c new file mode 100644 index 00000000000..3d962bbd02b --- /dev/null +++ b/games/NXDoom/src/z_native.c @@ -0,0 +1,556 @@ +/**************************************************************************** + * apps/games/NXDoom/src/z_native.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Zone Memory Allocation. Neat. + * + * This is an implementation of the zone memory API which + * uses native calls to malloc() and free(). + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ZONEID 0x1d4a11 + +#ifdef TESTING +#define malloc test_malloc +#define free test_free +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct memblock_s memblock_t; + +struct memblock_s +{ + int id; /* = ZONEID */ + int tag; + int size; + void **user; + memblock_t *prev; + memblock_t *next; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Linked list of allocated blocks for each tag type */ + +static memblock_t *allocated_blocks[PU_NUM_TAGS]; + +#ifdef TESTING +static int test_malloced = 0; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: z_insert_block + * + * Description: + * Add a block into the linked list for its type. + * + ****************************************************************************/ + +static void z_insert_block(memblock_t *block) +{ + block->prev = NULL; + block->next = allocated_blocks[block->tag]; + allocated_blocks[block->tag] = block; + + if (block->next != NULL) + { + block->next->prev = block; + } +} + +/**************************************************************************** + * Name: z_insert_block + * + * Description: + * Remove a block from its linked list. + * + ****************************************************************************/ + +static void z_remove_block(memblock_t *block) +{ + /* Unlink from list */ + + if (block->prev == NULL) + { + /* Start of list */ + + allocated_blocks[block->tag] = block->next; + } + else + { + if (block->prev->next != block) + { + i_error("Z_RemoveBlock: Doubly-linked list corrupted!"); + } + + block->prev->next = block->next; + } + + if (block->next != NULL) + { + if (block->next->prev != block) + { + i_error("Z_RemoveBlock: Doubly-linked list corrupted!"); + } + + block->next->prev = block->prev; + } +} + +/**************************************************************************** + * Name: clear_cache + * + * Description: + * Empty data from the cache list to allocate enough data of the size + * required. + * + * Return: + * Returns true if any blocks were freed. + * + ****************************************************************************/ + +static boolean clear_cache(int size) +{ + memblock_t *block; + memblock_t *next_block; + int remaining; + + block = allocated_blocks[PU_CACHE]; + + if (block == NULL) + { + /* Cache is already empty. */ + + return false; + } + + /* Search to the end of the PU_CACHE list. The blocks at the end + * of the list are the ones that have been free for longer and + * are more likely to be unneeded now. + */ + + while (block->next != NULL) + { + block = block->next; + } + + /* Search backwards through the list freeing blocks until we have + * freed the amount of memory required. + */ + + remaining = size; + + while (remaining > 0) + { + if (block == NULL) + { + break; /* No blocks left to free; we've done our best. */ + } + + next_block = block->prev; + + z_remove_block(block); + + remaining -= block->size; + + if (block->user) + { + *block->user = NULL; + } + + free(block); + + block = next_block; + } + + return true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef TESTING +void *test_malloc(size_t size) +{ + int *result; + + if (test_malloced + size > 2 * 1024 * 1024) + { + return NULL; + } + + test_malloced += size; + + result = malloc(size + sizeof(int)); + + *result = size; + + return result + 1; +} + +void test_free(void *data) +{ + int *i; + + i = ((int *)data) - 1; + + test_malloced -= *i; + + free(i); +} +#endif /* #ifdef TESTING */ + +/**************************************************************************** + * Name: z_init + ****************************************************************************/ + +void z_init(void) +{ + memset(allocated_blocks, 0, sizeof(allocated_blocks)); + printf("zone memory: Using native C allocator.\n"); +} + +/**************************************************************************** + * Name: z_free + ****************************************************************************/ + +void z_free(void *ptr) +{ + memblock_t *block; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + { + i_error("z_free: freed a pointer without ZONEID"); + } + + if (block->tag != PU_FREE && block->user != NULL) + { + /* clear the user's mark */ + + *block->user = NULL; + } + + z_remove_block(block); + + /* Free back to system */ + + free(block); +} + +/**************************************************************************** + * Name: z_malloc + * + * Description: + * You can pass a NULL user if the tag is < PU_PURGELEVEL. + * + * Return: + * The translated key code. + * + ****************************************************************************/ + +void *z_malloc(int size, int tag, void *user) +{ + memblock_t *newblock; + unsigned char *data; + void *result; + + if (tag < 0 || tag >= PU_NUM_TAGS || tag == PU_FREE) + { + i_error("z_malloc: attempted to allocate a block with an invalid " + "tag: %i", + tag); + } + + if (user == NULL && tag >= PU_PURGELEVEL) + { + i_error("z_malloc: an owner is required for purgeable blocks"); + } + + /* Malloc a block of the required size */ + + newblock = NULL; + + while (newblock == NULL) + { + newblock = (memblock_t *)malloc(sizeof(memblock_t) + size); + + if (newblock == NULL) + { + if (!clear_cache(sizeof(memblock_t) + size)) + { + i_error("z_malloc: failed on allocation of %i bytes", size); + } + } + } + + newblock->tag = tag; + + /* Hook into the linked list for this tag type */ + + newblock->id = ZONEID; + newblock->user = user; + newblock->size = size; + + z_insert_block(newblock); + + data = (unsigned char *)newblock; + result = data + sizeof(memblock_t); + + if (user != NULL) + { + *newblock->user = result; + } + + return result; +} + +/**************************************************************************** + * Name: z_free_tags + ****************************************************************************/ + +void z_free_tags(int lowtag, int hightag) +{ + int i; + + for (i = lowtag; i <= hightag; ++i) + { + memblock_t *block; + memblock_t *next; + + /* Free all in this chain */ + + for (block = allocated_blocks[i]; block != NULL; ) + { + next = block->next; + + /* Free this block */ + + if (block->user != NULL) + { + *block->user = NULL; + } + + free(block); + + /* Jump to the next in the chain */ + + block = next; + } + + /* This chain is empty now */ + + allocated_blocks[i] = NULL; + } +} + +/**************************************************************************** + * Name: z_dump_heap + ****************************************************************************/ + +void z_dump_heap(int lowtag, int hightag) +{ + /* WARN: broken */ + +#if 0 + memblock_t *block; + + printf("zone size: %i location: %p\n", mainzone->size, mainzone); + + printf("tag range: %i to %i\n", lowtag, hightag); + + for (block = mainzone->blocklist.next; ; block = block->next) + { + if (block->tag >= lowtag && block->tag <= hightag) + printf("block:%p size:%7i user:%p tag:%3i\n", block, + block->size, block->user, block->tag); + + if (block->next == &mainzone->blocklist) + { + /* all blocks have been hit */ + + break; + } + + if ((byte *)block + block->size != (byte *)block->next) + printf("ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + printf("ERROR: next block doesn't have proper back link\n"); + + if (block->tag == PU_FREE && block->next->tag == PU_FREE) + printf("ERROR: two consecutive free blocks\n"); + } +#endif +} + +/**************************************************************************** + * Name: z_file_dump_heap + ****************************************************************************/ + +void z_file_dump_heap(FILE *f) +{ + /* WARN: broken */ +#if 0 + memblock_t *block; + + fprintf(f, "zone size: %i location: %p\n", mainzone->size, mainzone); + + 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); + + if (block->next == &mainzone->blocklist) + { + /* all blocks have been hit */ + + break; + } + + if ((byte *)block + block->size != (byte *)block->next) + fprintf(f, "ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + fprintf(f, "ERROR: next block doesn't have proper back link\n"); + + if (block->tag == PU_FREE && block->next->tag == PU_FREE) + fprintf(f, "ERROR: two consecutive free blocks\n"); + } +#endif +} + +/**************************************************************************** + * Name: z_check_heap + ****************************************************************************/ + +void z_check_heap(void) +{ + memblock_t *block; + memblock_t *prev; + int i; + + /* Check all chains */ + + for (i = 0; i < PU_NUM_TAGS; ++i) + { + prev = NULL; + + for (block = allocated_blocks[i]; block != NULL; block = block->next) + { + if (block->id != ZONEID) + { + i_error("z_check_heap: Block without a ZONEID!"); + } + + if (block->prev != prev) + { + i_error("z_check_heap: Doubly-linked list corrupted!"); + } + + prev = block; + } + } +} + +/**************************************************************************** + * Name: z_change_tag2 + ****************************************************************************/ + +void z_change_tag2(void *ptr, int tag, const char *file, int line) +{ + memblock_t *block; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + i_error("%s:%i: z_change_tag: block without a ZONEID!", file, line); + + if (tag >= PU_PURGELEVEL && block->user == NULL) + i_error("%s:%i: z_change_tag: an owner is required " + "for purgeable blocks", + file, line); + + /* Remove the block from its current list, and rehook it into + * its new list. + */ + + z_remove_block(block); + block->tag = tag; + z_insert_block(block); +} + +void z_change_user(void *ptr, void **user) +{ + memblock_t *block; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + { + i_error("z_change_user: Tried to change user for invalid block!"); + } + + block->user = user; + *user = ptr; +} + +/**************************************************************************** + * Name: z_free_memory + ****************************************************************************/ + +int z_free_memory(void) +{ + return -1; /* Limited by the system?? */ +} + +/**************************************************************************** + * Name: z_zone_size + ****************************************************************************/ + +unsigned int z_zone_size(void) +{ + return 0; +} diff --git a/games/NXDoom/src/z_zone.c b/games/NXDoom/src/z_zone.c new file mode 100644 index 00000000000..f5274e334fa --- /dev/null +++ b/games/NXDoom/src/z_zone.c @@ -0,0 +1,557 @@ +/**************************************************************************** + * apps/games/NXDoom/src/z_zone.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * DESCRIPTION: + * Zone Memory Allocation. Neat. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" + +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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 cacheable block, + * because it will get overwritten automatically if needed. + */ + +#define MEM_ALIGN sizeof(void *) +#define ZONEID 0x1d4a11 + +#define MINFRAGMENT 64 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct memblock_s +{ + int size; /* including the header and possibly tiny fragments */ + void **user; + int tag; /* PU_FREE if this is free */ + int id; /* should be ZONEID */ + struct memblock_s *next; + struct memblock_s *prev; +} memblock_t; + +typedef struct +{ + /* total bytes malloced, including header */ + + int size; + + /* start / end cap for linked list */ + + memblock_t blocklist; + + memblock_t *rover; +} memzone_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static memzone_t *mainzone; +static boolean zero_on_free; +static boolean scan_on_free; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: scan_for_block + * + * Description: + * Scan the zone heap for pointers within the specified range, and warn + * about any remaining pointers. + * + ****************************************************************************/ + +static void scan_for_block(void *start, void *end) +{ + memblock_t *block; + void **mem; + int i; + int len; + int tag; + + block = mainzone->blocklist.next; + + while (block->next != &mainzone->blocklist) + { + tag = block->tag; + + if (tag == PU_STATIC || tag == PU_LEVEL || tag == PU_LEVSPEC) + { + /* Scan for pointers on the assumption that pointers are aligned + * on word boundaries (word size depending on pointer size): + */ + + mem = (void **)((byte *)block + sizeof(memblock_t)); + len = (block->size - sizeof(memblock_t)) / sizeof(void *); + + for (i = 0; i < len; ++i) + { + if (start <= mem[i] && mem[i] <= end) + { + fprintf(stderr, + "%p has dangling pointer into freed block " + "%p (%p -> %p)\n", + mem, start, &mem[i], mem[i]); + } + } + } + + block = block->next; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: z_init + ****************************************************************************/ + +void z_init(void) +{ + memblock_t *block; + int size; + + mainzone = (memzone_t *)i_zone_base(&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; + + block->tag = PU_FREE; /* free block */ + + block->size = mainzone->size - sizeof(memzone_t); + + /* [Deliberately undocumented] + * Zone memory debugging flag. If set, memory is zeroed after it is freed + * to deliberately break any code that attempts to use it after free. + */ + + zero_on_free = m_parm_exists("-zonezero"); + + /* [Deliberately undocumented] + * Zone memory debugging flag. If set, each time memory is freed, the zone + * heap is scanned to look for remaining pointers to the freed block. + */ + + scan_on_free = m_parm_exists("-zonescan"); +} + +/**************************************************************************** + * Name: 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("z_free: freed a pointer without ZONEID"); + + if (block->tag != PU_FREE && block->user != NULL) + { + *block->user = 0; /* clear the user's mark */ + } + + /* mark as free */ + + block->tag = PU_FREE; + block->user = NULL; + block->id = 0; + + /* If the -zonezero flag is provided, we zero out the block on free + * to break code that depends on reading freed memory. + */ + + if (zero_on_free) + { + memset(ptr, 0, block->size - sizeof(memblock_t)); + } + + if (scan_on_free) + { + scan_for_block(ptr, (byte *)ptr + block->size - sizeof(memblock_t)); + } + + other = block->prev; + + if (other->tag == PU_FREE) + { + /* 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->tag == PU_FREE) + { + /* merge with previous free block */ + + block->size += other->size; + block->next = other->next; + block->next->prev = block; + + if (other == mainzone->rover) mainzone->rover = block; + } +} + +/**************************************************************************** + * Name: z_malloc + * + * Description: + * You can pass a NULL 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; + void *result; + + size = (size + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1); + + /* scan through the block list, looking for the first free block of + * sufficient size, throwing out any purgeable 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->tag == PU_FREE) base = base->prev; + + rover = base; + start = base->prev; + + do + { + if (rover == start) + { + /* scanned all the way around the list */ + + i_error("z_malloc: failed on allocation of %i bytes", size); + } + + if (rover->tag != PU_FREE) + { + 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->tag != PU_FREE || 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; + + newblock->tag = PU_FREE; + newblock->user = NULL; + newblock->prev = base; + newblock->next = base->next; + newblock->next->prev = newblock; + + base->next = newblock; + base->size = size; + } + + if (user == NULL && tag >= PU_PURGELEVEL) + i_error("z_malloc: an owner is required for purgeable blocks"); + + base->user = user; + base->tag = tag; + + result = (void *)((byte *)base + sizeof(memblock_t)); + + if (base->user) + { + *base->user = result; + } + + /* next allocation will start looking here */ + + mainzone->rover = base->next; + + base->id = ZONEID; + + return result; +} + +/**************************************************************************** + * Name: z_free_tags + ****************************************************************************/ + +void z_free_tags(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->tag == PU_FREE) continue; + + if (block->tag >= lowtag && block->tag <= hightag) + z_free((byte *)block + sizeof(memblock_t)); + } +} + +/**************************************************************************** + * Name: z_dump_heap + * + * Description: + * Note: TFileDumpHeap( stdout ) ? + * + ****************************************************************************/ + +void z_dump_heap(int lowtag, int hightag) +{ + memblock_t *block; + + printf("zone size: %i location: %p\n", mainzone->size, mainzone); + + printf("tag range: %i to %i\n", lowtag, hightag); + + for (block = mainzone->blocklist.next; ; block = block->next) + { + if (block->tag >= lowtag && block->tag <= hightag) + printf("block:%p size:%7i user:%p tag:%3i\n", block, + block->size, block->user, block->tag); + + if (block->next == &mainzone->blocklist) + { + break; /* all blocks have been hit */ + } + + if ((byte *)block + block->size != (byte *)block->next) + printf("ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + printf("ERROR: next block doesn't have proper back link\n"); + + if (block->tag == PU_FREE && block->next->tag == PU_FREE) + printf("ERROR: two consecutive free blocks\n"); + } +} + +/**************************************************************************** + * Name: z_file_dump_heap + ****************************************************************************/ + +void z_file_dump_heap(FILE *f) +{ + memblock_t *block; + + fprintf(f, "zone size: %i location: %p\n", mainzone->size, mainzone); + + 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); + + if (block->next == &mainzone->blocklist) + { + break; /* all blocks have been hit */ + } + + if ((byte *)block + block->size != (byte *)block->next) + fprintf(f, "ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + fprintf(f, "ERROR: next block doesn't have proper back link\n"); + + if (block->tag == PU_FREE && block->next->tag == PU_FREE) + fprintf(f, "ERROR: two consecutive free blocks\n"); + } +} + +/**************************************************************************** + * Name: z_check_heap + ****************************************************************************/ + +void z_check_heap(void) +{ + memblock_t *block; + + for (block = mainzone->blocklist.next; ; block = block->next) + { + if (block->next == &mainzone->blocklist) + { + break; /* all blocks have been hit */ + } + + if ((byte *)block + block->size != (byte *)block->next) + i_error("z_check_heap: block size does not touch the next block\n"); + + if (block->next->prev != block) + i_error("z_check_heap: next block doesn't have proper back link\n"); + + if (block->tag == PU_FREE && block->next->tag == PU_FREE) + i_error("z_check_heap: two consecutive free blocks\n"); + } +} + +/**************************************************************************** + * Name: z_change_tag2 + ****************************************************************************/ + +void z_change_tag2(void *ptr, int tag, const char *file, int line) +{ + memblock_t *block; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + i_error("%s:%i: z_change_tag: block without a ZONEID!", file, line); + + if (tag >= PU_PURGELEVEL && block->user == NULL) + i_error("%s:%i: z_change_tag: an owner is required " + "for purgeable blocks", + file, line); + + block->tag = tag; +} + +/**************************************************************************** + * Name: z_change_user + ****************************************************************************/ + +void z_change_user(void *ptr, void **user) +{ + memblock_t *block; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + { + i_error("z_change_user: Tried to change user for invalid block!"); + } + + block->user = user; + *user = ptr; +} + +/**************************************************************************** + * Name: z_free_memory + ****************************************************************************/ + +int z_free_memory(void) +{ + memblock_t *block; + int free; + + free = 0; + + for (block = mainzone->blocklist.next; block != &mainzone->blocklist; + block = block->next) + { + if (block->tag == PU_FREE || block->tag >= PU_PURGELEVEL) + free += block->size; + } + + return free; +} + +/**************************************************************************** + * Name: z_zone_size + ****************************************************************************/ + +unsigned int z_zone_size(void) +{ + return mainzone->size; +} diff --git a/games/NXDoom/src/z_zone.h b/games/NXDoom/src/z_zone.h new file mode 100644 index 00000000000..4df6bb3fb84 --- /dev/null +++ b/games/NXDoom/src/z_zone.h @@ -0,0 +1,89 @@ +/**************************************************************************** + * apps/games/NXDoom/src/z_zone.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * 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__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* This is used to get the local FILE:LINE info from CPP prior to really call + * the function in question. + */ + +#define z_change_tag(p, t) z_change_tag2((p), (t), __FILE__, __LINE__) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* ZONE MEMORY + * PU - purge tags. + */ + +enum +{ + PU_STATIC = 1, /* static entire execution time */ + PU_SOUND, /* static while playing */ + PU_MUSIC, /* static while playing */ + PU_FREE, /* a free block */ + PU_LEVEL, /* static until level exited */ + PU_LEVSPEC, /* a special thinker in a level */ + + /* Tags >= PU_PURGELEVEL are purgeable whenever needed. */ + + PU_PURGELEVEL, + PU_CACHE, + + /* Total number of different tag types */ + + PU_NUM_TAGS +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void z_init(void); +void *z_malloc(int size, int tag, void *ptr); +void z_free(void *ptr); +void z_free_tags(int lowtag, int hightag); +void z_dump_heap(int lowtag, int hightag); +void z_file_dump_heap(FILE *f); +void z_check_heap(void); +void z_change_tag2(void *ptr, int tag, const char *file, int line); +void z_change_user(void *ptr, void **user); +int z_free_memory(void); +unsigned int z_zone_size(void); + +#endif /* __Z_ZONE__ */ diff --git a/games/NXDoom/textscreen/.gitignore b/games/NXDoom/textscreen/.gitignore new file mode 100644 index 00000000000..3be8ec0c79f --- /dev/null +++ b/games/NXDoom/textscreen/.gitignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +.deps +*.a +tags +TAGS diff --git a/games/NXDoom/textscreen/fonts/README b/games/NXDoom/textscreen/fonts/README new file mode 100644 index 00000000000..a9b9b1b24f2 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/README @@ -0,0 +1,38 @@ + +This directory contains the fonts used by libtextscreen: + + * normal.png is the standard DOS font, from the DOSbox project. + Copyright (C) 2002-2004 The DOSBox Team + + * small.png contains a miniature, half-size font for low-resolution + displays. This is based on the Atari-Small font by Tom Fine. The + original font was standard ASCII only; this has been extended to the + full Extended ASCII range with scaled-down versions of the full-size + DOS font. Original copyright notice: + + Copyright (c) 1999, Thomas A. Fine + + License to copy, modify, and distribute for both commercial and + non-commercial use is herby granted, provided this notice + is preserved. + + Email to my last name at head.cfa.harvard.edu + http://hea-www.harvard.edu/~fine/ + + * large.png is a scaled-up version of normal.png with extra detail + added; this is for use on modern high-resolution ("retina") displays. + +All modifications and enhancements to the above fonts are: + + Copyright (C) 2005-2016 Simon Howard + + 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. + diff --git a/games/NXDoom/textscreen/fonts/codepage.h b/games/NXDoom/textscreen/fonts/codepage.h new file mode 100644 index 00000000000..f506c90e409 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/codepage.h @@ -0,0 +1,105 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/fonts/codepage.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2017 Simon Howard + * + * 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. + * + * This file contains a #define macro mapping from the characters + * in the CP437 code page to the equivalent Unicode characters. It + * therefore defines which Unicode characters can be represented on + * a text screen using such a code page. + * + * If you're changing the textscreen font to show a different code + * page, you should update the mapping in this file to match. The + * Wikipedia pages for DOS code pages are a good place to look for + * this information. + * + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CODE_PAGE_TO_UNICODE \ + { \ + /* CP437 control codes: */ \ + \ + 0x0000, 0x263a, 0x263b, 0x2665, /* 00 - 0f */ \ + 0x2666, 0x2663, 0x2660, 0x2022, 0x25d8, 0x25cb, 0x25d9, \ + 0x2642, 0x2640, 0x266a, 0x266b, 0x263c, \ + \ + 0x25ba, 0x25c4, 0x2195, 0x203c, /* 10 - 1f */ \ + 0x00b6, 0x00a7, 0x25ac, 0x21a8, 0x2191, 0x2193, 0x2192, \ + 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc, \ + \ + /* Standard ASCII range: */ \ + \ + 0x0020, 0x0021, 0x0022, 0x0023, /* 20 - 2f */ \ + 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, \ + 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, \ + \ + 0x0030, 0x0031, 0x0032, 0x0033, /* 30 - 3f */ \ + 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, \ + 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, \ + \ + 0x0040, 0x0041, 0x0042, 0x0043, /* 40 - 4f */ \ + 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, \ + 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, \ + \ + 0x0050, 0x0051, 0x0052, 0x0053, /* 50 - 5f */ \ + 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, \ + 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, \ + \ + 0x0060, 0x0061, 0x0062, 0x0063, /* 60 - 6f */ \ + 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, \ + 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, \ + \ + 0x0070, 0x0071, 0x0072, 0x0073, /* 70 - 7f */ \ + 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, \ + 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, \ + \ + /* CP437 Extended ASCII range: */ \ + \ + 0x00c7, 0x00fc, 0x00e9, 0x00e2, /* 80-8f */ \ + 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, \ + 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, \ + \ + 0x00c9, 0x00e6, 0x00c6, 0x00f4, /* 90-9f */ \ + 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, \ + 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, \ + \ + 0x00e1, 0x00ed, 0x00f3, 0x00fa, /* a0-af */ \ + 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, \ + 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, \ + \ + 0x2591, 0x2592, 0x2593, 0x2502, /* b0-bf */ \ + 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, \ + 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, \ + \ + 0x2514, 0x2534, 0x252c, 0x251c, /* c0-cf */ \ + 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, \ + 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, \ + \ + 0x2568, 0x2564, 0x2565, 0x2559, /* d0-df */ \ + 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, \ + 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, \ + \ + 0x03b1, 0x00df, 0x0393, 0x03c0, /* e0-ef */ \ + 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, \ + 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, \ + \ + 0x2261, 0x00b1, 0x2265, 0x2264, /* f0-ff */ \ + 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, \ + 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0, \ + } diff --git a/games/NXDoom/textscreen/fonts/convert-font b/games/NXDoom/textscreen/fonts/convert-font new file mode 100644 index 00000000000..b3d828cc55c --- /dev/null +++ b/games/NXDoom/textscreen/fonts/convert-font @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# +# Copyright(C) 2016 Simon Howard +# +# 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. +# +# +# Converts images into libtextscreen fonts. +# + +import sys +import os +import re + +GRID_COLUMNS = 16 +GRID_ROWS = 16 + +try: + import Image +except ImportError: + try: + from PIL import Image + except ImportError: + print("WARNING: Could not update %s. " + "Please install the Python Imaging library or Pillow." + % sys.argv[3]) + sys.exit(0) + + +def generate_font_data(filename): + """Read font data from the given file. + + Args: + filename: Image file to read. + Returns: + Tuple containing: + (width, height) dimensions of each font character. + Array of byte data for font. + """ + im = Image.open(filename) + width, height = im.size + assert (width % GRID_COLUMNS) == 0 + assert (height % GRID_ROWS) == 0 + + char_w, char_h = width // GRID_COLUMNS, height // GRID_ROWS + font_data_len = (width * height) // 8 + font_data = [0] * font_data_len + + for y in range(height): + for x in range(width): + px = im.getpixel((x, y)) + if px > (127, 127, 127): + char_x, char_y = x // char_w, y // char_h + x1, y1 = x % char_w, y % char_h + bit_index = ( + (char_y * GRID_COLUMNS + char_x) * (char_w * char_h) + + (y1 * char_w) + x1) + font_data[bit_index // 8] |= 1 << (bit_index % 8) + + return (char_w, char_h), font_data + +def convert_image(font_prefix, filename, output_filename): + """Convert the given image to a text output file. + + Args: + font_prefix: Prefix string to attach to constants. + filename: Input image file to read. + output_filename: Header file to write. + """ + dimensions, data = generate_font_data(filename) + + with open(output_filename, "w") as outfile: + outfile.write("/* Font data generated from %s; do not edit.\n" + " Please see textscreen/fonts/README for copyright\n" + " information. */\n\n" % filename) + outfile.write("static const uint8_t %s_font_data[] =\n{\n" % + font_prefix) + for index, b in enumerate(data): + if (index % 8) == 0: + outfile.write(" ") + outfile.write("0x%02x," % b) + if ((index + 1) % 8) == 0: + outfile.write("\n") + else: + outfile.write(" ") + outfile.write("};\n") + + outfile.write("\n") + outfile.write("static const txt_font_t %s_font =\n{\n" % font_prefix) + outfile.write(" \"%s\", %s_font_data, %d, %d,\n" % ( + font_prefix, font_prefix, + dimensions[0], dimensions[1])) + outfile.write("};\n") + +convert_image(sys.argv[1], sys.argv[2], sys.argv[3]) + diff --git a/games/NXDoom/textscreen/fonts/large.h b/games/NXDoom/textscreen/fonts/large.h new file mode 100644 index 00000000000..a58692d9171 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/large.h @@ -0,0 +1,1391 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/fonts/large.h + * + * SPDX-License-Identifer: GPLv2 + * + * Font data generated from large.png; do not edit. Please see + * textscreen/fonts/README for copyright information. + * + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const uint8_t large_font_data[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0xfe, 0x7f, 0x07, 0xe0, 0x03, 0xc0, 0x33, 0xcc, 0x33, 0xcc, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xf3, 0xcf, 0xf3, 0xcf, + 0xe3, 0xc7, 0xc3, 0xc3, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x07, 0xe0, + 0xfe, 0x7f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, + 0xcf, 0xf3, 0xcf, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0xf0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0e, 0x3e, 0x1f, 0xff, 0x3f, 0xff, 0x3f, + 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfc, 0x0f, + 0xfc, 0x0f, 0xf8, 0x07, 0xf0, 0x03, 0xe0, 0x01, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, + 0xf0, 0x03, 0xf8, 0x07, 0xfc, 0x0f, 0xfe, 0x1f, 0xff, 0x3f, 0xff, 0x3f, + 0xfe, 0x1f, 0xfc, 0x0f, 0xf8, 0x07, 0xf0, 0x03, 0xe0, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe0, 0x07, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0x3e, 0x7c, 0x3f, 0xfc, + 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3e, 0x7c, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0xfe, 0x7f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xdc, 0x3b, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x1f, + 0x3c, 0x3c, 0x1c, 0x38, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, + 0x1c, 0x38, 0x3c, 0x3c, 0xf8, 0x1f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0xf0, 0x07, 0xe0, 0xc3, 0xc3, 0xe3, 0xc7, 0xf3, 0xcf, 0xf3, 0xcf, + 0xf3, 0xcf, 0xf3, 0xcf, 0xe3, 0xc7, 0xc3, 0xc3, 0x07, 0xe0, 0x0f, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0xc0, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x80, 0x37, 0xc0, 0x33, 0xe0, 0x31, 0xf0, 0x30, 0xfc, 0x03, 0xfe, 0x07, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0xfe, 0x07, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x1f, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0xf8, 0x1f, 0xf0, 0x0f, 0xc0, 0x03, 0xc0, 0x03, + 0xfc, 0x3f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xff, 0xf0, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xff, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xfc, 0x00, 0xfe, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x7f, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xfc, 0xff, 0x3c, 0xf0, 0x3c, 0xf0, + 0xfc, 0xff, 0xfc, 0xff, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, + 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf8, 0x3c, 0xfc, + 0x3e, 0xfc, 0x3f, 0xfc, 0x3f, 0x7c, 0x3f, 0x38, 0x1f, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc3, 0xc3, 0xcf, 0xf3, + 0xfc, 0x3f, 0xf0, 0x0f, 0x3f, 0xfc, 0x3f, 0xfc, 0xf0, 0x0f, 0xfc, 0x3f, + 0xcf, 0xf3, 0xc3, 0xc3, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x7f, 0x00, 0xff, 0x00, + 0xff, 0x01, 0xff, 0x03, 0xff, 0x07, 0xff, 0x07, 0xff, 0x03, 0xff, 0x01, + 0xff, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x07, 0x00, + 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x30, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x3e, 0x00, 0x3f, + 0x80, 0x3f, 0xc0, 0x3f, 0xe0, 0x3f, 0xf0, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f, + 0xf0, 0x3f, 0xe0, 0x3f, 0xc0, 0x3f, 0x80, 0x3f, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3c, 0x00, 0x38, 0x00, 0x30, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xe0, 0x07, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x3f, 0xf8, 0x1f, + 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3c, 0x3c, + 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xfe, 0xff, 0xc7, 0xf3, 0xc7, 0xf3, + 0xc7, 0xf3, 0xc7, 0xf3, 0xc7, 0xf3, 0xc7, 0xf3, 0xfe, 0xf3, 0xfc, 0xf3, + 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, + 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x38, + 0x3e, 0x00, 0xfc, 0x00, 0xf8, 0x03, 0xf0, 0x07, 0x38, 0x0f, 0x1c, 0x1e, + 0x0e, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x1c, 0x1e, 0x0e, 0x3c, 0x07, + 0xf8, 0x03, 0xf0, 0x03, 0xc0, 0x0f, 0x00, 0x1f, 0x07, 0x3e, 0x0f, 0x3c, + 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, + 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, + 0xf8, 0x1f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x3f, 0xf8, 0x1f, 0xf0, 0x0f, 0xe0, 0x07, + 0xc0, 0x03, 0x80, 0x01, 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xe0, 0x07, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x3f, 0xf8, 0x1f, 0xf0, 0x0f, 0xe0, 0x07, + 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, + 0xff, 0x1f, 0xff, 0x1f, 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x70, 0x00, + 0x38, 0x00, 0x1c, 0x00, 0xfe, 0x3f, 0xfe, 0x3f, 0x1c, 0x00, 0x38, 0x00, + 0x70, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x30, 0x0c, 0x38, 0x1c, 0x3c, 0x3c, + 0xfe, 0x7f, 0xfe, 0x7f, 0x3c, 0x3c, 0x38, 0x1c, 0x30, 0x0c, 0x20, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xe0, 0x01, + 0xf0, 0x03, 0xf0, 0x03, 0xf8, 0x07, 0xf8, 0x07, 0xfc, 0x0f, 0xfc, 0x0f, + 0xfe, 0x1f, 0xfe, 0x1f, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, + 0xfe, 0x1f, 0xfe, 0x1f, 0xfc, 0x0f, 0xfc, 0x0f, 0xf8, 0x07, 0xf8, 0x07, + 0xf0, 0x03, 0xf0, 0x03, 0xe0, 0x01, 0xe0, 0x01, 0xc0, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe0, 0x07, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xe0, 0x07, 0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x38, 0x1c, 0x30, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x07, 0x38, 0x07, + 0x38, 0x07, 0x38, 0x07, 0xff, 0x3f, 0xff, 0x3f, 0x38, 0x07, 0x38, 0x07, + 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0xff, 0x3f, 0xff, 0x3f, + 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x38, 0x0f, 0x30, 0x0f, 0x30, 0x0f, 0x00, 0x0f, 0x00, + 0xfe, 0x0f, 0xfc, 0x1f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x03, 0x3c, 0x03, 0x3c, 0x07, 0x3c, 0x0f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, 0x0f, 0x38, + 0x0f, 0x3c, 0x06, 0x1e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x18, 0x07, 0x3c, + 0x03, 0x3c, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf8, 0x07, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x38, 0x07, 0xf0, 0x03, 0xf0, 0x03, 0xfc, 0x30, 0xfe, 0x38, + 0xcf, 0x1f, 0xcf, 0x1f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x8f, 0x0f, 0xfe, 0x3f, 0xfc, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x80, 0x0f, 0xc0, 0x03, 0xe0, 0x01, 0xe0, 0x00, 0xe0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x01, 0xc0, 0x03, + 0x80, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xf0, 0x01, 0xc0, 0x03, 0x80, 0x07, + 0x00, 0x07, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, + 0x80, 0x07, 0xc0, 0x03, 0xf0, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x38, 0x3c, 0x3c, 0x78, 0x1e, + 0xf0, 0x0f, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xf0, 0x0f, + 0x78, 0x1e, 0x3c, 0x3c, 0x1c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x3f, 0xfc, 0x3f, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x30, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, + 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, + 0x1e, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x1f, + 0x3c, 0x3c, 0x1e, 0x78, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x8f, 0xf1, 0xcf, 0xf3, 0xcf, 0xf3, 0x8f, 0xf1, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, 0xf8, 0x1f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x03, 0xc0, 0x03, 0xe0, 0x03, 0xf0, 0x03, 0xf8, 0x03, 0xfc, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xfc, 0x0f, 0x0e, 0x1c, 0x07, 0x3c, + 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0f, 0x30, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x1f, 0x3e, 0x0f, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x0f, 0x3c, 0x1f, 0x3e, 0xfe, 0x1f, 0xfc, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x0f, 0x80, 0x0f, 0xc0, 0x0f, 0xe0, 0x0f, 0xf0, 0x0f, + 0x78, 0x0f, 0x3c, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0xc0, 0x3f, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x0f, 0xff, 0x1f, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf8, 0x03, + 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0xff, 0x0f, 0xff, 0x1f, 0x1f, 0x3e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1f, 0x3e, 0xfe, 0x1f, 0xfc, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, + 0xe0, 0x01, 0xe0, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x1f, 0x3e, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, 0xfc, 0x0f, 0xfc, 0x0f, + 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x1f, 0x3e, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0xfe, 0x3f, 0xfc, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0xfc, 0x07, 0xfc, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 0x00, + 0xe0, 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1e, 0x00, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x78, 0x00, + 0xf0, 0x00, 0xe0, 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1e, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, + 0xe0, 0x01, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3e, 0x00, 0x1f, 0x80, 0x0f, + 0xc0, 0x07, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x8f, 0x3f, 0xcf, 0x3f, 0xcf, 0x3f, 0xcf, 0x3f, + 0xcf, 0x3f, 0xcf, 0x3f, 0xcf, 0x1f, 0x8f, 0x0f, 0x0f, 0x00, 0x0f, 0x00, + 0xfe, 0x0f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, 0xf8, 0x07, + 0x3c, 0x0f, 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0xff, 0x0f, + 0x3c, 0x1c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1c, + 0xfc, 0x0f, 0xfc, 0x0f, 0x3c, 0x1c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1c, 0xff, 0x0f, 0xff, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xf0, 0x0f, 0x78, 0x1e, 0x3c, 0x3c, 0x1e, 0x38, 0x1e, 0x30, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x1e, 0x30, 0x1e, 0x38, 0x3c, 0x3c, 0x78, 0x1e, + 0xf0, 0x0f, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x03, 0xbc, 0x07, 0x3c, 0x0f, + 0x3c, 0x1e, 0x3c, 0x1c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1c, 0x3c, 0x1e, + 0x3c, 0x0f, 0xbc, 0x07, 0xff, 0x03, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, + 0x3c, 0x3c, 0x3c, 0x38, 0x3c, 0x30, 0x3c, 0x20, 0x3c, 0x03, 0x3c, 0x03, + 0xfc, 0x03, 0xfc, 0x03, 0x3c, 0x03, 0x3c, 0x03, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x20, 0x3c, 0x30, 0x3c, 0x38, 0x3c, 0x3c, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x3c, 0x3c, 0x3c, 0x38, 0x3c, 0x30, 0x3c, 0x20, + 0x3c, 0x03, 0x3c, 0x03, 0xfc, 0x03, 0xfc, 0x03, 0x3c, 0x03, 0x3c, 0x03, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0x78, 0x1c, 0x3c, 0x38, + 0x1e, 0x30, 0x0e, 0x30, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0xcf, 0x3f, 0xcf, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x0e, 0x3c, 0x1e, 0x3c, + 0x3c, 0x3c, 0x78, 0x3e, 0xf0, 0x37, 0xe0, 0x33, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x0f, 0xf0, 0x0f, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0xc0, 0x3f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0xfe, 0x07, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3c, 0x3f, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1e, 0x3c, 0x0f, + 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x03, 0xfc, 0x07, 0x3c, 0x0f, 0x3c, 0x1e, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3f, 0x3c, 0x3f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x20, 0x3c, 0x30, 0x3c, 0x38, 0x3c, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xf3, 0x8f, 0xf1, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x0f, 0x3c, + 0x1f, 0x3c, 0x3f, 0x3c, 0x7f, 0x3c, 0xff, 0x3c, 0xff, 0x3f, 0xff, 0x3f, + 0xcf, 0x3f, 0x8f, 0x3f, 0x0f, 0x3f, 0x0f, 0x3e, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x07, 0xfc, 0x0f, 0x1e, 0x1e, 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, 0x1e, 0x1e, + 0xfc, 0x0f, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0xff, 0x0f, 0x3c, 0x1e, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1e, 0xfc, 0x0f, 0xfc, 0x07, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0xcf, 0x3c, 0xcf, 0x3c, 0xcf, 0x3f, 0xcf, 0x3f, 0xfe, 0x1f, 0xfc, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x07, 0xff, 0x0f, 0x3c, 0x1c, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x38, + 0x3c, 0x3c, 0x3c, 0x1c, 0xfc, 0x0f, 0xfc, 0x07, 0x3c, 0x07, 0x3c, 0x0e, + 0x3c, 0x1c, 0x3c, 0x1c, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x38, + 0x3f, 0x38, 0x3f, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x00, 0x3c, 0x00, 0xf8, 0x00, 0xf0, 0x03, + 0xc0, 0x0f, 0x00, 0x1f, 0x00, 0x3c, 0x00, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x1f, 0x3e, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xcf, 0xf3, 0xc7, 0xe3, 0xc3, 0xc3, 0xc1, 0x83, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, + 0xfc, 0x0f, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, + 0x78, 0x1e, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x8f, 0xf1, 0x8f, 0xf1, 0xcf, 0xf3, 0xcf, 0xf3, + 0xef, 0xf7, 0xef, 0xf7, 0x7e, 0x7e, 0x7e, 0x7e, 0x3c, 0x3c, 0x3c, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, + 0x78, 0x1e, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x07, + 0xf0, 0x0f, 0x78, 0x1e, 0x3c, 0x3c, 0x1e, 0x78, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, 0x78, 0x1e, 0xf0, 0x0f, 0xe0, 0x07, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0xf0, 0x07, 0x78, 0x03, 0x3c, 0x01, 0x1e, 0x00, 0x0f, 0x80, 0x07, + 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x1e, 0x00, + 0x0f, 0x80, 0x0f, 0xc0, 0x0f, 0xe0, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0xfc, 0x00, 0xfc, 0x00, + 0xf0, 0x03, 0xf0, 0x03, 0xc0, 0x0f, 0xc0, 0x0f, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, + 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x03, 0x80, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x01, 0xfc, 0x03, 0x00, 0x07, 0x00, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f, + 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x9e, 0x0f, + 0xfc, 0x3c, 0x78, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0xfc, 0x01, 0xfc, 0x03, 0x3c, 0x07, 0x3c, 0x0e, + 0x3c, 0x1c, 0x3c, 0x1c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x1c, 0x3c, 0x1c, 0xfc, 0x0f, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xfc, 0x0f, + 0x1e, 0x1c, 0x0f, 0x3c, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x38, 0x1e, 0x1c, 0xfc, 0x0f, 0xf8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x0f, 0xc0, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0xf0, 0x0f, 0xf8, 0x0f, 0x3c, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x1f, 0x0f, + 0xfe, 0x3d, 0xfc, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x38, 0x1f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xf0, 0x03, + 0x38, 0x07, 0x3c, 0x0e, 0x3c, 0x0c, 0x3c, 0x0c, 0x3c, 0x00, 0x3c, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x38, 0xfc, 0x3d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0f, + 0xfc, 0x0f, 0xf8, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x07, 0x0f, 0x0e, 0x0f, + 0xfc, 0x03, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x0f, 0xbc, 0x1f, 0xfc, 0x3c, 0x7c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3f, 0x3c, 0x3f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x38, 0x1c, + 0xf0, 0x0f, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x38, 0x3c, 0x1c, 0x3c, 0x0e, 0x3c, 0x07, + 0xbc, 0x03, 0xfc, 0x01, 0xfc, 0x01, 0xfc, 0x03, 0x3c, 0x07, 0x3c, 0x0e, + 0x3c, 0x1c, 0x3c, 0x38, 0x3f, 0x38, 0x3f, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x3c, 0x7f, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xf3, 0xcf, 0xf3, + 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, + 0xcf, 0xf3, 0xcf, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc7, 0x07, 0xef, 0x0f, 0x3c, 0x1c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xfc, 0x0f, + 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, 0xfc, 0x0f, 0xf8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc7, 0x07, 0xef, 0x0f, 0x7c, 0x1e, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1e, + 0xfc, 0x0f, 0xfc, 0x07, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x38, 0xfc, 0x3d, 0x1e, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x1e, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0xc0, 0x3f, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x07, 0xcf, 0x0f, + 0xfc, 0x1c, 0x7c, 0x38, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, 0x3e, 0x00, 0xfc, 0x00, + 0xf0, 0x03, 0xc0, 0x07, 0x00, 0x0f, 0x00, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, + 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x00, 0xe0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xff, 0x0f, 0xff, 0x0f, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x38, 0xe0, 0x3c, 0xc0, 0x0f, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x8f, 0x0f, 0xfe, 0x3d, 0xfc, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, 0x78, 0x1e, 0xf0, 0x0f, + 0xe0, 0x07, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, + 0xff, 0xff, 0xff, 0xff, 0x7e, 0x7e, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, + 0x3c, 0x3c, 0x78, 0x1e, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0xe0, 0x07, + 0xf0, 0x0f, 0x78, 0x1e, 0x3c, 0x3c, 0x1e, 0x78, 0x0f, 0xf0, 0x07, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0e, 0x3c, + 0xfc, 0x3f, 0xf8, 0x3f, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x07, + 0xff, 0x03, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x1f, 0x07, 0x0e, 0x03, 0x07, + 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x30, 0x07, 0x38, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3f, + 0x80, 0x03, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xfc, 0x00, 0xfc, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xfc, 0x00, 0xc0, 0x01, 0x80, 0x03, + 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x00, 0x3f, 0x00, 0x3f, + 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, + 0xc0, 0x03, 0xc0, 0x01, 0xfc, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x38, 0xfc, 0x3c, + 0xce, 0x0f, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x07, 0x38, + 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0x38, 0x1c, 0x1c, 0x38, + 0x0e, 0x30, 0x0f, 0x30, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x30, 0x0e, 0x30, 0x1c, 0x38, 0x38, 0x1c, + 0xf0, 0x0f, 0xe0, 0x0f, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x1c, + 0xfc, 0x0f, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x8f, 0x0f, 0xfe, 0x3d, 0xfc, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x07, + 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x38, 0x1f, 0x3c, + 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0xfc, 0x03, 0x00, 0x07, 0x00, 0x0f, + 0xf8, 0x0f, 0xfc, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x9e, 0x0f, 0xfc, 0x3c, 0x78, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0xfc, 0x03, + 0x00, 0x07, 0x00, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x9e, 0x0f, 0xfc, 0x3c, 0x78, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, + 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x01, 0xfc, 0x03, 0x00, 0x07, 0x00, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f, + 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x9e, 0x0f, + 0xfc, 0x3c, 0x78, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x1c, 0x0e, 0x1c, 0x0e, 0xf0, 0x03, 0xe0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0xfc, 0x03, 0x00, 0x07, 0x00, 0x0f, + 0xf8, 0x0f, 0xfc, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x9e, 0x0f, 0xfc, 0x3c, 0x78, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0x38, 0x1c, 0x3c, 0x38, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x38, 0x38, 0x1c, + 0xf0, 0x0f, 0xe0, 0x0f, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x1c, + 0xf0, 0x0f, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x38, 0x1f, 0x3c, + 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x38, 0x1f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, + 0xc0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x38, 0x1f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x03, 0xf0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0x70, 0x0e, 0x38, 0x1c, 0x1c, 0x38, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, + 0xc0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, + 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xf0, 0x03, + 0x1c, 0x0e, 0x1c, 0x0e, 0xf0, 0x03, 0xe0, 0x01, 0x00, 0x00, 0xc0, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x3c, 0x38, 0x3c, 0x30, + 0x3c, 0x00, 0x3c, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x30, 0x3c, 0x38, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x1e, 0x38, 0x3f, 0xf0, 0x73, 0xe0, 0xf3, 0xc0, 0xf3, 0xc0, 0x73, + 0xf8, 0x3f, 0xfc, 0x1f, 0xce, 0x03, 0xc7, 0x03, 0xc7, 0x07, 0xce, 0x1e, + 0xfc, 0xfc, 0x78, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, 0xf0, 0x3f, 0x38, 0x0f, 0x1c, 0x0f, + 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0x3f, 0xff, 0x3f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x3f, 0x0f, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, + 0x1c, 0x0e, 0x0e, 0x1c, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xfc, 0x0f, + 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, 0xfc, 0x0f, 0xf8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x07, 0xfc, 0x0f, 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, + 0xfc, 0x0f, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xfc, 0x0f, 0x1e, 0x1e, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x1e, 0x1e, 0xfc, 0x0f, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xf0, 0x00, 0xf8, 0x01, 0x9c, 0x03, + 0x0e, 0x07, 0x07, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x8f, 0x0f, 0xfe, 0x3d, 0xfc, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, + 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x8f, 0x0f, + 0xfe, 0x3d, 0xfc, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0e, 0x3c, 0xfc, 0x3f, 0xf8, 0x3f, 0x00, 0x3c, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x07, 0xfc, 0x0f, 0x0e, 0x1c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0e, 0x1c, 0xfc, 0x0f, 0xf8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0e, 0x1c, + 0xfc, 0x0f, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf8, 0x1f, 0xfc, 0x3f, + 0x0e, 0x70, 0x07, 0xe0, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, + 0x07, 0x00, 0x07, 0x00, 0x07, 0xe0, 0x0e, 0x70, 0xfc, 0x3f, 0xf8, 0x1f, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x3c, 0x0e, + 0x3c, 0x0c, 0x3c, 0x0c, 0x3c, 0x00, 0x3c, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3f, 0x38, 0x3f, 0x1c, 0xff, 0x0f, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xe0, 0x0e, 0x70, 0x1c, 0x38, 0x38, 0x1c, 0x70, 0x0e, 0xe0, 0x07, + 0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x0f, 0xff, 0x1f, 0x3c, 0x3c, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x3c, + 0xfc, 0x1f, 0xfc, 0x0f, 0x3c, 0x30, 0x3c, 0x38, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0xff, 0x3c, 0xff, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0xff, 0xf8, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x3f, 0x80, 0x77, 0xc0, 0xe3, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xfc, 0x3f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc7, 0x03, 0xee, 0x01, 0xfc, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0xc0, 0x01, + 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x01, 0xfc, 0x03, 0x00, 0x07, 0x00, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f, + 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x9e, 0x0f, + 0xfc, 0x3c, 0x78, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, + 0x38, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xfc, 0x0f, + 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, 0xfc, 0x0f, 0xf8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0xc0, 0x01, + 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x8f, 0x0f, + 0xfe, 0x3d, 0xfc, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x38, 0xfc, 0x3c, 0xcf, 0x0f, 0x87, 0x07, + 0x00, 0x00, 0x00, 0x00, 0xc7, 0x07, 0xef, 0x0f, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x38, 0xfc, 0x3c, 0xcf, 0x0f, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x3c, 0x0f, 0x3c, 0x1f, 0x3c, 0x3f, 0x3c, 0x7f, 0x3c, 0xff, 0x3c, + 0xff, 0x3d, 0xef, 0x3f, 0xcf, 0x3f, 0x8f, 0x3f, 0x0f, 0x3f, 0x0f, 0x3e, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x0f, 0xf0, 0x0f, + 0x38, 0x0f, 0x1c, 0x0f, 0x1c, 0x0f, 0x38, 0x0f, 0xf0, 0x3f, 0xe0, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x1c, 0x0e, 0x38, 0x07, + 0xf0, 0x03, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x00, 0x07, 0x00, + 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x0f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x20, 0x0f, 0x30, 0x0f, 0x38, 0x0f, 0x1c, 0x0f, 0x0e, 0x0f, 0x07, + 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x3e, 0x07, 0x7f, 0x83, 0xf3, 0xc1, 0xf1, 0x00, 0x38, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x07, 0x80, 0xff, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x20, 0x0f, 0x30, 0x0f, 0x38, 0x0f, 0x1c, + 0x0f, 0x0e, 0x0f, 0x07, 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, + 0x38, 0x3c, 0x1c, 0x3e, 0x0e, 0x3f, 0x87, 0x3d, 0xc3, 0x3c, 0x61, 0x3c, + 0xf0, 0x3f, 0xf0, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xe0, 0x07, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3c, 0x78, 0x1e, + 0x3c, 0x0f, 0x9e, 0x07, 0xcf, 0x03, 0xcf, 0x03, 0x9e, 0x07, 0x3c, 0x0f, + 0x78, 0x1e, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcf, 0x03, 0x9e, 0x07, 0x3c, 0x0f, 0x78, 0x1e, 0xf0, 0x3c, 0xf0, 0x3c, + 0x78, 0x1e, 0x3c, 0x0f, 0x9e, 0x07, 0xcf, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, + 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c, + 0xc0, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0, 0xc0, + 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c, + 0xc0, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0, 0xc0, + 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c, + 0xcc, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, + 0x33, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0x33, 0x33, + 0xcc, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, + 0x33, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0x33, 0x33, + 0xcc, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, + 0x33, 0x33, 0x33, 0x33, 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, + 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, 0xcf, 0xcf, 0xcf, 0xcf, + 0xfc, 0xfc, 0xfc, 0xfc, 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, + 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, 0xcf, 0xcf, 0xcf, 0xcf, + 0xfc, 0xfc, 0xfc, 0xfc, 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, + 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xff, 0x03, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xff, 0x3c, 0xff, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x03, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0x3c, 0xff, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0xff, 0x3c, 0xff, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0xff, 0x3c, 0xff, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0x3c, 0xff, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xff, 0x03, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x03, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0xfc, 0xf0, 0xfc, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0xfc, 0xf0, 0xfc, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0xff, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xf0, 0xff, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0xfc, 0xf0, 0xfc, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xff, 0xfc, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0xfc, 0xf0, 0xfc, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0xfc, 0xf0, 0xfc, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0xff, 0xf0, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0xff, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xc0, 0xff, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xf0, 0xff, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, + 0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x3c, 0xfe, 0x3e, 0xcf, 0x1f, 0x87, 0x0f, + 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, + 0x87, 0x0f, 0xcf, 0x1f, 0xfe, 0x3d, 0xfc, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xfc, 0x03, + 0x0e, 0x07, 0x0e, 0x07, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x07, 0x8f, 0x07, + 0xcf, 0x03, 0xcf, 0x03, 0x8f, 0x07, 0x0f, 0x0f, 0x0f, 0x0e, 0x0f, 0x1c, + 0x0f, 0x1c, 0x0f, 0x1c, 0x0f, 0x1c, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x38, 0x1e, 0x30, 0x3c, 0x00, 0x78, 0x00, + 0xf0, 0x00, 0xe0, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, + 0x78, 0x00, 0x3c, 0x00, 0x1e, 0x30, 0x0f, 0x38, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x3f, 0xfc, 0x3f, 0xce, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, + 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0xce, 0x01, + 0xfc, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1c, 0x3c, 0x1e, 0xfc, 0x0f, 0xfc, 0x07, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x38, 0xfc, 0x3c, 0xcf, 0x0f, 0xc7, 0x07, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, + 0xe0, 0x07, 0xf0, 0x0f, 0x38, 0x1c, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, + 0x1c, 0x38, 0x38, 0x1c, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03, + 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xf0, 0x03, + 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x0e, 0x1c, 0x07, 0x38, 0x07, 0x38, + 0xff, 0x3f, 0xff, 0x3f, 0x07, 0x38, 0x07, 0x38, 0x0e, 0x1c, 0x0e, 0x1c, + 0x1c, 0x0e, 0x38, 0x07, 0xf0, 0x03, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf8, 0x07, + 0x3c, 0x0f, 0x1e, 0x1e, 0x0f, 0x3c, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, + 0x07, 0x38, 0x07, 0x38, 0x0e, 0x1c, 0x1e, 0x1e, 0x1c, 0x0e, 0x3c, 0x0f, + 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x3f, 0x3f, 0x3f, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0xc0, 0x3f, 0xe0, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x03, + 0x00, 0x07, 0x00, 0x0f, 0xe0, 0x1f, 0xf0, 0x3f, 0x38, 0x3c, 0x1c, 0x38, + 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x38, 0x1c, + 0xf0, 0x0f, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0xfc, 0x3f, 0xce, 0xf3, 0xcf, 0xf3, + 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xce, 0xf3, 0xfc, 0x3f, 0xf8, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0xf8, 0x1f, 0xfc, 0x3f, + 0x0e, 0x77, 0x8f, 0xf3, 0xcf, 0xf3, 0xef, 0xf1, 0xff, 0xf0, 0xff, 0x70, + 0xfc, 0x3f, 0xfc, 0x1f, 0x1c, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0xc0, 0x0f, 0xe0, 0x01, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 0x00, 0xe0, 0x01, + 0xc0, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x3f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xf0, 0x00, + 0xe0, 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1e, 0x00, 0x1e, + 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, + 0x78, 0x00, 0x78, 0x00, 0xf0, 0x00, 0xe0, 0x01, 0xc0, 0x03, 0x80, 0x07, + 0x00, 0x0f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x80, 0x7f, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xcf, 0x03, 0xcf, 0x03, 0xcf, 0x03, 0xcf, 0x03, + 0xcf, 0x03, 0xcf, 0x03, 0xfe, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x38, 0xfc, 0x3c, 0xcf, 0x0f, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x38, 0xfc, 0x3c, 0xcf, 0x0f, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x1c, 0x0e, 0x38, 0x07, + 0xf0, 0x03, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x07, 0x0f, 0x0f, 0x0f, 0x1e, 0x0f, 0x3c, 0x0f, 0x78, 0x0f, 0xf0, 0x0f, + 0xe0, 0x0f, 0xc0, 0x0f, 0x80, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xcf, 0x03, 0xef, 0x07, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x3c, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xfc, 0x00, + 0xce, 0x01, 0xc7, 0x03, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x03, 0x07, 0x03, 0xff, 0x03, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, + 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, + 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static const txt_font_t large_font = +{ + "large", + large_font_data, + 16, + 32, +}; diff --git a/games/NXDoom/textscreen/fonts/large.png b/games/NXDoom/textscreen/fonts/large.png new file mode 100644 index 00000000000..3827ab599f1 Binary files /dev/null and b/games/NXDoom/textscreen/fonts/large.png differ diff --git a/games/NXDoom/textscreen/fonts/normal.h b/games/NXDoom/textscreen/fonts/normal.h new file mode 100644 index 00000000000..4ecaf378787 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/normal.h @@ -0,0 +1,367 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/fonts/normal.h + * + * SPDX-License-Identifer: GPLv2 + * + * Font data generated from normal.png; do not edit. Please see + * textscreen/fonts/README for copyright information. + * + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const uint8_t normal_font_data[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, + 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, + 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x7f, 0x7f, 0x7f, 0x7f, 0x3e, 0x1c, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x3e, 0x7f, + 0x3e, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, + 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, + 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x78, 0x70, + 0x58, 0x4c, 0x1e, 0x33, 0x33, 0x33, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xcc, 0xfc, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0e, 0x0f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xe7, 0x67, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x7f, 0x1f, + 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x70, + 0x78, 0x7c, 0x7f, 0x7c, 0x78, 0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xdb, + 0xdb, 0xdb, 0xde, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x63, 0x06, 0x1c, 0x36, 0x63, 0x63, 0x36, 0x1c, 0x30, 0x63, + 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, + 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x7f, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x7f, 0x06, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, + 0x03, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x1c, 0x3e, 0x3e, 0x7f, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x3e, 0x3e, + 0x1c, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x36, 0x7f, 0x36, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x3e, 0x63, 0x43, 0x03, 0x3e, 0x60, 0x60, 0x61, 0x63, 0x3e, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x30, 0x18, + 0x0c, 0x06, 0x63, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, + 0x36, 0x1c, 0x6e, 0x3b, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x0c, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x1e, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, + 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x63, 0x60, 0x60, 0x3c, 0x60, 0x60, 0x60, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x38, 0x3c, 0x36, 0x33, 0x7f, + 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x03, + 0x03, 0x03, 0x3f, 0x60, 0x60, 0x60, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x06, 0x03, 0x03, 0x3f, 0x63, 0x63, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x60, 0x60, 0x30, 0x18, + 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, + 0x63, 0x63, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x7e, 0x60, 0x60, 0x60, 0x30, 0x1e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x63, 0x63, 0x30, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x7b, 0x7b, + 0x7b, 0x3b, 0x03, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, + 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x66, 0x66, 0x66, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x43, 0x03, 0x03, 0x03, + 0x03, 0x43, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x36, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x36, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x66, 0x46, 0x16, 0x1e, 0x16, 0x06, 0x46, 0x66, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x66, 0x46, 0x16, 0x1e, 0x16, + 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, + 0x43, 0x03, 0x03, 0x7b, 0x63, 0x63, 0x66, 0x5c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x33, 0x33, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x67, 0x66, 0x66, 0x36, 0x1e, 0x1e, 0x36, 0x66, 0x66, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x46, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7, + 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x67, 0x6f, 0x7f, 0x7b, 0x73, 0x63, 0x63, 0x63, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x66, + 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x6b, 0x7b, 0x3e, + 0x30, 0x70, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x66, 0x66, 0x66, 0x3e, 0x36, + 0x66, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, + 0x63, 0x06, 0x1c, 0x30, 0x60, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, + 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, + 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc3, 0x61, 0x30, 0x18, 0x0c, 0x06, 0x83, 0xc3, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x03, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x3e, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, + 0x06, 0x1e, 0x36, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x03, 0x03, 0x03, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x30, 0x30, 0x3c, 0x36, 0x33, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x26, 0x06, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x3e, 0x30, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x07, 0x06, + 0x06, 0x36, 0x6e, 0x66, 0x66, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x70, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x07, 0x06, + 0x06, 0x66, 0x36, 0x1e, 0x1e, 0x36, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0xff, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6e, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x6e, 0x66, 0x06, 0x06, 0x06, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x06, + 0x1c, 0x30, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0c, + 0x0c, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, + 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x7e, 0x60, 0x30, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x33, 0x18, 0x0c, 0x06, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, + 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6e, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, + 0x63, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, + 0x43, 0x03, 0x03, 0x03, 0x43, 0x66, 0x3c, 0x30, 0x60, 0x3e, 0x00, 0x00, + 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x00, 0x3e, 0x63, 0x7f, + 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, + 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x33, 0x00, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x00, 0x1e, 0x30, 0x3e, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x1c, + 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x06, 0x06, 0x66, 0x3c, 0x30, 0x60, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x00, 0x3e, 0x63, 0x7f, + 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, + 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x0c, 0x18, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x1c, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, + 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x0c, 0x18, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x63, + 0x7f, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x1c, 0x00, + 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x0c, 0x06, 0x00, 0x7f, 0x66, 0x06, 0x3e, 0x06, 0x06, 0x66, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, + 0x7e, 0x1b, 0x3b, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x36, + 0x33, 0x33, 0x7f, 0x33, 0x33, 0x33, 0x33, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x1c, 0x36, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x3e, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, + 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x1e, 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x00, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, + 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x7e, 0x60, 0x30, 0x1e, 0x00, + 0x00, 0x63, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, + 0xc3, 0x03, 0x03, 0x03, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x36, 0x26, 0x06, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x67, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, + 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x66, 0x66, + 0x3e, 0x46, 0x66, 0xf6, 0x66, 0x66, 0x66, 0xcf, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x06, 0x00, 0x1e, 0x30, 0x3e, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, + 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x0c, 0x06, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x06, 0x00, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, + 0x00, 0x3b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x6e, 0x3b, 0x00, 0x63, 0x67, 0x6f, 0x7f, 0x7b, 0x73, 0x63, 0x63, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x36, 0x7c, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x36, + 0x1c, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x0c, 0x0c, 0x06, 0x03, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x03, + 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x03, 0x43, 0x63, 0x33, 0x18, 0x0c, 0x06, 0x73, 0xd9, 0x60, + 0x30, 0xf8, 0x00, 0x00, 0x00, 0x03, 0x03, 0x43, 0x63, 0x33, 0x18, 0x0c, + 0x66, 0x73, 0x69, 0x7c, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x36, 0x1b, 0x36, 0x6c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x36, 0x6c, + 0x36, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x22, 0x88, 0x22, + 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, + 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, + 0xaa, 0x55, 0xaa, 0x55, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, + 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x6f, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6f, 0x60, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0xec, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0xef, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6e, 0x3b, 0x1b, 0x1b, 0x1b, 0x3b, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x33, 0x33, 0x33, 0x1b, 0x33, 0x63, 0x63, 0x63, 0x33, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x63, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7f, 0x63, 0x06, 0x0c, 0x18, 0x0c, 0x06, 0x63, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, + 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x63, 0x36, 0x36, 0x36, 0x36, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x18, 0x30, 0x7c, 0x66, + 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x60, 0x7e, 0xdb, 0xdb, 0xcf, 0x7e, 0x06, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x06, 0x06, 0x3e, 0x06, + 0x06, 0x06, 0x0c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, + 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0xd8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1b, 0x1b, 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x00, + 0x6e, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x36, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x37, 0x36, 0x36, 0x3c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1b, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x0c, 0x06, 0x13, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static const txt_font_t normal_font = +{ + "normal", + normal_font_data, + 8, + 16, +}; diff --git a/games/NXDoom/textscreen/fonts/normal.png b/games/NXDoom/textscreen/fonts/normal.png new file mode 100644 index 00000000000..884bb98d419 Binary files /dev/null and b/games/NXDoom/textscreen/fonts/normal.png differ diff --git a/games/NXDoom/textscreen/fonts/small.h b/games/NXDoom/textscreen/fonts/small.h new file mode 100644 index 00000000000..b7dd28d2214 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/small.h @@ -0,0 +1,111 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/fonts/small.h + * + * SPDX-License-Identifer: GPLv2 + * + * Font data generated from small.png; do not edit. Please see + * textscreen/fonts/README for copyright information. + * + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const uint8_t small_font_data[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x4e, 0x00, 0xf6, 0xf5, 0xb1, 0x6f, + 0x00, 0xea, 0x04, 0x00, 0x00, 0xe4, 0x04, 0x00, 0x40, 0xaa, 0xe4, 0x00, + 0x40, 0xee, 0xe4, 0x00, 0x00, 0x60, 0x06, 0x00, 0xff, 0x9f, 0xf9, 0xff, + 0x00, 0x96, 0x69, 0x00, 0xff, 0x69, 0x96, 0xff, 0xe0, 0x74, 0x75, 0x00, + 0x60, 0x69, 0x6f, 0x06, 0xc0, 0x44, 0x74, 0x03, 0xe0, 0xaa, 0xba, 0x03, + 0x22, 0x27, 0x27, 0x22, 0x10, 0x73, 0x13, 0x00, 0x80, 0xec, 0x8c, 0x00, + 0x40, 0x4e, 0xe4, 0x04, 0xa0, 0xaa, 0xa0, 0x00, 0xf0, 0xb9, 0xaa, 0x00, + 0x16, 0x96, 0x86, 0x06, 0x00, 0x00, 0xff, 0x00, 0x40, 0x4e, 0xe4, 0xf4, + 0x40, 0x4e, 0x44, 0x04, 0x40, 0x44, 0xe4, 0x04, 0x20, 0xf4, 0x24, 0x00, + 0x40, 0xf2, 0x42, 0x00, 0x00, 0x10, 0x07, 0x00, 0x00, 0xf5, 0x05, 0x00, + 0x00, 0x72, 0x0f, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x22, 0x02, 0x02, 0x50, 0x05, 0x00, 0x00, 0x50, 0x57, 0x75, 0x05, + 0x22, 0x25, 0x54, 0x22, 0x50, 0x24, 0x12, 0x05, 0x52, 0x52, 0x35, 0x06, + 0x20, 0x02, 0x00, 0x00, 0x40, 0x22, 0x22, 0x04, 0x10, 0x22, 0x22, 0x01, + 0x50, 0x72, 0x52, 0x00, 0x20, 0x72, 0x22, 0x00, 0x00, 0x00, 0x20, 0x12, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x20, 0x02, 0x40, 0x24, 0x12, 0x01, + 0x20, 0x75, 0x55, 0x02, 0x20, 0x23, 0x22, 0x07, 0x20, 0x45, 0x12, 0x07, + 0x70, 0x24, 0x54, 0x02, 0x40, 0x56, 0x47, 0x04, 0x70, 0x31, 0x54, 0x02, + 0x60, 0x31, 0x55, 0x02, 0x70, 0x44, 0x22, 0x02, 0x20, 0x25, 0x55, 0x02, + 0x20, 0x55, 0x46, 0x03, 0x00, 0x02, 0x20, 0x00, 0x00, 0x02, 0x20, 0x01, + 0x00, 0x24, 0x21, 0x04, 0x00, 0x07, 0x07, 0x00, 0x00, 0x21, 0x24, 0x01, + 0x20, 0x45, 0x02, 0x02, 0x20, 0x55, 0x11, 0x06, 0x20, 0x55, 0x57, 0x05, + 0x30, 0x35, 0x55, 0x03, 0x20, 0x15, 0x51, 0x02, 0x30, 0x55, 0x55, 0x03, + 0x70, 0x71, 0x11, 0x07, 0x70, 0x71, 0x11, 0x01, 0x20, 0x15, 0x55, 0x02, + 0x50, 0x75, 0x55, 0x05, 0x70, 0x22, 0x22, 0x07, 0x40, 0x44, 0x54, 0x02, + 0x50, 0x35, 0x55, 0x05, 0x10, 0x11, 0x11, 0x07, 0x50, 0x57, 0x55, 0x05, + 0x40, 0x75, 0x57, 0x01, 0x20, 0x55, 0x55, 0x02, 0x30, 0x55, 0x13, 0x01, + 0x20, 0x55, 0x35, 0x06, 0x30, 0x55, 0x53, 0x05, 0x20, 0x25, 0x54, 0x02, + 0x70, 0x22, 0x22, 0x02, 0x50, 0x55, 0x55, 0x07, 0x50, 0x55, 0x55, 0x02, + 0x50, 0x55, 0x75, 0x05, 0x50, 0x25, 0x55, 0x05, 0x50, 0x25, 0x22, 0x02, + 0x70, 0x24, 0x12, 0x07, 0x60, 0x22, 0x22, 0x06, 0x10, 0x21, 0x42, 0x04, + 0x30, 0x22, 0x22, 0x03, 0x20, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, + 0x10, 0x02, 0x00, 0x00, 0x00, 0x43, 0x56, 0x06, 0x10, 0x31, 0x55, 0x03, + 0x00, 0x16, 0x11, 0x06, 0x40, 0x64, 0x55, 0x06, 0x00, 0x52, 0x17, 0x06, + 0x40, 0x72, 0x22, 0x02, 0x00, 0x56, 0x65, 0x34, 0x10, 0x31, 0x55, 0x05, + 0x20, 0x30, 0x22, 0x07, 0x40, 0x60, 0x44, 0x34, 0x10, 0x51, 0x53, 0x05, + 0x20, 0x22, 0x22, 0x02, 0x00, 0x75, 0x55, 0x05, 0x00, 0x53, 0x55, 0x05, + 0x00, 0x52, 0x55, 0x02, 0x00, 0x53, 0x35, 0x11, 0x00, 0x56, 0x65, 0x44, + 0x00, 0x16, 0x11, 0x01, 0x00, 0x16, 0x42, 0x03, 0x20, 0x72, 0x22, 0x02, + 0x00, 0x55, 0x55, 0x07, 0x00, 0x55, 0x55, 0x02, 0x00, 0x55, 0x75, 0x05, + 0x00, 0x55, 0x52, 0x05, 0x00, 0x55, 0x65, 0x34, 0x00, 0x47, 0x12, 0x07, + 0x24, 0x12, 0x22, 0x04, 0x22, 0x02, 0x22, 0x02, 0x21, 0x42, 0x22, 0x01, + 0xa0, 0x05, 0x00, 0x00, 0x20, 0x55, 0x75, 0x00, 0x20, 0x15, 0x51, 0x32, + 0x05, 0x55, 0x55, 0x07, 0x12, 0x52, 0x17, 0x06, 0xa4, 0x43, 0x56, 0x06, + 0x05, 0x43, 0x56, 0x06, 0x42, 0x43, 0x56, 0x06, 0x52, 0x42, 0x56, 0x06, + 0x00, 0x16, 0x11, 0x36, 0x52, 0x52, 0x17, 0x06, 0x05, 0x52, 0x17, 0x06, + 0x42, 0x52, 0x17, 0x06, 0x50, 0x30, 0x22, 0x07, 0x52, 0x30, 0x22, 0x07, + 0x42, 0x30, 0x22, 0x07, 0x05, 0x52, 0x57, 0x05, 0x52, 0x52, 0x57, 0x05, + 0x24, 0x17, 0x13, 0x07, 0x00, 0xad, 0x5e, 0x0e, 0xe0, 0xf5, 0x55, 0x0d, + 0x52, 0x20, 0x55, 0x02, 0x05, 0x52, 0x55, 0x02, 0x42, 0x52, 0x55, 0x02, + 0x52, 0x50, 0x55, 0x07, 0x42, 0x50, 0x55, 0x07, 0x05, 0x55, 0x65, 0x34, + 0x05, 0x52, 0x55, 0x02, 0x05, 0x55, 0x55, 0x07, 0x20, 0x16, 0x61, 0x02, + 0x2c, 0x72, 0x22, 0x0f, 0x50, 0x25, 0x27, 0x02, 0x30, 0x35, 0xd5, 0x05, + 0x48, 0xe4, 0x54, 0x02, 0x24, 0x43, 0x56, 0x06, 0x24, 0x30, 0x22, 0x07, + 0x12, 0x52, 0x55, 0x02, 0x24, 0x50, 0x55, 0x07, 0x5a, 0x30, 0x55, 0x05, + 0x5a, 0x90, 0xdb, 0x09, 0x56, 0x0e, 0x0f, 0x00, 0x52, 0x02, 0x07, 0x00, + 0x20, 0x20, 0x51, 0x02, 0x00, 0x70, 0x11, 0x00, 0x00, 0xe0, 0x88, 0x00, + 0x11, 0x25, 0x8d, 0xc4, 0x11, 0x25, 0xa1, 0x8e, 0x20, 0x20, 0x22, 0x02, + 0x00, 0xa0, 0xa5, 0x00, 0x00, 0x50, 0x5a, 0x00, 0x28, 0x28, 0x28, 0x28, + 0x5a, 0x5a, 0x5a, 0x5a, 0xeb, 0xeb, 0xeb, 0xeb, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x74, 0x44, 0x44, 0x44, 0x47, 0x47, 0x44, 0xaa, 0xba, 0xaa, 0xaa, + 0x00, 0xf0, 0xaa, 0xaa, 0x00, 0x47, 0x47, 0x44, 0xaa, 0x8b, 0xab, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x8f, 0xab, 0xaa, 0xaa, 0x8b, 0x0f, 0x00, + 0xaa, 0xfa, 0x00, 0x00, 0x44, 0x47, 0x07, 0x00, 0x00, 0x70, 0x44, 0x44, + 0x44, 0xc4, 0x00, 0x00, 0x44, 0xf4, 0x00, 0x00, 0x00, 0xf0, 0x44, 0x44, + 0x44, 0xc4, 0x44, 0x44, 0x00, 0xf0, 0x00, 0x00, 0x44, 0xf4, 0x44, 0x44, + 0x44, 0x4c, 0x4c, 0x44, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0x0e, 0x00, + 0x00, 0x2e, 0xaa, 0xaa, 0xaa, 0x0b, 0x0f, 0x00, 0x00, 0x0f, 0xab, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x0f, 0x0f, 0x00, 0xaa, 0x0b, 0xab, 0xaa, + 0x44, 0x0f, 0x0f, 0x00, 0xaa, 0xfa, 0x00, 0x00, 0x00, 0x0f, 0x4f, 0x44, + 0x00, 0xf0, 0xaa, 0xaa, 0xaa, 0xea, 0x00, 0x00, 0x44, 0x4c, 0x0c, 0x00, + 0x00, 0x4c, 0x4c, 0x44, 0x00, 0xe0, 0xaa, 0xaa, 0xaa, 0xfa, 0xaa, 0xaa, + 0x44, 0x4f, 0x4f, 0x44, 0x44, 0x74, 0x00, 0x00, 0x00, 0xc0, 0x44, 0x44, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, + 0xcc, 0xcc, 0xcc, 0xcc, 0xff, 0xff, 0x00, 0x00, 0x00, 0xa0, 0x55, 0x0a, + 0x30, 0x35, 0x95, 0x05, 0xf0, 0x19, 0x11, 0x01, 0x00, 0x5f, 0x55, 0x05, + 0x00, 0x17, 0x12, 0x07, 0x00, 0x5e, 0x55, 0x02, 0x00, 0xaa, 0x2e, 0x01, + 0x00, 0x5a, 0x44, 0x04, 0x70, 0x52, 0x25, 0x07, 0x20, 0x75, 0x55, 0x02, + 0x60, 0x99, 0x66, 0x0f, 0x60, 0x21, 0x55, 0x02, 0x00, 0xd6, 0x6b, 0x00, + 0x80, 0x9f, 0xf9, 0x01, 0x60, 0x71, 0x11, 0x06, 0x20, 0x55, 0x55, 0x05, + 0x70, 0x70, 0x70, 0x00, 0x20, 0x27, 0x70, 0x00, 0x10, 0x42, 0x12, 0x07, + 0x40, 0x12, 0x42, 0x07, 0x80, 0x44, 0x44, 0x44, 0x44, 0x44, 0x25, 0x00, + 0x00, 0x02, 0x07, 0x02, 0xa0, 0x05, 0x5a, 0x00, 0x52, 0x02, 0x00, 0x00, + 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x4c, 0x54, 0x46, 0x00, + 0x57, 0x55, 0x00, 0x00, 0x43, 0x72, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static const txt_font_t small_font = +{ + "small", + small_font_data, + 4, + 8, +}; diff --git a/games/NXDoom/textscreen/fonts/small.png b/games/NXDoom/textscreen/fonts/small.png new file mode 100644 index 00000000000..155797fe630 Binary files /dev/null and b/games/NXDoom/textscreen/fonts/small.png differ diff --git a/games/NXDoom/textscreen/textscreen.h b/games/NXDoom/textscreen/textscreen.h new file mode 100644 index 00000000000..40824da68d4 --- /dev/null +++ b/games/NXDoom/textscreen/textscreen.h @@ -0,0 +1,57 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/textscreen.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TEXTSCREEN_H +#define TEXTSCREEN_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "../src/doomkeys.h" +#include "txt_main.h" + +#include "txt_button.h" +#include "txt_checkbox.h" +#include "txt_conditional.h" +#include "txt_desktop.h" +#include "txt_dropdown.h" +#include "txt_fileselect.h" +#include "txt_inputbox.h" +#include "txt_label.h" +#include "txt_radiobutton.h" +#include "txt_scrollpane.h" +#include "txt_separator.h" +#include "txt_spinctrl.h" +#include "txt_strut.h" +#include "txt_table.h" +#include "txt_widget.h" +#include "txt_window.h" +#include "txt_window_action.h" + +#ifdef __cplusplus +} +#endif + +#endif /* TEXTSCREEN_H */ diff --git a/games/NXDoom/textscreen/txt_button.c b/games/NXDoom/textscreen/txt_button.c new file mode 100644 index 00000000000..0eadb71a4c0 --- /dev/null +++ b/games/NXDoom/textscreen/txt_button.c @@ -0,0 +1,159 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_button.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomkeys.h" + +#include "txt_button.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +static void txt_button_size_calc(TXT_UNCAST_ARG(button)); +static void txt_button_drawer(TXT_UNCAST_ARG(button)); +static void txt_button_destructor(TXT_UNCAST_ARG(button)); +static int txt_button_keypress(TXT_UNCAST_ARG(button), int key); +static void txt_button_mousepress(TXT_UNCAST_ARG(button), int x, int y, + int b); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_button_class = +{ + txt_always_selectable, + txt_button_size_calc, + txt_button_drawer, + txt_button_keypress, + txt_button_destructor, + txt_button_mousepress, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_button_size_calc(TXT_UNCAST_ARG(button)) +{ + TXT_CAST_ARG(txt_button_t, button); + + button->widget.w = txt_utf8_strlen(button->label); + button->widget.h = 1; +} + +static void txt_button_drawer(TXT_UNCAST_ARG(button)) +{ + TXT_CAST_ARG(txt_button_t, button); + int i; + int w; + + w = button->widget.w; + + txt_set_widget_bg(button); + + txt_draw_string(button->label); + + for (i = txt_utf8_strlen(button->label); i < w; ++i) + { + txt_draw_string(" "); + } +} + +static void txt_button_destructor(TXT_UNCAST_ARG(button)) +{ + TXT_CAST_ARG(txt_button_t, button); + + free(button->label); +} + +static int txt_button_keypress(TXT_UNCAST_ARG(button), int key) +{ + TXT_CAST_ARG(txt_button_t, button); + + if (key == KEY_ENTER) + { + txt_emit_signal(button, "pressed"); + return 1; + } + + return 0; +} + +static void txt_button_mousepress(TXT_UNCAST_ARG(button), int x, int y, + int b) +{ + TXT_CAST_ARG(txt_button_t, button); + + if (b == TXT_MOUSE_LEFT) + { + /* Equivalent to pressing enter */ + + txt_button_keypress(button, KEY_ENTER); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_set_button_label(txt_button_t *button, const char *label) +{ + free(button->label); + button->label = strdup(label); +} + +txt_button_t *txt_new_button(const char *label) +{ + txt_button_t *button; + + button = malloc(sizeof(txt_button_t)); + + txt_init_widget(button, &txt_button_class); + button->label = strdup(label); + + return button; +} + +/* Button with a callback set automatically */ + +txt_button_t *txt_new_button2(const char *label, txt_widget_signal_f func, + void *user_data) +{ + txt_button_t *button; + + button = txt_new_button(label); + + txt_signal_connect(button, "pressed", func, user_data); + + return button; +} diff --git a/games/NXDoom/textscreen/txt_button.h b/games/NXDoom/textscreen/txt_button.h new file mode 100644 index 00000000000..1982e894ae8 --- /dev/null +++ b/games/NXDoom/textscreen/txt_button.h @@ -0,0 +1,80 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_button.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_BUTTON_H +#define TXT_BUTTON_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Button widget. + * + * A button is a widget that can be selected to perform some action. + * When a button is pressed, it emits the "pressed" signal. + */ + +struct txt_button_s +{ + txt_widget_t widget; + char *label; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new button widget. + * + * @param label The label to use on the new button (UTF-8 format). + * @return Pointer to the new button widget. + */ + +txt_button_t *txt_new_button(const char *label); + +/** + * Create a new button widget, binding the "pressed" signal to a + * specified callback function. + * + * @param label The label to use on the new button (UTF-8 format). + * @param func The callback function to invoke. + * @param user_data User-specified pointer to pass to the callback. + * @return Pointer to the new button widget. + */ + +txt_button_t *txt_new_button2(const char *label, txt_widget_signal_f func, + void *user_data); + +/** + * Change the label used on a button. + * + * @param button The button. + * @param label The new label (UTF-8 format). + */ + +void txt_set_button_label(txt_button_t *button, const char *label); + +#endif /* TXT_BUTTON_H */ diff --git a/games/NXDoom/textscreen/txt_checkbox.c b/games/NXDoom/textscreen/txt_checkbox.c new file mode 100644 index 00000000000..cd467ff3b74 --- /dev/null +++ b/games/NXDoom/textscreen/txt_checkbox.c @@ -0,0 +1,174 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_checkbox.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomkeys.h" + +#include "txt_checkbox.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_checkbox_size_calc(TXT_UNCAST_ARG(checkbox)); +static void txt_checkbox_drawer(TXT_UNCAST_ARG(checkbox)); +static void txt_checkbox_destructor(TXT_UNCAST_ARG(checkbox)); +static int txt_check_box_keypress(TXT_UNCAST_ARG(checkbox), int key); +static void txt_checkbox_mousepress(TXT_UNCAST_ARG(checkbox), int x, int y, + int b); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_checkbox_class = +{ + txt_always_selectable, + txt_checkbox_size_calc, + txt_checkbox_drawer, + txt_check_box_keypress, + txt_checkbox_destructor, + txt_checkbox_mousepress, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_checkbox_size_calc(TXT_UNCAST_ARG(checkbox)) +{ + TXT_CAST_ARG(txt_checkbox_t, checkbox); + + /* Minimum width is the string length + right-side space for padding */ + + checkbox->widget.w = txt_utf8_strlen(checkbox->label) + 5; + checkbox->widget.h = 1; +} + +static void txt_checkbox_drawer(TXT_UNCAST_ARG(checkbox)) +{ + TXT_CAST_ARG(txt_checkbox_t, checkbox); + txt_saved_colors_t colors; + int i; + int w; + + w = checkbox->widget.w; + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + txt_draw_string("("); + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + + if ((*checkbox->variable != 0) ^ checkbox->inverted) + { + txt_draw_code_page_string("\x07"); + } + else + { + txt_draw_string(" "); + } + + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + + txt_draw_string(") "); + + txt_restore_colours(&colors); + txt_set_widget_bg(checkbox); + txt_draw_string(checkbox->label); + + for (i = txt_utf8_strlen(checkbox->label); i < w - 4; ++i) + { + txt_draw_string(" "); + } +} + +static void txt_checkbox_destructor(TXT_UNCAST_ARG(checkbox)) +{ + TXT_CAST_ARG(txt_checkbox_t, checkbox); + + free(checkbox->label); +} + +static int txt_check_box_keypress(TXT_UNCAST_ARG(checkbox), int key) +{ + TXT_CAST_ARG(txt_checkbox_t, checkbox); + + if (key == KEY_ENTER || key == ' ') + { + *checkbox->variable = !*checkbox->variable; + txt_emit_signal(checkbox, "changed"); + return 1; + } + + return 0; +} + +static void txt_checkbox_mousepress(TXT_UNCAST_ARG(checkbox), int x, int y, + int b) +{ + TXT_CAST_ARG(txt_checkbox_t, checkbox); + + if (b == TXT_MOUSE_LEFT) + { + /* Equivalent to pressing enter */ + + txt_check_box_keypress(checkbox, KEY_ENTER); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_checkbox_t *txt_new_check_box(const char *label, int *variable) +{ + txt_checkbox_t *checkbox; + + checkbox = malloc(sizeof(txt_checkbox_t)); + + txt_init_widget(checkbox, &txt_checkbox_class); + checkbox->label = strdup(label); + checkbox->variable = variable; + checkbox->inverted = 0; + + return checkbox; +} + +txt_checkbox_t *txt_new_inverted_checkbox(const char *label, int *variable) +{ + txt_checkbox_t *result; + + result = txt_new_check_box(label, variable); + result->inverted = 1; + + return result; +} diff --git a/games/NXDoom/textscreen/txt_checkbox.h b/games/NXDoom/textscreen/txt_checkbox.h new file mode 100644 index 00000000000..b988f2cf193 --- /dev/null +++ b/games/NXDoom/textscreen/txt_checkbox.h @@ -0,0 +1,82 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_checkbox.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_CHECKBOX_H +#define TXT_CHECKBOX_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Checkbox widget. + * + * A checkbox is used to control boolean values that may be either on + * or off. The widget has a label that is displayed to the right of + * the checkbox indicator. The widget tracks an integer variable; + * if the variable is non-zero, the checkbox is checked, while if it + * is zero, the checkbox is unchecked. It is also possible to + * create "inverted" checkboxes where this logic is reversed. + * + * When a checkbox is changed, it emits the "changed" signal. + */ + +struct txt_checkbox_s +{ + txt_widget_t widget; + char *label; + int *variable; + int inverted; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new checkbox. + * + * @param label The label for the new checkbox (UTF-8 format). + * @param variable Pointer to the variable containing this checkbox's + * value. + * @return Pointer to the new checkbox. + */ + +txt_checkbox_t *txt_new_check_box(const char *label, int *variable); + +/** + * Create a new inverted checkbox. + * + * An inverted checkbox displays the opposite of a normal checkbox; + * where it would be checked, it appears unchecked, and vice-versa. + * + * @param label The label for the new checkbox (UTF-8 format). + * @param variable Pointer to the variable containing this checkbox's + * value. + * @return Pointer to the new checkbox. + */ + +txt_checkbox_t *txt_new_inverted_checkbox(const char *label, int *variable); + +#endif /* TXT_CHECKBOX_H */ diff --git a/games/NXDoom/textscreen/txt_conditional.c b/games/NXDoom/textscreen/txt_conditional.c new file mode 100644 index 00000000000..182711915f1 --- /dev/null +++ b/games/NXDoom/textscreen/txt_conditional.c @@ -0,0 +1,189 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_conditional.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2016 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_conditional.h" +#include "txt_strut.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct txt_conditional_s +{ + txt_widget_t widget; + int *var; + int expected_value; + txt_widget_t *child; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int condition_true(txt_conditional_t *conditional) +{ + return *conditional->var == conditional->expected_value; +} + +static int txt_cond_selectable(TXT_UNCAST_ARG(conditional)) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + return condition_true(conditional) && + txt_selectable_widget(conditional->child); +} + +static void txt_cond_size_calc(TXT_UNCAST_ARG(conditional)) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (!condition_true(conditional)) + { + conditional->widget.w = 0; + conditional->widget.h = 0; + } + else + { + txt_calc_widget_size(conditional->child); + conditional->widget.w = conditional->child->w; + conditional->widget.h = conditional->child->h; + } +} + +static void txt_cond_layout(TXT_UNCAST_ARG(conditional)) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (condition_true(conditional)) + { + conditional->child->x = conditional->widget.x; + conditional->child->y = conditional->widget.y; + txt_layout_widget(conditional->child); + } +} + +static void txt_cond_drawer(TXT_UNCAST_ARG(conditional)) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (condition_true(conditional)) + { + txt_draw_widget(conditional->child); + } +} + +static void txt_cond_destructor(TXT_UNCAST_ARG(conditional)) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + txt_destroy_widget(conditional->child); +} + +static void txt_cond_focused(TXT_UNCAST_ARG(conditional), int focused) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (condition_true(conditional)) + { + txt_set_widget_focus(conditional->child, focused); + } +} + +static int txt_cond_key_press(TXT_UNCAST_ARG(conditional), int key) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (condition_true(conditional)) + { + return txt_widget_key_press(conditional->child, key); + } + + return 0; +} + +static void txt_cond_mouse_press(TXT_UNCAST_ARG(conditional), int x, int y, + int b) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (condition_true(conditional)) + { + txt_widget_mouse_press(conditional->child, x, y, b); + } +} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_conditional_class = +{ + txt_cond_selectable, txt_cond_size_calc, txt_cond_drawer, + txt_cond_key_press, txt_cond_destructor, txt_cond_mouse_press, + txt_cond_layout, txt_cond_focused, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_conditional_t *txt_new_conidtional(int *var, int expected_value, + TXT_UNCAST_ARG(child)) +{ + TXT_CAST_ARG(txt_widget_t, child); + txt_conditional_t *conditional; + + conditional = malloc(sizeof(txt_conditional_t)); + + txt_init_widget(conditional, &txt_conditional_class); + conditional->var = var; + conditional->expected_value = expected_value; + conditional->child = child; + + child->parent = &conditional->widget; + + return conditional; +} + +/* "Static" conditional that returns an empty strut if the given static + * value is false. Kind of like a conditional but we only evaluate it at + * creation time. + */ + +txt_widget_t *txt_if(int conditional, TXT_UNCAST_ARG(child)) +{ + TXT_CAST_ARG(txt_widget_t, child); + + if (conditional) + { + return child; + } + else + { + txt_strut_t *nullwidget; + txt_destroy_widget(child); + nullwidget = txt_new_strut(0, 0); + return &nullwidget->widget; + } +} diff --git a/games/NXDoom/textscreen/txt_conditional.h b/games/NXDoom/textscreen/txt_conditional.h new file mode 100644 index 00000000000..691c891e4b4 --- /dev/null +++ b/games/NXDoom/textscreen/txt_conditional.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_conditional.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2016 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_CONDITIONAL_H +#define TXT_CONDITIONAL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/** + * @file txt_conditional.h + * + * Conditional widget. + */ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/** + * Conditional widget. + * + * A conditional widget contains another widget, and conditionally + * shows or hides it based on the value of a variable. + */ + +typedef struct txt_conditional_s txt_conditional_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new conditional widget. + * + * @param var The variable to check. + * @param expected_value If the variable has this value, the widget is + * shown. + * @param child The inner widget to show or hide. + * @return Pointer to the new conditional widget. + */ + +txt_conditional_t *txt_new_conidtional(int *var, int expected_value, + TXT_UNCAST_ARG(child)); + +/** + * Return the given child widget if the given boolean condition is true. + * + * If the condition is not true, the child widget is destroyed and a dummy + * "null" widget is returned that shows nothing. + * + * @param condition Boolean condition - true or false value. + * @param child Widget to conditionally return. + * @return Either child (if condition is true) or a null + * widget. + */ + +txt_widget_t *txt_if(int condition, TXT_UNCAST_ARG(child)); + +#endif /* #ifndef TXT_CONDITIONAL_H */ diff --git a/games/NXDoom/textscreen/txt_desktop.c b/games/NXDoom/textscreen/txt_desktop.c new file mode 100644 index 00000000000..06416a233ab --- /dev/null +++ b/games/NXDoom/textscreen/txt_desktop.c @@ -0,0 +1,416 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_desktop.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_desktop.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_separator.h" +#include "txt_window.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HELP_KEY KEY_F1 +#define MAXWINDOWS 128 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static char *g_desktop_title; +static txt_window_t *g_all_windows[MAXWINDOWS]; +static int g_num_windows = 0; +static int g_main_loop_running = 0; + +static txt_idle_callback_f g_periodic_callback = NULL; +static void *g_periodic_callback_data; +static unsigned int g_periodic_callback_period; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_exit_mainloop(void) +{ + g_main_loop_running = 0; +} + +static int txt_raise_window(txt_window_t *window) +{ + int i; + + for (i = 0; i < g_num_windows - 1; ++i) + { + if (g_all_windows[i] == window) + { + g_all_windows[i] = g_all_windows[i + 1]; + g_all_windows[i + 1] = window; + + if (i == g_num_windows - 2) + { + txt_set_window_focus(g_all_windows[i], 0); + txt_set_window_focus(window, 1); + } + + return 1; + } + } + + /* Window not in the list, or at the end of the list (top) already. */ + + return 0; +} + +static void draw_desktop_background(const char *title) +{ + int i; + unsigned char *screendata; + unsigned char *p; + + screendata = txt_get_screen_data(); + + /* Fill the screen with gradient characters */ + + p = screendata; + + for (i = 0; i < TXT_SCREEN_W * TXT_SCREEN_H; ++i) + { + *p++ = 0xb1; + *p++ = TXT_COLOR_GREY | (TXT_COLOR_BLUE << 4); + } + + /* Draw the top and bottom banners */ + + p = screendata; + + for (i = 0; i < TXT_SCREEN_W; ++i) + { + *p++ = ' '; + *p++ = TXT_COLOR_BLACK | (TXT_COLOR_GREY << 4); + } + + p = screendata + (TXT_SCREEN_H - 1) * TXT_SCREEN_W * 2; + + for (i = 0; i < TXT_SCREEN_W; ++i) + { + *p++ = ' '; + *p++ = TXT_COLOR_BLACK | (TXT_COLOR_GREY << 4); + } + + /* Print the title */ + + txt_goto_xy(0, 0); + txt_fgcolour(TXT_COLOR_BLACK); + txt_bgcolour(TXT_COLOR_GREY, 0); + + txt_putchar(' '); + txt_puts(title); +} + +static void draw_help_indicator(void) +{ + char keybuf[10]; + int fgcolor; + int x; + int y; + + txt_get_key_description(HELP_KEY, keybuf, sizeof(keybuf)); + + txt_get_mouse_position(&x, &y); + + if (y == 0 && x >= TXT_SCREEN_W - 9) + { + fgcolor = TXT_COLOR_GREY; + txt_bgcolour(TXT_COLOR_BLACK, 0); + } + else + { + fgcolor = TXT_COLOR_BLACK; + txt_bgcolour(TXT_COLOR_GREY, 0); + } + + txt_goto_xy(TXT_SCREEN_W - 9, 0); + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + txt_draw_string(" "); + txt_draw_string(keybuf); + + txt_fgcolour(fgcolor); + txt_draw_string("=Help "); +} + +static void desktop_input_event(int c) +{ + txt_window_t *active_window; + int x; + int y; + + switch (c) + { + case TXT_MOUSE_LEFT: + txt_get_mouse_position(&x, &y); + + /* Clicking the top-right of the screen is equivalent + * to pressing the help key. + */ + + if (y == 0 && x >= TXT_SCREEN_W - 9) + { + desktop_input_event(HELP_KEY); + } + break; + + case HELP_KEY: + active_window = txt_get_active_window(); + if (active_window != NULL) + { + txt_open_window_help_url(active_window); + } + break; + + default: + break; + } +} + +static void txt_draw_ascii_table(void) +{ + unsigned char *screendata; + char buf[10]; + int x; + int y; + int n; + + screendata = txt_get_screen_data(); + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + txt_bgcolour(TXT_COLOR_BLACK, 0); + + for (y = 0; y < 16; ++y) + { + for (x = 0; x < 16; ++x) + { + n = y * 16 + x; + + txt_goto_xy(x * 5, y); + txt_snprintf(buf, sizeof(buf), "%02x ", n); + txt_puts(buf); + + /* Write the character directly to the screen memory buffer: */ + + screendata[(y * TXT_SCREEN_W + x * 5 + 3) * 2] = n; + } + } + + txt_update_screen(); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_add_desktop_window(txt_window_t *win) +{ + /* Previously-top window loses focus: */ + + if (g_num_windows > 0) + { + txt_set_window_focus(g_all_windows[g_num_windows - 1], 0); + } + + g_all_windows[g_num_windows] = win; + ++g_num_windows; + + /* New window gains focus: */ + + txt_set_window_focus(win, 1); +} + +void txt_remove_desktop_window(txt_window_t *win) +{ + int from; + int to; + + /* Window must lose focus if it's being removed: */ + + txt_set_window_focus(win, 0); + + for (from = 0, to = 0; from < g_num_windows; ++from) + { + if (g_all_windows[from] != win) + { + g_all_windows[to] = g_all_windows[from]; + ++to; + } + } + + g_num_windows = to; + + /* Top window gains focus: */ + + if (g_num_windows > 0) + { + txt_set_window_focus(g_all_windows[g_num_windows - 1], 1); + } +} + +txt_window_t *txt_get_active_window(void) +{ + if (g_num_windows == 0) + { + return NULL; + } + + return g_all_windows[g_num_windows - 1]; +} + +int txt_lower_window(txt_window_t *window) +{ + int i; + + for (i = 0; i < g_num_windows - 1; ++i) + { + if (g_all_windows[i + 1] == window) + { + g_all_windows[i + 1] = g_all_windows[i]; + g_all_windows[i] = window; + + if (i == g_num_windows - 2) + { + txt_set_window_focus(window, 0); + txt_set_window_focus(g_all_windows[i + 1], 1); + } + + return 1; + } + } + + /* Window not in the list, or at the start of the list (bottom) already. */ + + return 0; +} + +void txt_set_desktop_title(const char *title) +{ + free(g_desktop_title); + g_desktop_title = strdup(title); + txt_set_window_title(title); +} + +void txt_draw_desktop(void) +{ + txt_window_t *active_window; + const char *title; + int i; + + txt_init_clip_area(); + + if (g_desktop_title == NULL) + title = ""; + else + title = g_desktop_title; + + draw_desktop_background(title); + + active_window = txt_get_active_window(); + if (active_window != NULL && active_window->help_url != NULL) + { + draw_help_indicator(); + } + + for (i = 0; i < g_num_windows; ++i) + { + txt_draw_window(g_all_windows[i]); + } + + txt_update_screen(); +} + +/* Fallback function to handle key/mouse events that are not handled by + * the active window. + */ + +void txt_dispatch_events(void) +{ + txt_window_t *active_window; + int c; + + while ((c = txt_getchar()) > 0) + { + active_window = txt_get_active_window(); + + if (active_window != NULL && !txt_window_keypress(active_window, c)) + { + desktop_input_event(c); + } + } +} + +void txt_set_periodic_callback(txt_idle_callback_f callback, void *user_data, + unsigned int period) +{ + g_periodic_callback = callback; + g_periodic_callback_data = user_data; + g_periodic_callback_period = period; +} + +void txt_gui_mainloop(void) +{ + g_main_loop_running = 1; + + while (g_main_loop_running) + { + txt_dispatch_events(); + + /* After the last window is closed, exit the loop */ + + if (g_num_windows <= 0) + { + txt_exit_mainloop(); + continue; + } + + txt_draw_desktop(); + + /* TXT_DrawASCIITable(); */ + + if (g_periodic_callback == NULL) + { + txt_sleep(0); + } + else + { + txt_sleep(g_periodic_callback_period); + + g_periodic_callback(g_periodic_callback_data); + } + } +} diff --git a/games/NXDoom/textscreen/txt_desktop.h b/games/NXDoom/textscreen/txt_desktop.h new file mode 100644 index 00000000000..faae8c8345e --- /dev/null +++ b/games/NXDoom/textscreen/txt_desktop.h @@ -0,0 +1,99 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_desktop.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_DESKTOP_H +#define TXT_DESKTOP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_window.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*txt_idle_callback_f)(void *user_data); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void txt_add_desktop_window(txt_window_t *win); +void txt_remove_desktop_window(txt_window_t *win); +void txt_draw_desktop(void); +void txt_dispatch_events(void); +void txt_draw_window(txt_window_t *window); +void txt_set_window_focus(txt_window_t *window, int focused); +int txt_window_keypress(txt_window_t *window, int c); + +/** + * Set the title displayed at the top of the screen. + * + * @param title The title to display (UTF-8 format). + */ + +void txt_set_desktop_title(const char *title); + +/** + * Start the main event loop. At least one window must have been + * opened prior to running this function. When no windows are left + * open, the event loop exits. + * + * It is possible to trigger an exit from this function using the + * @ref TXT_ExitMainLoop function. + */ + +void txt_gui_mainloop(void); + +/** + * Get the top window on the desktop that is currently receiving + * inputs. + * + * @return The active window, or NULL if no windows are present. + */ + +txt_window_t *txt_get_active_window(void); + +/** + * Set a callback function to be invoked periodically by the main + * loop code. + * + * @param callback The callback to invoke, or NULL to cancel + * an existing callback. + * @param user_data Extra data to pass to the callback. + * @param period Maximum time between invoking each callback. + * eg. a value of 200 will cause the callback + * to be invoked at least once every 200ms. + */ + +void txt_set_periodic_callback(txt_idle_callback_f callback, void *user_data, + unsigned int period); +/** + * Lower the z-position of the given window relative to other windows. + * + * @param window The window to make inactive. + * @return Non-zero if the window was lowered successfully, + * or zero if the window could not be lowered further. + */ + +int txt_lower_window(txt_window_t *window); + +#endif /* TXT_DESKTOP_H */ diff --git a/games/NXDoom/textscreen/txt_dropdown.c b/games/NXDoom/textscreen/txt_dropdown.c new file mode 100644 index 00000000000..da19a8efd86 --- /dev/null +++ b/games/NXDoom/textscreen/txt_dropdown.c @@ -0,0 +1,343 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_dropdown.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomkeys.h" + +#include "txt_button.h" +#include "txt_dropdown.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + txt_window_t *window; + txt_dropdown_list_t *list; + int item; +} callback_data_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_dropdown_list_size_calc(TXT_UNCAST_ARG(list)); +static void txt_dropdown_list_drawer(TXT_UNCAST_ARG(list)); +static void txt_dropdown_list_destructor(TXT_UNCAST_ARG(list)); +static int txt_dropdown_lis_keypress(TXT_UNCAST_ARG(list), int key); +static void txt_dropdown_list_mousepress(TXT_UNCAST_ARG(list), int x, int y, + int b); +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_dropdown_list_class = +{ + txt_always_selectable, + txt_dropdown_list_size_calc, + txt_dropdown_list_drawer, + txt_dropdown_lis_keypress, + txt_dropdown_list_destructor, + txt_dropdown_list_mousepress, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Check if the selected value for a list is valid */ + +static int valid_selection(txt_dropdown_list_t *list) +{ + return *list->variable >= 0 && *list->variable < list->num_values; +} + +/* Calculate the Y position for the selector window */ + +static int selector_window_y(txt_dropdown_list_t *list) +{ + int result; + + if (valid_selection(list)) + { + result = list->widget.y - 1 - *list->variable; + } + else + { + result = list->widget.y - 1 - (list->num_values / 2); + } + + /* Keep dropdown inside the screen. */ + + if (result < 1) + { + result = 1; + } + else if (result + list->num_values > (TXT_SCREEN_H - 3)) + { + result = TXT_SCREEN_H - list->num_values - 3; + } + + return result; +} + +/* Called when a button in the selector window is pressed */ + +static void item_selected(TXT_UNCAST_ARG(button), + TXT_UNCAST_ARG(callback_data)) +{ + TXT_CAST_ARG(callback_data_t, callback_data); + + /* Set the variable */ + + *callback_data->list->variable = callback_data->item; + + txt_emit_signal(callback_data->list, "changed"); + + /* Close the window */ + + txt_close_window(callback_data->window); +} + +/* Free callback data when the window is closed */ + +static void free_callback_data(TXT_UNCAST_ARG(list), + TXT_UNCAST_ARG(callback_data)) +{ + TXT_CAST_ARG(callback_data_t, callback_data); + free(callback_data); +} + +/* Catch presses of escape and close the window. */ + +static int selector_window_listener(txt_window_t *window, int key, + void *user_data) +{ + if (key == KEY_ESCAPE) + { + txt_close_window(window); + return 1; + } + + return 0; +} + +static int selector_mouse_listener(txt_window_t *window, int x, int y, int b, + void *unused) +{ + txt_widget_t *win; + + win = (txt_widget_t *)window; + + if (x < win->x || x > win->x + win->w || y < win->y || y > win->y + win->h) + { + txt_close_window(window); + return 1; + } + + return 0; +} + +/* Open the dropdown list window to select an item */ + +static void open_selector_window(txt_dropdown_list_t *list) +{ + txt_window_t *window; + int i; + + /* Open a simple window with no title bar or action buttons. */ + + window = txt_new_window(NULL); + + txt_set_window_action(window, TXT_HORIZ_LEFT, NULL); + txt_set_window_action(window, TXT_HORIZ_CENTER, NULL); + txt_set_window_action(window, TXT_HORIZ_RIGHT, NULL); + + /* Position the window so that the currently selected item appears + * over the top of the list widget. + */ + + txt_set_window_position(window, TXT_HORIZ_LEFT, TXT_VERT_TOP, + list->widget.x - 2, selector_window_y(list)); + + /* Add a button to the window for each option in the list. */ + + for (i = 0; i < list->num_values; ++i) + { + txt_button_t *button; + callback_data_t *data; + + button = txt_new_button(list->values[i]); + + txt_add_widget(window, button); + + /* Callback struct */ + + data = malloc(sizeof(callback_data_t)); + data->list = list; + data->window = window; + data->item = i; + + /* When the button is pressed, invoke the button press callback */ + + txt_signal_connect(button, "pressed", item_selected, data); + + /* When the window is closed, free back the callback struct */ + + txt_signal_connect(window, "closed", free_callback_data, data); + + /* Is this the currently-selected value? If so, select the button + * in the window as the default. + */ + + if (i == *list->variable) + { + txt_select_widget(window, button); + } + } + + /* Catch presses of escape in this window and close it. */ + + txt_set_key_listener(window, selector_window_listener, NULL); + txt_set_mouse_listener(window, selector_mouse_listener, NULL); +} + +static int dropdown_list_width(txt_dropdown_list_t *list) +{ + int i; + int result; + + /* Find the maximum string width */ + + result = 0; + + for (i = 0; i < list->num_values; ++i) + { + int w = txt_utf8_strlen(list->values[i]); + if (w > result) + { + result = w; + } + } + + return result; +} + +static void txt_dropdown_list_size_calc(TXT_UNCAST_ARG(list)) +{ + TXT_CAST_ARG(txt_dropdown_list_t, list); + + list->widget.w = dropdown_list_width(list); + list->widget.h = 1; +} + +static void txt_dropdown_list_drawer(TXT_UNCAST_ARG(list)) +{ + TXT_CAST_ARG(txt_dropdown_list_t, list); + unsigned int i; + const char *str; + + /* Set bg/fg text colors. */ + + txt_set_widget_bg(list); + + /* Select a string to draw from the list, if the current value is + * in range. Otherwise fall back to a default. + */ + + if (valid_selection(list)) + { + str = list->values[*list->variable]; + } + else + { + str = "???"; + } + + /* Draw the string and fill to the end with spaces */ + + txt_draw_string(str); + + for (i = txt_utf8_strlen(str); i < list->widget.w; ++i) + { + txt_draw_string(" "); + } +} + +static void txt_dropdown_list_destructor(TXT_UNCAST_ARG(list)) +{ +} + +static int txt_dropdown_lis_keypress(TXT_UNCAST_ARG(list), int key) +{ + TXT_CAST_ARG(txt_dropdown_list_t, list); + + if (key == KEY_ENTER) + { + open_selector_window(list); + return 1; + } + + return 0; +} + +static void txt_dropdown_list_mousepress(TXT_UNCAST_ARG(list), int x, int y, + int b) +{ + TXT_CAST_ARG(txt_dropdown_list_t, list); + + /* Left mouse click does the same as selecting and pressing enter */ + + if (b == TXT_MOUSE_LEFT) + { + txt_dropdown_lis_keypress(list, KEY_ENTER); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_dropdown_list_t *txt_new_dropdown_list(int *variable, + const char **values, int num_values) +{ + txt_dropdown_list_t *list; + + list = malloc(sizeof(txt_dropdown_list_t)); + + txt_init_widget(list, &txt_dropdown_list_class); + list->variable = variable; + list->values = values; + list->num_values = num_values; + + return list; +} diff --git a/games/NXDoom/textscreen/txt_dropdown.h b/games/NXDoom/textscreen/txt_dropdown.h new file mode 100644 index 00000000000..08286325bca --- /dev/null +++ b/games/NXDoom/textscreen/txt_dropdown.h @@ -0,0 +1,71 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_dropdown.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_DROPDOWN_H +#define TXT_DROPDOWN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Dropdown list widget. + * + * A dropdown list allows the user to select from a list of values, + * which appears when the list is selected. + * + * When the value of a dropdown list is changed, the "changed" signal + * is emitted. + */ + +struct txt_dropdown_list_s +{ + txt_widget_t widget; + int *variable; + const char **values; + int num_values; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new dropdown list widget. + * + * The parameters specify a list of string labels, and a pointer to an + * integer variable. The variable contains the current "value" of the + * list, as an index within the list of labels. + * + * @param variable Pointer to the variable containing the + * list's value. + * @param values Pointer to an array of strings containing + * the labels to use for the list (UTF-8 format). + * @param num_values The number of variables in the list. + */ + +txt_dropdown_list_t *txt_new_dropdown_list(int *variable, + const char **values, int num_values); + +#endif /* TXT_DROPDOWN_H */ diff --git a/games/NXDoom/textscreen/txt_fileselect.c b/games/NXDoom/textscreen/txt_fileselect.c new file mode 100644 index 00000000000..b1343efd6eb --- /dev/null +++ b/games/NXDoom/textscreen/txt_fileselect.c @@ -0,0 +1,359 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_fileselect.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Routines for selecting files. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_fileselect.h" +#include "txt_gui.h" +#include "txt_inputbox.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_widget.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Linux version: invoke the Zenity command line program to pop up a + * dialog box. This avoids adding Gtk+ as a compile dependency. + */ + +#define ZENITY_BINARY "/usr/bin/zenity" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct txt_fileselect_s +{ + txt_widget_t widget; + txt_inputbox_t *inputbox; + int size; + const char *prompt; + const char **extensions; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_file_select_size_calc(TXT_UNCAST_ARG(fileselect)); +static void txt_file_select_drawer(TXT_UNCAST_ARG(fileselect)); +static int txt_file_select_keypress(TXT_UNCAST_ARG(fileselect), int key); +static void txt_file_select_destructor(TXT_UNCAST_ARG(fileselect)); +static void txt_file_select_mousepress(TXT_UNCAST_ARG(fileselect), int x, + int y, int b); +static void txt_file_select_focused(TXT_UNCAST_ARG(fileselect), int focused); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Dummy value to select a directory. */ + +const char *TXT_DIRECTORY[] = +{ + "__directory__", + NULL, +}; + +txt_widget_class_t txt_fileselect_class = +{ + txt_always_selectable, + txt_file_select_size_calc, + txt_file_select_drawer, + txt_file_select_keypress, + txt_file_select_destructor, + txt_file_select_mousepress, + NULL, + txt_file_select_focused, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static char *exec_read_output(char **argv) +{ + char *result; + int completed; + int pid; + int status; + int result_len; + int pipefd[2]; + + if (pipe(pipefd) != 0) + { + return NULL; + } + + pid = fork(); + + if (pid == 0) + { + dup2(pipefd[1], fileno(stdout)); + execv(argv[0], argv); + exit(-1); + } + + fcntl(pipefd[0], F_SETFL, O_NONBLOCK); + + /* Read program output into 'result' string. + * Wait until the program has completed and (if it was successful) + * a full line has been read. + */ + + result = NULL; + result_len = 0; + completed = 0; + + while (!completed || + (status == 0 && (result == NULL || strchr(result, '\n') == NULL))) + { + char buf[64]; + int bytes; + + if (!completed && waitpid(pid, &status, WNOHANG) != 0) + { + completed = 1; + } + + bytes = read(pipefd[0], buf, sizeof(buf)); + + if (bytes < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK) + { + status = -1; + break; + } + } + else + { + char *new_result = realloc(result, result_len + bytes + 1); + if (new_result == NULL) + { + break; + } + + result = new_result; + memcpy(result + result_len, buf, bytes); + result_len += bytes; + result[result_len] = '\0'; + } + + usleep(100 * 1000); + txt_sleep(1); + txt_update_screen(); + } + + close(pipefd[0]); + close(pipefd[1]); + + /* Must have a success exit code. */ + + if (WEXITSTATUS(status) != 0) + { + free(result); + result = NULL; + } + + /* Strip off newline from the end. */ + + if (result != NULL && result[result_len - 1] == '\n') + { + result[result_len - 1] = '\0'; + } + + return result; +} + +static unsigned int num_extensions(const char **extensions) +{ + unsigned int result = 0; + + if (extensions != NULL) + { + for (result = 0; extensions[result] != NULL; ++result) + ; + } + + return result; +} + +/* ExpandExtension + * given an extension (like wad) + * return a pointer to a string that is a case-insensitive + * pattern representation (like [Ww][Aa][Dd]) + */ + +static char *expand_extension(const char *orig) +{ + int oldlen; + int newlen; + int i; + char *c; + char *newext = NULL; + + oldlen = strlen(orig); + newlen = oldlen * 4; /* pathological case: 'w' => '[Ww]' */ + newext = malloc(newlen + 1); + + if (newext == NULL) + { + return NULL; + } + + c = newext; + + for (i = 0; i < oldlen; ++i) + { + if (isalpha(orig[i])) + { + *c++ = '['; + *c++ = tolower(orig[i]); + *c++ = toupper(orig[i]); + *c++ = ']'; + } + else + { + *c++ = orig[i]; + } + } + + *c = '\0'; + return newext; +} + +static char *txt_select_file(const char *window_title, + const char **extensions) +{ + return NULL; +} + +static void txt_file_select_size_calc(TXT_UNCAST_ARG(fileselect)) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + + /* Calculate widget size, but override the width to always + * be the configured size. + */ + + txt_calc_widget_size(fileselect->inputbox); + fileselect->widget.w = fileselect->size; + fileselect->widget.h = fileselect->inputbox->widget.h; +} + +static void txt_file_select_drawer(TXT_UNCAST_ARG(fileselect)) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + + /* Input box widget inherits all the properties of the + * file selector. + */ + + fileselect->inputbox->widget.x = fileselect->widget.x + 2; + fileselect->inputbox->widget.y = fileselect->widget.y; + fileselect->inputbox->widget.w = fileselect->widget.w - 2; + fileselect->inputbox->widget.h = fileselect->widget.h; + + /* Triple bar symbol gives a distinguishing look to the file selector. */ + + txt_draw_code_page_string("\xf0 "); + txt_bgcolour(TXT_COLOR_BLACK, 0); + txt_draw_widget(fileselect->inputbox); +} + +static void txt_file_select_destructor(TXT_UNCAST_ARG(fileselect)) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + + txt_destroy_widget(fileselect->inputbox); +} + +static int txt_file_select_keypress(TXT_UNCAST_ARG(fileselect), int key) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + return txt_widget_key_press(fileselect->inputbox, key); +} + +static void txt_file_select_mousepress(TXT_UNCAST_ARG(fileselect), int x, + int y, int b) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + txt_widget_mouse_press(fileselect->inputbox, x, y, b); +} + +static void txt_file_select_focused(TXT_UNCAST_ARG(fileselect), int focused) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + + txt_set_widget_focus(fileselect->inputbox, focused); +} + +/* If the (inner) inputbox widget is changed, emit a change to the + * outer (fileselect) widget. + */ + +static void input_box_changed(TXT_UNCAST_ARG(widget), + TXT_UNCAST_ARG(fileselect)) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + + txt_emit_signal(&fileselect->widget, "changed"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_fileselect_t *txt_new_file_selector(char **variable, int size, + const char *prompt, + const char **extensions) +{ + txt_fileselect_t *fileselect; + + fileselect = malloc(sizeof(txt_fileselect_t)); + txt_init_widget(fileselect, &txt_fileselect_class); + fileselect->inputbox = txt_new_input_box(variable, 1024); + fileselect->inputbox->widget.parent = &fileselect->widget; + fileselect->size = size; + fileselect->prompt = prompt; + fileselect->extensions = extensions; + + txt_signal_connect(fileselect->inputbox, "changed", input_box_changed, + fileselect); + + return fileselect; +} diff --git a/games/NXDoom/textscreen/txt_fileselect.h b/games/NXDoom/textscreen/txt_fileselect.h new file mode 100644 index 00000000000..a8bc7eb5ceb --- /dev/null +++ b/games/NXDoom/textscreen/txt_fileselect.h @@ -0,0 +1,70 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_fileselect.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Routines for selecting files, and the txt_fileselect_t widget. + * + ****************************************************************************/ + +#ifndef TXT_FILESELECT_H +#define TXT_FILESELECT_H + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/** + * File selection widget. + * + * A file selection widget resembles an input box (@ref txt_inputbox_t) + * but opens a file selector dialog box when clicked. + */ + +typedef struct txt_fileselect_s txt_fileselect_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/** + * Special value to use for 'extensions' that selects a directory + * instead of a file. + */ + +extern const char *TXT_DIRECTORY[]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new txt_fileselect_t widget. + * + * @param variable Pointer to a char * variable in which the selected + * file should be stored (UTF-8 format). + * @param size Width of the file selector widget in characters. + * @param prompt Pointer to a string containing a prompt to display + * in the file selection window. + * @param extensions NULL-terminated list of filename extensions that + * can be used for this widget, or @ref TXT_DIRECTORY + * to select directories. + */ + +txt_fileselect_t *txt_new_file_selector(char **variable, int size, + const char *prompt, + const char **extensions); + +#endif /* TXT_FILESELECT_H */ diff --git a/games/NXDoom/textscreen/txt_font.h b/games/NXDoom/textscreen/txt_font.h new file mode 100644 index 00000000000..9815f837885 --- /dev/null +++ b/games/NXDoom/textscreen/txt_font.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_font.h + * + * SPDX-License-Identifer: GPLv2 + * + ****************************************************************************/ + +#ifndef __TEXTSCREEN_FONT_H__ +#define __TEXTSCREEN_FONT_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct +{ + const char *name; + const uint8_t *data; + unsigned int w; + unsigned int h; +} txt_font_t; + +#endif /* __TEXTSCREEN_FONT_H__ */ diff --git a/games/NXDoom/textscreen/txt_gui.c b/games/NXDoom/textscreen/txt_gui.c new file mode 100644 index 00000000000..9df99e62d01 --- /dev/null +++ b/games/NXDoom/textscreen/txt_gui.c @@ -0,0 +1,519 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_gui.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define VALID_X(x) ((x) >= g_cliparea->x1 && (x) < g_cliparea->x2) +#define VALID_Y(y) ((y) >= g_cliparea->y1 && (y) < g_cliparea->y2) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct txt_cliparea_s txt_cliparea_t; + +struct txt_cliparea_s +{ + int x1; + int x2; + int y1; + int y2; + txt_cliparea_t *next; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Array of border characters for drawing windows. The array looks like this: + * + * +-++ + * | || + * +-++ + * +-++ + */ + +static const int g_borders[4][4] = +{ + {0xda, 0xc4, 0xc2, 0xbf}, + {0xb3, ' ', 0xb3, 0xb3}, + {0xc3, 0xc4, 0xc5, 0xb4}, + {0xc0, 0xc4, 0xc1, 0xd9}, +}; + +static txt_cliparea_t *g_cliparea = NULL; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#if 0 /* UNUSED */ +static void txt_draw_desktop_background(const char *title) +{ + int i; + unsigned char *screendata; + unsigned char *p; + + screendata = txt_get_screen_data(); + + /* Fill the screen with gradient characters */ + + p = screendata; + + for (i = 0; i < TXT_SCREEN_W * TXT_SCREEN_H; ++i) + { + *p++ = 0xb1; + *p++ = TXT_COLOR_GREY | (TXT_COLOR_BLUE << 4); + } + + /* Draw the top and bottom banners */ + + p = screendata; + + for (i = 0; i < TXT_SCREEN_W; ++i) + { + *p++ = ' '; + *p++ = TXT_COLOR_BLACK | (TXT_COLOR_GREY << 4); + } + + p = screendata + (TXT_SCREEN_H - 1) * TXT_SCREEN_W * 2; + + for (i = 0; i < TXT_SCREEN_W; ++i) + { + *p++ = ' '; + *p++ = TXT_COLOR_BLACK | (TXT_COLOR_GREY << 4); + } + + /* Print the title */ + + txt_goto_xy(0, 0); + txt_fgcolour(TXT_COLOR_BLACK); + txt_bgcolour(TXT_COLOR_GREY, 0); + + txt_draw_string(" "); + txt_draw_string(title); +} +#endif + +static void txt_draw_shadow(int x, int y, int w, int h) +{ + unsigned char *screendata; + unsigned char *p; + int x1; + int y1; + + screendata = txt_get_screen_data(); + + for (y1 = y; y1 < y + h; ++y1) + { + p = screendata + (y1 * TXT_SCREEN_W + x) * 2; + + for (x1 = x; x1 < x + w; ++x1) + { + if (VALID_X(x1) && VALID_Y(y1)) + { + p[1] = TXT_COLOR_DARK_GREY; + } + + p += 2; + } + } +} + +static void put_unicode_char(unsigned int c) +{ + int d; + + /* Treat control characters specially. */ + + if (c == '\n' || c == '\b') + { + txt_putchar(c); + return; + } + + /* Map Unicode character into the symbol used to represent it in this + * code page. For unrepresentable characters, print a fallback instead. + * Note that we use txt_put_symbol() here because we just want to do a + * raw write into the screen buffer. + */ + + d = txt_unicode_character(c); + + if (d >= 0) + { + txt_put_symbol(d); + } + else + { + txt_put_symbol('\xa8'); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_draw_window_frame(const char *title, int x, int y, int w, int h) +{ + txt_saved_colors_t colors; + int x1; + int y1; + int bx; + int by; + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + + for (y1 = y; y1 < y + h; ++y1) + { + /* Select the appropriate row and column in the borders + * array to pick the appropriate character to draw at + * this location. + * + * Draw a horizontal line on the third line down, so we + * draw a box around the title. + */ + + by = y1 == y ? 0 + : y1 == y + 2 && title != NULL ? 2 + : y1 == y + h - 1 ? 3 + : 1; + + for (x1 = x; x1 < x + w; ++x1) + { + bx = x1 == x ? 0 : x1 == x + w - 1 ? 3 : 1; + + if (VALID_X(x1) && VALID_Y(y1)) + { + txt_goto_xy(x1, y1); + txt_putchar(g_borders[by][bx]); + } + } + } + + /* Draw the title */ + + if (title != NULL) + { + txt_goto_xy(x + 1, y + 1); + txt_bgcolour(TXT_COLOR_GREY, 0); + txt_fgcolour(TXT_COLOR_BLUE); + + for (x1 = 0; x1 < w - 2; ++x1) + { + txt_draw_string(" "); + } + + txt_goto_xy(x + (w - txt_utf8_strlen(title)) / 2, y + 1); + txt_draw_string(title); + } + + /* Draw the window's shadow. */ + + txt_draw_shadow(x + 2, y + h, w, 1); + txt_draw_shadow(x + w, y + 1, 2, h); + + txt_restore_colours(&colors); +} + +void txt_draw_separator(int x, int y, int w) +{ + txt_saved_colors_t colors; + unsigned char *data; + int x1; + int b; + + data = txt_get_screen_data(); + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + + if (!VALID_Y(y)) + { + return; + } + + data += (y * TXT_SCREEN_W + x) * 2; + + for (x1 = x; x1 < x + w; ++x1) + { + txt_goto_xy(x1, y); + + b = x1 == x ? 0 : x1 == x + w - 1 ? 3 : 1; + + if (VALID_X(x1)) + { + /* Read the current value from the screen + * Check that it matches what the window should look like if + * there is no separator, then apply the separator + */ + + if (*data == g_borders[1][b]) + { + txt_putchar(g_borders[2][b]); + } + } + + data += 2; + } + + txt_restore_colours(&colors); +} + +/* Alternative to txt_draw_string() where the argument is a "code page + * string" - characters are in native code page format and not UTF-8. + */ + +void txt_draw_code_page_string(const char *s) +{ + int x; + int y; + int x1; + const char *p; + + txt_get_xy(&x, &y); + + if (VALID_Y(y)) + { + x1 = x; + + for (p = s; *p != '\0'; ++p) + { + if (VALID_X(x1)) + { + txt_goto_xy(x1, y); + txt_putchar(*p); + } + + x1 += 1; + } + } + + txt_goto_xy(x + strlen(s), y); +} + +void txt_draw_string(const char *s) +{ + int x; + int y; + int x1; + const char *p; + unsigned int c; + + txt_get_xy(&x, &y); + + if (VALID_Y(y)) + { + x1 = x; + + for (p = s; *p != '\0'; ) + { + c = txt_decode_utf8(&p); + + if (c == 0) + { + break; + } + + if (VALID_X(x1)) + { + txt_goto_xy(x1, y); + put_unicode_char(c); + } + + x1 += 1; + } + } + + txt_goto_xy(x + txt_utf8_strlen(s), y); +} + +void txt_draw_horiz_scrollbar(int x, int y, int w, int cursor, int range) +{ + txt_saved_colors_t colors; + int x1; + int cursor_x; + + if (!VALID_Y(y)) + { + return; + } + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BLACK); + txt_bgcolour(TXT_COLOR_GREY, 0); + + txt_goto_xy(x, y); + txt_putchar('\x1b'); + + cursor_x = x + 1; + + if (range > 0) + { + cursor_x += (cursor * (w - 3)) / range; + } + + if (cursor_x > x + w - 2) + { + cursor_x = x + w - 2; + } + + for (x1 = x + 1; x1 < x + w - 1; ++x1) + { + if (VALID_X(x1)) + { + if (x1 == cursor_x) + { + txt_putchar('\xdb'); + } + else + { + txt_putchar('\xb1'); + } + } + } + + txt_putchar('\x1a'); + txt_restore_colours(&colors); +} + +void txt_draw_vert_scrollbar(int x, int y, int h, int cursor, int range) +{ + txt_saved_colors_t colors; + int y1; + int cursor_y; + + if (!VALID_X(x)) + { + return; + } + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BLACK); + txt_bgcolour(TXT_COLOR_GREY, 0); + + txt_goto_xy(x, y); + txt_putchar('\x18'); + + cursor_y = y + 1; + + if (cursor_y > y + h - 2) + { + cursor_y = y + h - 2; + } + + if (range > 0) + { + cursor_y += (cursor * (h - 3)) / range; + } + + for (y1 = y + 1; y1 < y + h - 1; ++y1) + { + if (VALID_Y(y1)) + { + txt_goto_xy(x, y1); + + if (y1 == cursor_y) + { + txt_putchar('\xdb'); + } + else + { + txt_putchar('\xb1'); + } + } + } + + txt_goto_xy(x, y + h - 1); + txt_putchar('\x19'); + txt_restore_colours(&colors); +} + +void txt_init_clip_area(void) +{ + if (g_cliparea == NULL) + { + g_cliparea = malloc(sizeof(txt_cliparea_t)); + g_cliparea->x1 = 0; + g_cliparea->x2 = TXT_SCREEN_W; + g_cliparea->y1 = 0; + g_cliparea->y2 = TXT_SCREEN_H; + g_cliparea->next = NULL; + } +} + +void txt_push_clip_area(int x1, int x2, int y1, int y2) +{ + txt_cliparea_t *newarea; + + newarea = malloc(sizeof(txt_cliparea_t)); + + /* Set the new clip area to the intersection of the old + * area and the new one. + */ + + newarea->x1 = g_cliparea->x1; + newarea->x2 = g_cliparea->x2; + newarea->y1 = g_cliparea->y1; + newarea->y2 = g_cliparea->y2; + + if (x1 > newarea->x1) newarea->x1 = x1; + if (x2 < newarea->x2) newarea->x2 = x2; + if (y1 > newarea->y1) newarea->y1 = y1; + if (y2 < newarea->y2) newarea->y2 = y2; + +#if 0 + printf("New scrollable area: %i,%i-%i,%i\n", x1, y1, x2, y2); +#endif + + /* Hook into the list */ + + newarea->next = g_cliparea; + g_cliparea = newarea; +} + +void txt_pop_clip_area(void) +{ + txt_cliparea_t *next_cliparea; + + /* Never pop the last entry */ + + if (g_cliparea->next == NULL) return; + + /* Unlink the last entry and delete */ + + next_cliparea = g_cliparea->next; + free(g_cliparea); + g_cliparea = next_cliparea; +} diff --git a/games/NXDoom/textscreen/txt_gui.h b/games/NXDoom/textscreen/txt_gui.h new file mode 100644 index 00000000000..3dfe0f59a5b --- /dev/null +++ b/games/NXDoom/textscreen/txt_gui.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_gui.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * + * Text mode emulation in SDL + * + ****************************************************************************/ + +#ifndef TXT_GUI_H +#define TXT_GUI_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TXT_INACTIVE_WINDOW_BACKGROUND TXT_COLOR_BLACK +#define TXT_ACTIVE_WINDOW_BACKGROUND TXT_COLOR_BLUE +#define TXT_HOVER_BACKGROUND TXT_COLOR_CYAN + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void txt_draw_window_frame(const char *title, int x, int y, int w, int h); +void txt_draw_separator(int x, int y, int w); +void txt_draw_code_page_string(const char *s); +void txt_draw_string(const char *s); + +void txt_draw_horiz_scrollbar(int x, int y, int w, int cursor, int range); +void txt_draw_vert_scrollbar(int x, int y, int h, int cursor, int range); + +void txt_init_clip_area(void); +void txt_push_clip_area(int x1, int x2, int y1, int y2); +void txt_pop_clip_area(void); + +#endif /* TXT_GUI_H */ diff --git a/games/NXDoom/textscreen/txt_inputbox.c b/games/NXDoom/textscreen/txt_inputbox.c new file mode 100644 index 00000000000..17801687a2c --- /dev/null +++ b/games/NXDoom/textscreen/txt_inputbox.c @@ -0,0 +1,387 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_inputbox.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_gui.h" +#include "txt_inputbox.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_input_box_size_calc(TXT_UNCAST_ARG(inputbox)); +static void txt_input_box_drawer(TXT_UNCAST_ARG(inputbox)); +static int txt_input_box_keypress(TXT_UNCAST_ARG(inputbox), int key); +static void txt_input_box_destructor(TXT_UNCAST_ARG(inputbox)); +static void txt_input_box_mousepress(TXT_UNCAST_ARG(inputbox), int x, int y, + int b); +static void txt_input_box_focused(TXT_UNCAST_ARG(inputbox), int focused); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_inputbox_class; +txt_widget_class_t txt_int_inputbox_class; + +txt_widget_class_t txt_inputbox_class = +{ + txt_always_selectable, + txt_input_box_size_calc, + txt_input_box_drawer, + txt_input_box_keypress, + txt_input_box_destructor, + txt_input_box_mousepress, + NULL, + txt_input_box_focused, +}; + +txt_widget_class_t txt_int_inputbox_class = +{ + txt_always_selectable, + txt_input_box_size_calc, + txt_input_box_drawer, + txt_input_box_keypress, + txt_input_box_destructor, + txt_input_box_mousepress, + NULL, + txt_input_box_focused, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void set_buffer_from_value(txt_inputbox_t *inputbox) +{ + if (inputbox->widget.widget_class == &txt_inputbox_class) + { + char **value = (char **)inputbox->value; + + if (*value != NULL) + { + txt_string_copy(inputbox->buffer, *value, + strnlen(*value, inputbox->buffer_len) + 1); + } + else + { + txt_string_copy(inputbox->buffer, "", inputbox->buffer_len); + } + } + else if (inputbox->widget.widget_class == &txt_int_inputbox_class) + { + int *value = (int *)inputbox->value; + txt_snprintf(inputbox->buffer, inputbox->buffer_len, "%i", *value); + } +} + +static void start_editing(txt_inputbox_t *inputbox) +{ + /* Integer input boxes start from an empty buffer: */ + + if (inputbox->widget.widget_class == &txt_int_inputbox_class) + { + txt_string_copy(inputbox->buffer, "", inputbox->buffer_len); + } + else + { + set_buffer_from_value(inputbox); + } + + /* Switch to text input mode so we get shifted input. */ + + txt_set_input_mode(TXT_INPUT_TEXT); + inputbox->editing = 1; +} + +static void stop_editing(txt_inputbox_t *inputbox) +{ + if (inputbox->editing) + { + /* Switch back to normal input mode. */ + + txt_set_input_mode(TXT_INPUT_NORMAL); + inputbox->editing = 0; + } +} + +static void finish_editing(txt_inputbox_t *inputbox) +{ + if (!inputbox->editing) + { + return; + } + + /* Save the new value back to the variable. */ + + if (inputbox->widget.widget_class == &txt_inputbox_class) + { + free(*((char **)inputbox->value)); + *((char **)inputbox->value) = strdup(inputbox->buffer); + } + else if (inputbox->widget.widget_class == &txt_int_inputbox_class) + { + *((int *)inputbox->value) = atoi(inputbox->buffer); + } + + txt_emit_signal(&inputbox->widget, "changed"); + + stop_editing(inputbox); +} + +static void txt_input_box_size_calc(TXT_UNCAST_ARG(inputbox)) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + + /* Enough space for the box + cursor */ + + inputbox->widget.w = inputbox->size + 1; + inputbox->widget.h = 1; +} + +static void txt_input_box_drawer(TXT_UNCAST_ARG(inputbox)) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + int focused; + int i; + int chars; + int w; + + focused = inputbox->widget.focused; + w = inputbox->widget.w; + + /* Select the background color based on whether we are currently + * editing, and if not, whether the widget is focused. + */ + + if (inputbox->editing && focused) + { + txt_bgcolour(TXT_COLOR_BLACK, 0); + } + else + { + txt_set_widget_bg(inputbox); + } + + if (!inputbox->editing) + { + /* If not editing, use the current value from inputbox->value. */ + + set_buffer_from_value(inputbox); + } + + /* If string size exceeds the widget's width, show only the end. */ + + if (txt_utf8_strlen(inputbox->buffer) > w - 1) + { + txt_draw_code_page_string("\xae"); + txt_draw_string(txt_utf8_skip_chars( + inputbox->buffer, txt_utf8_strlen(inputbox->buffer) - w + 2)); + chars = w - 1; + } + else + { + txt_draw_string(inputbox->buffer); + chars = txt_utf8_strlen(inputbox->buffer); + } + + if (chars < w && inputbox->editing && focused) + { + txt_bgcolour(TXT_COLOR_BLACK, 1); + txt_draw_string("_"); + ++chars; + } + + for (i = chars; i < w; ++i) + { + txt_draw_string(" "); + } +} + +static void txt_input_box_destructor(TXT_UNCAST_ARG(inputbox)) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + + stop_editing(inputbox); + free(inputbox->buffer); +} + +static void backspace(txt_inputbox_t *inputbox) +{ + unsigned int len; + char *p; + + len = txt_utf8_strlen(inputbox->buffer); + + if (len > 0) + { + p = txt_utf8_skip_chars(inputbox->buffer, len - 1); + *p = '\0'; + } +} + +static void add_character(txt_inputbox_t *inputbox, int key) +{ + char *end; + char *p; + + if (txt_utf8_strlen(inputbox->buffer) < inputbox->size) + { + /* Add character to the buffer */ + + end = inputbox->buffer + strlen(inputbox->buffer); + p = txt_encode_utf8(end, key); + *p = '\0'; + } +} + +static int txt_input_box_keypress(TXT_UNCAST_ARG(inputbox), int key) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + unsigned int c; + + if (!inputbox->editing) + { + if (key == KEY_ENTER) + { + start_editing(inputbox); + return 1; + } + + /* Backspace or delete erases the contents of the box. */ + + if ((key == KEY_DEL || key == KEY_BACKSPACE) && + inputbox->widget.widget_class == &txt_inputbox_class) + { + free(*((char **)inputbox->value)); + *((char **)inputbox->value) = strdup(""); + } + + return 0; + } + + if (key == KEY_ENTER) + { + finish_editing(inputbox); + } + + if (key == KEY_ESCAPE) + { + stop_editing(inputbox); + } + + if (key == KEY_BACKSPACE) + { + backspace(inputbox); + } + + c = TXT_KEY_TO_UNICODE(key); + + /* Add character to the buffer, but only if it's a printable character + * that we can represent on the screen. + */ + + if (isprint(c) || (c >= 128 && txt_unicode_character(c) >= 0)) + { + add_character(inputbox, c); + } + + return 1; +} + +static void txt_input_box_mousepress(TXT_UNCAST_ARG(inputbox), int x, int y, + int b) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + + if (b == TXT_MOUSE_LEFT) + { + /* Make mouse clicks start editing the box */ + + if (!inputbox->editing) + { + /* Send a simulated keypress to start editing */ + + txt_widget_key_press(inputbox, KEY_ENTER); + } + } +} + +static void txt_input_box_focused(TXT_UNCAST_ARG(inputbox), int focused) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + + /* Stop editing when we lose focus. */ + + if (inputbox->editing && !focused) + { + finish_editing(inputbox); + } +} + +static txt_inputbox_t *new_input_box(txt_widget_class_t *widget_class, + void *value, int size) +{ + txt_inputbox_t *inputbox; + + inputbox = malloc(sizeof(txt_inputbox_t)); + + txt_init_widget(inputbox, widget_class); + inputbox->value = value; + inputbox->size = size; + + /* 'size' is the maximum number of characters that can be entered, + * but for a UTF-8 string, each character can take up to four + * characters. + */ + + inputbox->buffer_len = size * 4 + 1; + inputbox->buffer = malloc(inputbox->buffer_len); + inputbox->editing = 0; + + return inputbox; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_inputbox_t *txt_new_input_box(char **value, int size) +{ + return new_input_box(&txt_inputbox_class, value, size); +} + +txt_inputbox_t *txt_new_int_input_box(int *value, int size) +{ + return new_input_box(&txt_int_inputbox_class, value, size); +} diff --git a/games/NXDoom/textscreen/txt_inputbox.h b/games/NXDoom/textscreen/txt_inputbox.h new file mode 100644 index 00000000000..077df013e30 --- /dev/null +++ b/games/NXDoom/textscreen/txt_inputbox.h @@ -0,0 +1,82 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_inputbox.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_INPUTBOX_H +#define TXT_INPUTBOX_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Input box widget. + * + * An input box is a widget that displays a value, which can be + * selected to enter a new value. + * + * Input box widgets can be of an integer or string type. + */ + +struct txt_inputbox_s +{ + txt_widget_t widget; + char *buffer; + size_t buffer_len; + unsigned int size; + int editing; + void *value; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new input box widget for controlling a string value. + * + * @param value Pointer to a string variable that contains + * a pointer to the current value of the + * input box. The value should be allocated + * dynamically; when the string is changed it + * will be freed and the variable set to point + * to the new string value. String will be in + * UTF-8 format. + * @param size Width of the input box, in characters. + * @return Pointer to the new input box widget. + */ + +txt_inputbox_t *txt_new_input_box(char **value, int size); + +/** + * Create a new input box widget for controlling an integer value. + * + * @param value Pointer to an integer variable containing + * the value of the input box. + * @param size Width of the input box, in characters. + * @return Pointer to the new input box widget. + */ + +txt_inputbox_t *txt_new_int_input_box(int *value, int size); + +#endif /* TXT_INPUTBOX_H */ diff --git a/games/NXDoom/textscreen/txt_io.c b/games/NXDoom/textscreen/txt_io.c new file mode 100644 index 00000000000..f8127f17d09 --- /dev/null +++ b/games/NXDoom/textscreen/txt_io.c @@ -0,0 +1,197 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_io.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * + * Text mode I/O functions, similar to C stdio + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_io.h" +#include "txt_main.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_cur_x = 0; +static int g_cur_y = 0; +static txt_color_t g_fgcolour = TXT_COLOR_GREY; +static txt_color_t g_bgcolour = TXT_COLOR_BLACK; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#if 0 /* UNUSED */ +static void txt_clear_screen(void) +{ + unsigned char *screen; + int i; + + screen = txt_get_screen_data(); + + for (i = 0; i < TXT_SCREEN_W * TXT_SCREEN_H; ++i) + { + screen[i * 2] = ' '; + screen[i * 2 + 1] = (bgcolor << 4) | fgcolor; + } + + cur_x = 0; + cur_y = 0; +} +#endif + +static void new_line(unsigned char *screendata) +{ + int i; + unsigned char *p; + + g_cur_x = 0; + ++g_cur_y; + + if (g_cur_y >= TXT_SCREEN_H) + { + /* Scroll the screen up */ + + g_cur_y = TXT_SCREEN_H - 1; + + memmove(screendata, screendata + TXT_SCREEN_W * 2, + TXT_SCREEN_W * 2 * (TXT_SCREEN_H - 1)); + + /* Clear the bottom line */ + + p = screendata + (TXT_SCREEN_H - 1) * 2 * TXT_SCREEN_W; + + for (i = 0; i < TXT_SCREEN_W; ++i) + { + *p++ = ' '; + *p++ = g_fgcolour | (g_bgcolour << 4); + } + } +} + +static void put_symbol(unsigned char *screendata, int c) +{ + unsigned char *p; + + p = screendata + g_cur_y * TXT_SCREEN_W * 2 + g_cur_x * 2; + + /* Add a new character to the buffer */ + + p[0] = c; + p[1] = g_fgcolour | (g_bgcolour << 4); + + ++g_cur_x; + + if (g_cur_x >= TXT_SCREEN_W) + { + new_line(screendata); + } +} + +static void put_char(unsigned char *screendata, int c) +{ + switch (c) + { + case '\n': + new_line(screendata); + break; + + case '\b': + + /* backspace */ + + --g_cur_x; + if (g_cur_x < 0) g_cur_x = 0; + break; + + default: + put_symbol(screendata, c); + break; + } +} + +/* "Blind" version of txt_putchar() below which doesn't do any interpretation + * of control signals. Just write a particular symbol to the screen buffer. + */ + +void txt_put_symbol(int c) +{ + put_symbol(txt_get_screen_data(), c); +} + +void txt_putchar(int c) +{ + put_char(txt_get_screen_data(), c); +} + +void txt_puts(const char *s) +{ + unsigned char *screen; + const char *p; + + screen = txt_get_screen_data(); + + for (p = s; *p != '\0'; ++p) + { + put_char(screen, *p); + } + + put_char(screen, '\n'); +} + +void txt_goto_xy(int x, int y) +{ + g_cur_x = x; + g_cur_y = y; +} + +void txt_get_xy(int *x, int *y) +{ + *x = g_cur_x; + *y = g_cur_y; +} + +void txt_fgcolour(txt_color_t color) +{ + g_fgcolour = color; +} + +void txt_bgcolour(int color, int blinking) +{ + g_bgcolour = color; + if (blinking) g_bgcolour |= TXT_COLOR_BLINKING; +} + +void txt_save_colours(txt_saved_colors_t *save) +{ + save->bgcolor = g_bgcolour; + save->fgcolor = g_fgcolour; +} + +void txt_restore_colours(txt_saved_colors_t *save) +{ + g_bgcolour = save->bgcolor; + g_fgcolour = save->fgcolor; +} diff --git a/games/NXDoom/textscreen/txt_io.h b/games/NXDoom/textscreen/txt_io.h new file mode 100644 index 00000000000..7d91b885bc6 --- /dev/null +++ b/games/NXDoom/textscreen/txt_io.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_io.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Text mode emulation in SDL + * + ****************************************************************************/ + +#ifndef TXT_IO_H +#define TXT_IO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_main.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct +{ + int bgcolor; + int fgcolor; +} txt_saved_colors_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void txt_put_symbol(int c); +void txt_putchar(int c); +void txt_puts(const char *s); +void txt_goto_xy(int x, int y); +void txt_get_xy(int *x, int *y); +void txt_fgcolour(txt_color_t color); +void txt_bgcolour(int color, int blinking); +void txt_save_colours(txt_saved_colors_t *save); +void txt_restore_colours(txt_saved_colors_t *save); + +#endif /* TXT_IO_H */ diff --git a/games/NXDoom/textscreen/txt_label.c b/games/NXDoom/textscreen/txt_label.c new file mode 100644 index 00000000000..c943b1b50a6 --- /dev/null +++ b/games/NXDoom/textscreen/txt_label.c @@ -0,0 +1,234 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_label.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_label.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_label_size_calc(TXT_UNCAST_ARG(label)); +static void txt_label_drawer(TXT_UNCAST_ARG(label)); +static void txt_label_destructor(TXT_UNCAST_ARG(label)); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_label_class = +{ + txt_never_selectable, + txt_label_size_calc, + txt_label_drawer, + NULL, + txt_label_destructor, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_label_size_calc(TXT_UNCAST_ARG(label)) +{ + TXT_CAST_ARG(txt_label_t, label); + + label->widget.w = label->w; + label->widget.h = label->h; +} + +static void txt_label_drawer(TXT_UNCAST_ARG(label)) +{ + TXT_CAST_ARG(txt_label_t, label); + unsigned int x; + unsigned int y; + int origin_x; + int origin_y; + unsigned int align_indent = 0; + unsigned int w; + unsigned int sw; + + w = label->widget.w; + + if (label->bgcolor >= 0) + { + txt_bgcolour(label->bgcolor, 0); + } + if (label->fgcolor >= 0) + { + txt_fgcolour(label->fgcolor); + } + + txt_get_xy(&origin_x, &origin_y); + + for (y = 0; y < label->h; ++y) + { + /* Calculate the amount to indent this line due to the align + * setting + */ + + sw = txt_utf8_strlen(label->lines[y]); + switch (label->widget.align) + { + case TXT_HORIZ_LEFT: + align_indent = 0; + break; + case TXT_HORIZ_CENTER: + align_indent = (label->w - sw) / 2; + break; + case TXT_HORIZ_RIGHT: + align_indent = label->w - sw; + break; + } + + /* Draw this line */ + + txt_goto_xy(origin_x, origin_y + y); + + /* Gap at the start */ + + for (x = 0; x < align_indent; ++x) + { + txt_draw_string(" "); + } + + /* The string itself */ + + txt_draw_string(label->lines[y]); + x += sw; + + /* Gap at the end */ + + for (; x < w; ++x) + { + txt_draw_string(" "); + } + } +} + +static void txt_label_destructor(TXT_UNCAST_ARG(label)) +{ + TXT_CAST_ARG(txt_label_t, label); + + free(label->label); + free(label->lines); +} + +#if 0 /* UNUSED */ +static void txt_set_bg_colour(txt_label_t *label, txt_color_t color) +{ + label->bgcolor = color; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_set_label(txt_label_t *label, const char *value) +{ + char *p; + unsigned int y; + + /* Free back the old label */ + + free(label->label); + free(label->lines); + + /* Set the new value */ + + label->label = strdup(value); + + /* Work out how many lines in this label */ + + label->h = 1; + + for (p = label->label; *p != '\0'; ++p) + { + if (*p == '\n') + { + ++label->h; + } + } + + /* Split into lines */ + + label->lines = malloc(sizeof(char *) * label->h); + label->lines[0] = label->label; + y = 1; + + for (p = label->label; *p != '\0'; ++p) + { + if (*p == '\n') + { + label->lines[y] = p + 1; + *p = '\0'; + ++y; + } + } + + label->w = 0; + + for (y = 0; y < label->h; ++y) + { + unsigned int line_len; + + line_len = txt_utf8_strlen(label->lines[y]); + + if (line_len > label->w) label->w = line_len; + } +} + +txt_label_t *txt_new_label(const char *text) +{ + txt_label_t *label; + + label = malloc(sizeof(txt_label_t)); + + txt_init_widget(label, &txt_label_class); + label->label = NULL; + label->lines = NULL; + + /* Default colors */ + + label->bgcolor = -1; + label->fgcolor = -1; + + txt_set_label(label, text); + + return label; +} + +void txt_set_fg_colour(txt_label_t *label, txt_color_t color) +{ + label->fgcolor = color; +} diff --git a/games/NXDoom/textscreen/txt_label.h b/games/NXDoom/textscreen/txt_label.h new file mode 100644 index 00000000000..f7f1b10330a --- /dev/null +++ b/games/NXDoom/textscreen/txt_label.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_label.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_LABEL_H +#define TXT_LABEL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_main.h" +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/** + * Label widget. + * + * A label widget does nothing except show a text label. + */ + +typedef struct txt_label_s txt_label_t; + +struct txt_label_s +{ + txt_widget_t widget; + char *label; + char **lines; + unsigned int w; + unsigned int h; + int fgcolor; + int bgcolor; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new label widget. + * + * @param label String to display in the widget (UTF-8 format). + * @return Pointer to the new label widget. + */ + +txt_label_t *txt_new_label(const char *label); + +/** + * Set the string displayed in a label widget. + * + * @param label The widget. + * @param value The string to display (UTF-8 format). + */ + +void txt_set_label(txt_label_t *label, const char *value); + +/** + * Set the foreground color of a label widget. + * + * @param label The widget. + * @param color The foreground color to use. + */ + +void txt_set_fg_colour(txt_label_t *label, txt_color_t color); + +#endif /* TXT_LABEL_H */ diff --git a/games/NXDoom/textscreen/txt_main.h b/games/NXDoom/textscreen/txt_main.h new file mode 100644 index 00000000000..34a92bde885 --- /dev/null +++ b/games/NXDoom/textscreen/txt_main.h @@ -0,0 +1,255 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_main.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * + * Base interface that abstracts the text mode screen. + * + ****************************************************************************/ + +#ifndef TXT_MAIN_H +#define TXT_MAIN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/* For the moment, txt_sdl.c is the only implementation of the base + * text mode screen API: + */ + +#include "txt_sdl.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* textscreen key values: + * Key values are difficult because we have to support multiple conflicting + * address spaces. + * First, Doom's key constants use 0-127 as ASCII and extra values from + * 128-255 to represent special keys. Second, mouse buttons are represented + * as buttons. Finally, we want to be able to support Unicode. + * + * So we define different ranges: + * 0-255: Doom key constants, including ASCII. + * 256-511: Mouse buttons and other reserved. + * >=512: Unicode values greater than 127 are offset up into this range. + + * Special keypress values that correspond to mouse button clicks + */ + +#define TXT_MOUSE_BASE 256 +#define TXT_MOUSE_LEFT (TXT_MOUSE_BASE + 0) +#define TXT_MOUSE_RIGHT (TXT_MOUSE_BASE + 1) +#define TXT_MOUSE_MIDDLE (TXT_MOUSE_BASE + 2) +#define TXT_MOUSE_SCROLLUP (TXT_MOUSE_BASE + 3) +#define TXT_MOUSE_SCROLLDOWN (TXT_MOUSE_BASE + 4) +#define TXT_MOUSE_X1 (TXT_MOUSE_BASE + 5) +#define TXT_MOUSE_X2 (TXT_MOUSE_BASE + 6) +#define TXT_MAX_MOUSE_BUTTONS 16 + +#define TXT_KEY_TO_MOUSE_BUTTON(x) \ + ((x) >= TXT_MOUSE_BASE && (x) < TXT_MOUSE_BASE + TXT_MAX_MOUSE_BUTTONS \ + ? (x) - TXT_MOUSE_BASE \ + : -1) + +/* Unicode offset. Unicode values from 128 onwards are offset up into + * this range, so TXT_UNICODE_BASE = Unicode character #128, and so on. + */ + +#define TXT_UNICODE_BASE 512 + +/* Convert a key value to a Unicode character: */ + +#define TXT_KEY_TO_UNICODE(x) \ + ((x) < 128 ? (x) \ + : (x) >= TXT_UNICODE_BASE ? ((x) - TXT_UNICODE_BASE + 128) \ + : 0) + +/* Convert a Unicode character to a key value: */ + +#define TXT_UNICODE_TO_KEY(u) \ + ((u) < 128 ? (u) : ((u) - 128 + TXT_UNICODE_BASE)) + +/* Screen size */ + +#define TXT_SCREEN_W 80 +#define TXT_SCREEN_H 25 + +#define TXT_COLOR_BLINKING (1 << 3) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef enum +{ + TXT_COLOR_BLACK, + TXT_COLOR_BLUE, + TXT_COLOR_GREEN, + TXT_COLOR_CYAN, + TXT_COLOR_RED, + TXT_COLOR_MAGENTA, + TXT_COLOR_BROWN, + TXT_COLOR_GREY, + TXT_COLOR_DARK_GREY, + TXT_COLOR_BRIGHT_BLUE, + TXT_COLOR_BRIGHT_GREEN, + TXT_COLOR_BRIGHT_CYAN, + TXT_COLOR_BRIGHT_RED, + TXT_COLOR_BRIGHT_MAGENTA, + TXT_COLOR_YELLOW, + TXT_COLOR_BRIGHT_WHITE, +} txt_color_t; + +/* Modifier keys. */ + +typedef enum +{ + TXT_MOD_SHIFT, + TXT_MOD_CTRL, + TXT_MOD_ALT, + TXT_NUM_MODIFIERS +} txt_modifier_t; + +/* Due to the way the SDL API works, we provide different ways of configuring + * how we read input events, each of which is useful in different scenarios. + */ + +typedef enum +{ + /* "Localized" output that takes software keyboard layout into account, + * but key shifting has no effect. + */ + + TXT_INPUT_NORMAL, + + /* "Raw" input; the keys correspond to physical keyboard layout and + * software keyboard layout has no effect. + */ + + TXT_INPUT_RAW, + + /* Used for full text input. Events are fully shifted and localized. + * However, not all keyboard keys will generate input. + * Setting this mode may activate the on-screen keyboard, depending on + * device and OS. + */ + + TXT_INPUT_TEXT, +} txt_input_mode_t; + +#ifdef __GNUC__ + +#define PRINTF_ATTR(fmt, first) __attribute__((format(printf, fmt, first))) + +#else /* __GNUC__ */ + +#define PRINTF_ATTR(fmt, first) + +#endif /* __GNUC__ */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Initialize the screen + * Returns 1 if successful, 0 if failed. + */ + +int txt_init(void); + +/* Shut down text mode emulation */ + +void txt_shutdown(void); + +/* Get a pointer to the buffer containing the raw screen data. */ + +unsigned char *txt_get_screen_data(void); + +/* Update an area of the screen */ + +void txt_update_screen_area(int x, int y, int w, int h); + +/* Update the whole screen */ + +void txt_update_screen(void); + +/* Set the RGB value for a particular entry in the color palette: */ + +void txt_set_colour(txt_color_t color, int r, int g, int b); + +/* Read a character from the keyboard */ + +int txt_getchar(void); + +/* Given a Unicode character, get a character that can be used to represent + * it on the code page being displayed on the screen. If the character cannot + * be represented, this returns -1. + */ + +int txt_unicode_character(unsigned int c); + +/* Read the current state of modifier keys that are held down. */ + +int txt_get_modifier_state(txt_modifier_t mod); + +/* Provides a short description of a key code, placing into the provided + * buffer. Note that the key is assumed to represent a physical key on the + * keyboard (like that returned by TXT_INPUT_RAW), and the resulting string + * takes keyboard layout into consideration. For example, + * txt_get_key_description('q') on a French keyboard returns "A". + * The contents of the filled buffer will be in UTF-8 format, but will never + * contain characters which can't be shown on the screen. + */ + +void txt_get_key_description(int key, char *buf, size_t buf_len); + +/* Retrieve the current position of the mouse */ + +void txt_get_mouse_position(int *x, int *y); + +/* Sleep until an event is received or the screen needs updating + * Optional timeout in ms (timeout == 0 : sleep forever) + */ + +void txt_sleep(int timeout); + +/* Change mode for text input. */ + +void txt_set_input_mode(txt_input_mode_t mode); + +/* Set the window title of the window containing the text mode screen */ + +void txt_set_window_title(const char *title); + +/* Safe string copy. */ + +void txt_string_copy(char *dest, const char *src, size_t dest_len); + +/* Safe version of vsnprintf(). */ + +int txt_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args); + +/* Safe version of snprintf(). */ + +int txt_snprintf(char *buf, size_t buf_len, const char *s, ...) + PRINTF_ATTR(3, 4); + +#endif /* TXT_MAIN_H */ diff --git a/games/NXDoom/textscreen/txt_radiobutton.c b/games/NXDoom/textscreen/txt_radiobutton.c new file mode 100644 index 00000000000..b13c617c908 --- /dev/null +++ b/games/NXDoom/textscreen/txt_radiobutton.c @@ -0,0 +1,179 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_radiobutton.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomkeys.h" + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_radiobutton.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +static void txt_radio_button_size_calc(TXT_UNCAST_ARG(radiobutton)); +static void txt_radio_button_drawer(TXT_UNCAST_ARG(radiobutton)); +static void txt_radio_button_destructor(TXT_UNCAST_ARG(radiobutton)); +static int txt_radio_button_keypress(TXT_UNCAST_ARG(radiobutton), int key); +static void txt_radio_button_mousepress(TXT_UNCAST_ARG(radiobutton), int x, + int y, int b); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_radiobutton_class = +{ + txt_always_selectable, + txt_radio_button_size_calc, + txt_radio_button_drawer, + txt_radio_button_keypress, + txt_radio_button_destructor, + txt_radio_button_mousepress, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_radio_button_size_calc(TXT_UNCAST_ARG(radiobutton)) +{ + TXT_CAST_ARG(txt_radiobutton_t, radiobutton); + + /* Minimum width is the string length + right-side spaces for padding */ + + radiobutton->widget.w = txt_utf8_strlen(radiobutton->label) + 5; + radiobutton->widget.h = 1; +} + +static void txt_radio_button_drawer(TXT_UNCAST_ARG(radiobutton)) +{ + TXT_CAST_ARG(txt_radiobutton_t, radiobutton); + txt_saved_colors_t colors; + int i; + int w; + + w = radiobutton->widget.w; + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + txt_draw_string("("); + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + + if (*radiobutton->variable == radiobutton->value) + { + txt_draw_code_page_string("\x07"); + } + else + { + txt_draw_string(" "); + } + + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + + txt_draw_string(") "); + + txt_restore_colours(&colors); + txt_set_widget_bg(radiobutton); + + txt_draw_string(radiobutton->label); + + for (i = txt_utf8_strlen(radiobutton->label); i < w - 5; ++i) + { + txt_draw_string(" "); + } +} + +static void txt_radio_button_destructor(TXT_UNCAST_ARG(radiobutton)) +{ + TXT_CAST_ARG(txt_radiobutton_t, radiobutton); + + free(radiobutton->label); +} + +static int txt_radio_button_keypress(TXT_UNCAST_ARG(radiobutton), int key) +{ + TXT_CAST_ARG(txt_radiobutton_t, radiobutton); + + if (key == KEY_ENTER || key == ' ') + { + if (*radiobutton->variable != radiobutton->value) + { + *radiobutton->variable = radiobutton->value; + txt_emit_signal(radiobutton, "selected"); + } + + return 1; + } + + return 0; +} + +static void txt_radio_button_mousepress(TXT_UNCAST_ARG(radiobutton), int x, + int y, int b) +{ + TXT_CAST_ARG(txt_radiobutton_t, radiobutton); + + if (b == TXT_MOUSE_LEFT) + { + /* Equivalent to pressing enter */ + + txt_radio_button_keypress(radiobutton, KEY_ENTER); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_radiobutton_t *txt_new_radio_button(const char *label, int *variable, + int value) +{ + txt_radiobutton_t *radiobutton; + + radiobutton = malloc(sizeof(txt_radiobutton_t)); + + txt_init_widget(radiobutton, &txt_radiobutton_class); + radiobutton->label = strdup(label); + radiobutton->variable = variable; + radiobutton->value = value; + + return radiobutton; +} + +#if 0 /* UNUSED */ +static void txt_set_radio_button_label(txt_radiobutton_t *radiobutton, + const char *value) +{ + free(radiobutton->label); + radiobutton->label = strdup(value); +} +#endif diff --git a/games/NXDoom/textscreen/txt_radiobutton.h b/games/NXDoom/textscreen/txt_radiobutton.h new file mode 100644 index 00000000000..f5cafb6c883 --- /dev/null +++ b/games/NXDoom/textscreen/txt_radiobutton.h @@ -0,0 +1,77 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_radiobutton.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_RADIOBUTTON_H +#define TXT_RADIOBUTTON_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* A radio button widget. + * + * Radio buttons are typically used in groups, to allow a value to be + * selected from a range of options. Each radio button corresponds + * to a particular option that may be selected. A radio button + * has an indicator to indicate whether it is the currently-selected + * value, and a text label. + * + * Internally, a radio button tracks an integer variable that may take + * a range of different values. Each radio button has a particular + * value associated with it; if the variable is equal to that value, + * the radio button appears selected. If a radio button is pressed + * by the user through the GUI, the variable is set to its value. + * + * When a radio button is selected, the "selected" signal is emitted. + */ + +struct txt_radiobutton_s +{ + txt_widget_t widget; + char *label; + int *variable; + int value; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new radio button widget. + * + * @param label The label to display next to the radio button + * (UTF-8 format). + * @param variable Pointer to the variable tracking whether this + * radio button is selected. + * @param value If the variable is equal to this value, the + * radio button appears selected. + * @return Pointer to the new radio button widget. + */ + +txt_radiobutton_t *txt_new_radio_button(const char *label, int *variable, + int value); + +#endif /* TXT_RADIOBUTTON_H */ diff --git a/games/NXDoom/textscreen/txt_scrollpane.c b/games/NXDoom/textscreen/txt_scrollpane.c new file mode 100644 index 00000000000..4fa3b718499 --- /dev/null +++ b/games/NXDoom/textscreen/txt_scrollpane.c @@ -0,0 +1,638 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_scrollpane.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_scrollpane.h" +#include "txt_table.h" + +#include "doomkeys.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SCROLLBAR_VERTICAL (1 << 0) +#define SCROLLBAR_HORIZONTAL (1 << 1) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int txt_scroll_pane_selectable(TXT_UNCAST_ARG(scrollpane)); +static void txt_scroll_pane_size_calc(TXT_UNCAST_ARG(scrollpane)); +static void txt_scroll_pane_drawer(TXT_UNCAST_ARG(scrollpane)); +static int txt_scroll_pane_keypress(TXT_UNCAST_ARG(scrollpane), int key); +static void txt_scroll_pane_destructor(TXT_UNCAST_ARG(scrollpane)); +static void txt_scroll_pane_mousepress(TXT_UNCAST_ARG(scrollpane), int x, + int y, int b); +static void txt_scroll_pane_layout(TXT_UNCAST_ARG(scrollpane)); +static void txt_scroll_pane_focus(TXT_UNCAST_ARG(scrollpane), int focused); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_scrollpane_class = +{ + txt_scroll_pane_selectable, txt_scroll_pane_size_calc, + txt_scroll_pane_drawer, txt_scroll_pane_keypress, + txt_scroll_pane_destructor, txt_scroll_pane_mousepress, + txt_scroll_pane_layout, txt_scroll_pane_focus, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int full_width(txt_scrollpane_t *scrollpane) +{ + if (scrollpane->child != NULL) + { + return scrollpane->child->w; + } + else + { + return 0; + } +} + +static int full_height(txt_scrollpane_t *scrollpane) +{ + if (scrollpane->child != NULL) + { + return scrollpane->child->h; + } + else + { + return 0; + } +} + +/* Calculate which scroll bars the pane needs. */ + +static int needs_scroll_bars(txt_scrollpane_t *scrollpane) +{ + int result; + + result = 0; + + if (full_width(scrollpane) > scrollpane->w) + { + result |= SCROLLBAR_HORIZONTAL; + } + + if (full_height(scrollpane) > scrollpane->h) + { + result |= SCROLLBAR_VERTICAL; + } + + return result; +} + +/* If a scrollbar isn't needed, the scroll position is reset. */ + +static void sanity_check_scrollbars(txt_scrollpane_t *scrollpane) +{ + int scrollbars; + int max_x; + int max_y; + + scrollbars = needs_scroll_bars(scrollpane); + + if ((scrollbars & SCROLLBAR_HORIZONTAL) == 0) + { + scrollpane->x = 0; + } + + if ((scrollbars & SCROLLBAR_VERTICAL) == 0) + { + scrollpane->y = 0; + } + + max_x = full_width(scrollpane) - scrollpane->w; + max_y = full_height(scrollpane) - scrollpane->h; + + if (scrollpane->x < 0) + { + scrollpane->x = 0; + } + else if (scrollpane->x > max_x) + { + scrollpane->x = max_x; + } + + if (scrollpane->y < 0) + { + scrollpane->y = 0; + } + else if (scrollpane->y > max_y) + { + scrollpane->y = max_y; + } +} + +static void txt_scroll_pane_size_calc(TXT_UNCAST_ARG(scrollpane)) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + int scrollbars; + + if (scrollpane->child != NULL) + { + txt_calc_widget_size(scrollpane->child); + } + + /* Expand as necessary (to ensure that no scrollbars are needed)? */ + + if (scrollpane->expand_w) + { + scrollpane->w = full_width(scrollpane); + } + + if (scrollpane->expand_h) + { + scrollpane->h = full_height(scrollpane); + } + + scrollpane->widget.w = scrollpane->w; + scrollpane->widget.h = scrollpane->h; + + /* If we have scroll bars, we need to expand slightly to + * accommodate them. Eg. if we have a vertical scrollbar, we + * need to be an extra character wide. + */ + + scrollbars = needs_scroll_bars(scrollpane); + + if (scrollbars & SCROLLBAR_HORIZONTAL) + { + ++scrollpane->widget.h; + } + + if (scrollbars & SCROLLBAR_VERTICAL) + { + ++scrollpane->widget.w; + } + + if (scrollpane->child != NULL) + { + if (scrollpane->child->w < scrollpane->w) + { + scrollpane->child->w = scrollpane->w; + } + + if (scrollpane->child->h < scrollpane->h) + { + scrollpane->child->h = scrollpane->h; + } + } +} + +static void txt_scroll_pane_drawer(TXT_UNCAST_ARG(scrollpane)) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + int x1; + int y1; + int x2; + int y2; + int scrollbars; + + /* We set a clipping area of the scroll pane. */ + + x1 = scrollpane->widget.x; + y1 = scrollpane->widget.y; + x2 = x1 + scrollpane->w; + y2 = y1 + scrollpane->h; + + scrollbars = needs_scroll_bars(scrollpane); + + if (scrollbars & SCROLLBAR_HORIZONTAL) + { + txt_draw_horiz_scrollbar(x1, y1 + scrollpane->h, scrollpane->w, + scrollpane->x, + full_width(scrollpane) - scrollpane->w); + } + + if (scrollbars & SCROLLBAR_VERTICAL) + { + txt_draw_vert_scrollbar(x1 + scrollpane->w, y1, scrollpane->h, + scrollpane->y, + full_height(scrollpane) - scrollpane->h); + } + + txt_push_clip_area(x1, x2, y1, y2); + + /* Draw the child widget */ + + if (scrollpane->child != NULL) + { + txt_draw_widget(scrollpane->child); + } + + /* Restore old clipping area. */ + + txt_pop_clip_area(); +} + +static void txt_scroll_pane_destructor(TXT_UNCAST_ARG(scrollpane)) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + + if (scrollpane->child != NULL) + { + txt_destroy_widget(scrollpane->child); + } +} + +static void txt_scroll_pane_focus(TXT_UNCAST_ARG(scrollpane), int focused) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + + /* Whether the child is focused depends only on whether the scroll pane + * itself is focused. Pass through focus to the child. + */ + + if (scrollpane->child != NULL) + { + txt_set_widget_focus(scrollpane->child, focused); + } +} + +/* Hack for tables - when browsing a table inside a scroll pane, + * automatically scroll the window to show the newly-selected + * item. + */ + +static void show_selected_widget(txt_scrollpane_t *scrollpane) +{ + txt_widget_t *selected; + + selected = txt_get_selected_widget(scrollpane->child); + + /* Scroll up or down? */ + + if (selected->y <= scrollpane->widget.y) + { + scrollpane->y -= scrollpane->widget.y - selected->y; + } + else if ((signed)(selected->y + selected->h) > + (signed)(scrollpane->widget.y + scrollpane->h)) + { + scrollpane->y += (selected->y + selected->h) - + (scrollpane->widget.y + scrollpane->h); + } + + /* Scroll left or right? */ + + if (selected->x <= scrollpane->widget.x) + { + scrollpane->x -= scrollpane->widget.x - selected->x; + } + else if ((signed)(selected->x + selected->w) > + (signed)(scrollpane->widget.x + scrollpane->w)) + { + scrollpane->x += (selected->x + selected->w) - + (scrollpane->widget.x + scrollpane->w); + } +} + +/* Another hack for tables - when scrolling in 'pages', the normal key press + * event does not provide children with enough information to know how far + * to move their selection to reach a new page. This function does so. + * Note that it *only* affects scrolling in pages, not with arrows! + * A side-effect of this, rather than 'pulling' the selection to fit within + * the new page, is that we will jump straight over ranges of unselectable + * items longer than a page, but that is also true of arrow-key scrolling. + * The other unfortunate effect of doing things this way is that page keys + * have no effect on tables _not_ in scrollpanes: not even home/end. + */ + +static int page_selected_widget(txt_scrollpane_t *scrollpane, int key) +{ + int pagex = 0; /* No page left/right yet, but some keyboards have them */ + int pagey = 0; + + /* Subtract one from the absolute page distance as this is slightly more + * intuitive: a page down first jumps to the bottom of the current page, + * then proceeds to scroll onwards. + */ + + switch (key) + { + case KEY_PGUP: + pagey = 1 - scrollpane->h; + break; + + case KEY_PGDN: + pagey = scrollpane->h - 1; + break; + + default: /* We shouldn't even be in this function */ + return 0; + } + + if (scrollpane->child->widget_class == &txt_table_class) + { + return txt_page_table(scrollpane->child, pagex, pagey); + } + + return 0; +} + +/* Interpret arrow key presses as scroll commands */ + +static int interpret_scroll_key(txt_scrollpane_t *scrollpane, int key) +{ + int maxy; + + switch (key) + { + case KEY_UPARROW: + if (scrollpane->y > 0) + { + --scrollpane->y; + return 1; + } + break; + + case KEY_DOWNARROW: + if (scrollpane->y < full_height(scrollpane) - scrollpane->h) + { + ++scrollpane->y; + return 1; + } + break; + + case KEY_LEFTARROW: + if (scrollpane->x > 0) + { + --scrollpane->x; + return 1; + } + break; + + case KEY_RIGHTARROW: + if (scrollpane->x < full_width(scrollpane) - scrollpane->w) + { + ++scrollpane->x; + return 1; + } + break; + + case KEY_PGUP: + if (scrollpane->y > 0) + { + scrollpane->y -= scrollpane->h; + if (scrollpane->y < 0) + { + scrollpane->y = 0; + } + + return 1; + } + break; + + case KEY_PGDN: + maxy = full_height(scrollpane) - scrollpane->h; + if (scrollpane->y < maxy) + { + scrollpane->y += scrollpane->h; + if (scrollpane->y > maxy) + { + scrollpane->y = maxy; + } + + return 1; + } + break; + + default: + break; + } + + return 0; +} + +static int txt_scroll_pane_keypress(TXT_UNCAST_ARG(scrollpane), int key) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + int result; + + result = 0; + + if (scrollpane->child != NULL) + { + result = txt_widget_key_press(scrollpane->child, key); + + /* Gross hack - if we're scrolling in a menu with the keyboard, + * automatically move the scroll pane to show the new + * selected item. + */ + + if ((key == KEY_UPARROW || key == KEY_DOWNARROW || + key == KEY_LEFTARROW || key == KEY_RIGHTARROW || + key == KEY_PGUP || key == KEY_PGDN || key == KEY_TAB) && + scrollpane->child->widget_class == &txt_table_class) + { + if (page_selected_widget(scrollpane, key)) + { + result = 1; + } + + show_selected_widget(scrollpane); + } + + /* If the child widget didn't use the keypress, we can see + * if it can be interpreted as a scrolling command. + */ + + if (result == 0) + { + result = interpret_scroll_key(scrollpane, key); + } + } + + return result; +} + +static void txt_scroll_pane_mousepress(TXT_UNCAST_ARG(scrollpane), int x, + int y, int b) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + int scrollbars; + int rel_x; + int rel_y; + + scrollbars = needs_scroll_bars(scrollpane); + + if (b == TXT_MOUSE_SCROLLUP) + { + if (scrollbars & SCROLLBAR_VERTICAL) + { + scrollpane->y -= 3; + } + else if (scrollbars & SCROLLBAR_HORIZONTAL) + { + scrollpane->x -= 3; + } + + return; + } + else if (b == TXT_MOUSE_SCROLLDOWN) + { + if (scrollbars & SCROLLBAR_VERTICAL) + { + scrollpane->y += 3; + } + else if (scrollbars & SCROLLBAR_HORIZONTAL) + { + scrollpane->x += 3; + } + + return; + } + + rel_x = x - scrollpane->widget.x; + rel_y = y - scrollpane->widget.y; + + /* Click on the horizontal scrollbar? */ + + if ((scrollbars & SCROLLBAR_HORIZONTAL) && rel_y == scrollpane->h) + { + if (rel_x == 0) + { + --scrollpane->x; + } + else if (rel_x == scrollpane->w - 1) + { + ++scrollpane->x; + } + else + { + int range = full_width(scrollpane) - scrollpane->w; + int bar_max = scrollpane->w - 3; + + scrollpane->x = ((rel_x - 1) * range + bar_max - 1) / bar_max; + } + + return; + } + + /* Click on the vertical scrollbar? */ + + if ((scrollbars & SCROLLBAR_VERTICAL) && rel_x == scrollpane->w) + { + if (rel_y == 0) + { + --scrollpane->y; + } + else if (rel_y == scrollpane->h - 1) + { + ++scrollpane->y; + } + else + { + int range = full_height(scrollpane) - scrollpane->h; + int bar_max = scrollpane->h - 3; + + scrollpane->y = ((rel_y - 1) * range + bar_max - 1) / bar_max; + } + + return; + } + + if (scrollpane->child != NULL) + { + txt_widget_mouse_press(scrollpane->child, x, y, b); + } +} + +static void txt_scroll_pane_layout(TXT_UNCAST_ARG(scrollpane)) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + + sanity_check_scrollbars(scrollpane); + + /* The child widget takes the same position as the scroll pane + * itself, but is offset by the scroll position. + */ + + if (scrollpane->child != NULL) + { + scrollpane->child->x = scrollpane->widget.x - scrollpane->x; + scrollpane->child->y = scrollpane->widget.y - scrollpane->y; + + txt_layout_widget(scrollpane->child); + } +} + +static int txt_scroll_pane_selectable(TXT_UNCAST_ARG(scrollpane)) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + + /* If scroll bars are displayed, the scroll pane must be selectable + * so that we can use the arrow keys to scroll around. + */ + + if (needs_scroll_bars(scrollpane)) + { + return 1; + } + + /* Otherwise, whether this is selectable depends on the child widget. */ + + return txt_selectable_widget(scrollpane->child); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_scrollpane_t *txt_new_scrollpane(int w, int h, TXT_UNCAST_ARG(target)) +{ + TXT_CAST_ARG(txt_widget_t, target); + txt_scrollpane_t *scrollpane; + + scrollpane = malloc(sizeof(txt_scrollpane_t)); + txt_init_widget(scrollpane, &txt_scrollpane_class); + scrollpane->w = w; + scrollpane->h = h; + scrollpane->x = 0; + scrollpane->y = 0; + scrollpane->child = target; + scrollpane->expand_w = w <= 0; + scrollpane->expand_h = h <= 0; + + /* Set parent pointer for inner widget. */ + + target->parent = &scrollpane->widget; + + return scrollpane; +} diff --git a/games/NXDoom/textscreen/txt_scrollpane.h b/games/NXDoom/textscreen/txt_scrollpane.h new file mode 100644 index 00000000000..831a014d9d7 --- /dev/null +++ b/games/NXDoom/textscreen/txt_scrollpane.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_scrollpane.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * + ****************************************************************************/ + +#ifndef TXT_SCROLLPANE_H +#define TXT_SCROLLPANE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Scrollable pane widget. + * + * A scrollable pane widget is a widget that contains another widget + * that is larger than it. Scroll bars appear on the side to allow + * different areas of the contained widget to be seen. + */ + +struct txt_scrollpane_s +{ + txt_widget_t widget; + int w; + int h; + int x; + int y; + int expand_w; + int expand_h; + txt_widget_t *child; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new scroll pane widget. + * + * @param w Width of the scroll pane, in characters. + * @param h Height of the scroll pane, in lines. + * @param target The target widget that the scroll pane will + * contain. + * @return Pointer to the new scroll pane widget. + */ + +txt_scrollpane_t *txt_new_scrollpane(int w, int h, TXT_UNCAST_ARG(target)); + +#endif /* TXT_SCROLLPANE_H */ diff --git a/games/NXDoom/textscreen/txt_sdl.c b/games/NXDoom/textscreen/txt_sdl.c new file mode 100644 index 00000000000..6041ab18728 --- /dev/null +++ b/games/NXDoom/textscreen/txt_sdl.c @@ -0,0 +1,262 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_sdl.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * + * Text mode emulation in SDL + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_main.h" +#include "txt_sdl.h" +#include "txt_utf8.h" + +/* Fonts: */ + +#include "txt_font.h" +#include "fonts/codepage.h" +#include "fonts/large.h" +#include "fonts/normal.h" +#include "fonts/small.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Time between character blinks in ms */ + +#define BLINK_PERIOD 250 + +/* XXX: duplicate from doomtype.h */ + +#define arrlen(array) (sizeof(array) / sizeof(*array)) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static unsigned char *screendata; + +/* Unicode key mapping; see codepage.h. */ + +static const short g_code_page_to_unicode[] = CODE_PAGE_TO_UNICODE; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Searches the desktop screen buffer to determine whether there are any + * blinking characters. + */ + +static int txt_has_blinking_chars(void) +{ + int x; + int y; + unsigned char *p; + + /* Check all characters in screen buffer */ + + for (y = 0; y < TXT_SCREEN_H; ++y) + { + for (x = 0; x < TXT_SCREEN_W; ++x) + { + p = &screendata[(y * TXT_SCREEN_W + x) * 2]; + + if (p[1] & 0x80) + { + return 1; /* This character is blinking */ + } + } + } + + /* None found */ + + return 0; +} + +static void txt_string_concat(char *dest, const char *src, size_t dest_len) +{ + size_t offset; + + offset = strlen(dest); + if (offset > dest_len) + { + offset = dest_len; + } + + txt_string_copy(dest + offset, src, dest_len - offset); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Initialize text mode screen + * + * Returns 1 if successful, 0 if an error occurred + */ + +int txt_init(void) +{ + return 1; +} + +void txt_shutdown(void) +{ +} + +void txt_set_colour(txt_color_t color, int r, int g, int b) +{ +} + +unsigned char *txt_get_screen_data(void) +{ + return screendata; +} + +void txt_update_screen_area(int x, int y, int w, int h) +{ +} + +void txt_update_screen(void) +{ + txt_update_screen_area(0, 0, TXT_SCREEN_W, TXT_SCREEN_H); +} + +void txt_get_mouse_position(int *x, int *y) +{ +} + +signed int txt_getchar(void) +{ + return -1; +} + +int txt_get_modifier_state(txt_modifier_t mod) +{ + return 0; +} + +int txt_unicode_character(unsigned int c) +{ + unsigned int i; + + /* Check the code page mapping to see if this character maps + * to anything. + */ + + for (i = 0; i < arrlen(g_code_page_to_unicode); ++i) + { + if (g_code_page_to_unicode[i] == c) + { + return i; + } + } + + return -1; +} + +void txt_get_key_description(int key, char *buf, size_t buf_len) +{ +} + +/* Sleeps until an event is received, the screen needs to be redrawn, + * or until timeout expires (if timeout != 0) + */ + +void txt_sleep(int timeout) +{ +} + +void txt_set_input_mode(txt_input_mode_t mode) +{ +} + +void txt_set_window_title(const char *title) +{ +} + +void txt_sdl_set_event_callback(void *user_data) +{ +} + +/* Safe string functions. */ + +void txt_string_copy(char *dest, const char *src, size_t dest_len) +{ + if (dest_len < 1) + { + return; + } + + dest[dest_len - 1] = '\0'; + strncpy(dest, src, dest_len - 1); +} + +/* Safe, portable vsnprintf(). */ + +int txt_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args) +{ + int result; + + if (buf_len < 1) + { + return 0; + } + + /* Windows (and other OSes?) has a vsnprintf() that doesn't always + * append a trailing \0. So we must do it, and write into a buffer + * that is one byte shorter; otherwise this function is unsafe. + */ + + result = vsnprintf(buf, buf_len, s, args); + + /* If truncated, change the final char in the buffer to a \0. + * A negative result indicates a truncated buffer on Windows. + */ + + if (result < 0 || result >= buf_len) + { + buf[buf_len - 1] = '\0'; + result = buf_len - 1; + } + + return result; +} + +/* Safe, portable snprintf(). */ + +int txt_snprintf(char *buf, size_t buf_len, const char *s, ...) +{ + va_list args; + int result; + va_start(args, s); + result = txt_vsnprintf(buf, buf_len, s, args); + va_end(args); + return result; +} diff --git a/games/NXDoom/textscreen/txt_sdl.h b/games/NXDoom/textscreen/txt_sdl.h new file mode 100644 index 00000000000..1de6af32958 --- /dev/null +++ b/games/NXDoom/textscreen/txt_sdl.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_sdl.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + * Text mode emulation in SDL + * + ****************************************************************************/ + +#ifndef TXT_SDL_H +#define TXT_SDL_H + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Set a callback function to call in the SDL event loop. Useful for + * intercepting events. Pass callback=NULL to clear an existing + * callback function. + * user_data is a void pointer to be passed to the callback function. + */ + +void txt_sdl_set_event_callback(void *user_data); + +#endif /* TXT_SDL_H */ diff --git a/games/NXDoom/textscreen/txt_separator.c b/games/NXDoom/textscreen/txt_separator.c new file mode 100644 index 00000000000..fc0e1f6eb15 --- /dev/null +++ b/games/NXDoom/textscreen/txt_separator.c @@ -0,0 +1,145 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_separator.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_separator.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_separator_size_calc(TXT_UNCAST_ARG(separator)); +static void txt_separator_drawer(TXT_UNCAST_ARG(separator)); +static void txt_separator_destructor(TXT_UNCAST_ARG(separator)); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_separator_class = +{ + txt_never_selectable, + txt_separator_size_calc, + txt_separator_drawer, + NULL, + txt_separator_destructor, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_separator_size_calc(TXT_UNCAST_ARG(separator)) +{ + TXT_CAST_ARG(txt_separator_t, separator); + + if (separator->label != NULL) + { + /* Minimum width is the string length + two spaces for padding */ + + separator->widget.w = txt_utf8_strlen(separator->label) + 2; + } + else + { + separator->widget.w = 0; + } + + separator->widget.h = 1; +} + +static void txt_separator_drawer(TXT_UNCAST_ARG(separator)) +{ + TXT_CAST_ARG(txt_separator_t, separator); + int x; + int y; + int w; + + w = separator->widget.w; + + txt_get_xy(&x, &y); + + /* Draw separator. Go back one character and draw two extra + * to overlap the window borders. + */ + + txt_draw_separator(x - 2, y, w + 4); + + if (separator->label != NULL) + { + txt_goto_xy(x, y); + + txt_fgcolour(TXT_COLOR_BRIGHT_GREEN); + txt_draw_string(" "); + txt_draw_string(separator->label); + txt_draw_string(" "); + } +} + +static void txt_separator_destructor(TXT_UNCAST_ARG(separator)) +{ + TXT_CAST_ARG(txt_separator_t, separator); + + free(separator->label); +} + +static void txt_set_separator_label(txt_separator_t *separator, + const char *label) +{ + free(separator->label); + + if (label != NULL) + { + separator->label = strdup(label); + } + else + { + separator->label = NULL; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_separator_t *txt_new_separator(const char *label) +{ + txt_separator_t *separator; + + separator = malloc(sizeof(txt_separator_t)); + + txt_init_widget(separator, &txt_separator_class); + + separator->label = NULL; + txt_set_separator_label(separator, label); + + return separator; +} diff --git a/games/NXDoom/textscreen/txt_separator.h b/games/NXDoom/textscreen/txt_separator.h new file mode 100644 index 00000000000..79be2f248c0 --- /dev/null +++ b/games/NXDoom/textscreen/txt_separator.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_separator.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_SEPARATOR_H +#define TXT_SEPARATOR_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Horizontal separator. + * + * A horizontal separator appears as a horizontal line divider across + * the length of the window in which it is added. An optional label + * allows the separator to be used as a section divider for grouping + * related controls. + */ + +struct txt_separator_s +{ + txt_widget_t widget; + char *label; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern txt_widget_class_t txt_separator_class; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new horizontal separator widget. + * + * @param label Label to display on the separator (UTF-8 format). + * If this is set to NULL, no label is displayed. + * @return The new separator widget. + */ + +txt_separator_t *txt_new_separator(const char *label); + +#endif /* TXT_SEPARATOR_H */ diff --git a/games/NXDoom/textscreen/txt_spinctrl.c b/games/NXDoom/textscreen/txt_spinctrl.c new file mode 100644 index 00000000000..2838cb49b9a --- /dev/null +++ b/games/NXDoom/textscreen/txt_spinctrl.c @@ -0,0 +1,460 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_spinctrl.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_spinctrl.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_spin_control_size_calc(TXT_UNCAST_ARG(spincontrol)); +static void txt_spin_control_drawer(TXT_UNCAST_ARG(spincontrol)); +static int txt_spincontrol_keypress(TXT_UNCAST_ARG(spincontrol), int key); +static void txt_spin_control_destructor(TXT_UNCAST_ARG(spincontrol)); +static void txt_spincontrol_mousepress(TXT_UNCAST_ARG(spincontrol), int x, + int y, int b); +static void txt_spincontrol_focused(TXT_UNCAST_ARG(spincontrol), + int focused); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_spincontrol_class = +{ + txt_always_selectable, + txt_spin_control_size_calc, + txt_spin_control_drawer, + txt_spincontrol_keypress, + txt_spin_control_destructor, + txt_spincontrol_mousepress, + NULL, + txt_spincontrol_focused, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Generate the format string to be used for displaying floats */ + +static void float_format_string(float step, char *buf, size_t buf_len) +{ + int precision; + + precision = (int)ceil(-log(step) / log(10)); + + if (precision > 0) + { + txt_snprintf(buf, buf_len, "%%.%if", precision); + } + else + { + txt_string_copy(buf, "%.1f", buf_len); + } +} + +/* Number of characters needed to represent a character */ + +static unsigned int int_width(int val) +{ + char buf[25]; + + txt_snprintf(buf, sizeof(buf), "%i", val); + + return strlen(buf); +} + +static unsigned int float_width(float val, float step) +{ + unsigned int precision; + unsigned int result; + + /* Calculate the width of the int value */ + + result = int_width((int)val); + + /* Add a decimal part if the precision specifies it */ + + precision = (unsigned int)ceil(-log(step) / log(10)); + + if (precision > 0) + { + result += precision + 1; + } + + return result; +} + +/* Returns the minimum width of the input box */ + +static unsigned int spin_control_width(txt_spincontrol_t *spincontrol) +{ + unsigned int minw; + unsigned int maxw; + + switch (spincontrol->type) + { + case TXT_SPINCONTROL_FLOAT: + minw = float_width(spincontrol->min.f, spincontrol->step.f); + maxw = float_width(spincontrol->max.f, spincontrol->step.f); + break; + + default: + case TXT_SPINCONTROL_INT: + minw = int_width(spincontrol->min.i); + maxw = int_width(spincontrol->max.i); + break; + } + + /* Choose the wider of the two values. Add one so that there is always + * space for the cursor when editing. + */ + + if (minw > maxw) + { + return minw; + } + else + { + return maxw; + } +} + +static void txt_spin_control_size_calc(TXT_UNCAST_ARG(spincontrol)) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + + spincontrol->widget.w = spin_control_width(spincontrol) + 5; + spincontrol->widget.h = 1; +} + +static void set_buffer(txt_spincontrol_t *spincontrol) +{ + char format[25]; + + switch (spincontrol->type) + { + case TXT_SPINCONTROL_INT: + txt_snprintf(spincontrol->buffer, spincontrol->buffer_len, "%i", + spincontrol->value->i); + break; + + case TXT_SPINCONTROL_FLOAT: + float_format_string(spincontrol->step.f, format, sizeof(format)); + txt_snprintf(spincontrol->buffer, spincontrol->buffer_len, format, + spincontrol->value->f); + break; + } +} + +static void txt_spin_control_drawer(TXT_UNCAST_ARG(spincontrol)) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + unsigned int i; + unsigned int padding; + txt_saved_colors_t colors; + int bw; + int focused; + + focused = spincontrol->widget.focused; + + txt_save_colours(&colors); + + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + txt_draw_code_page_string("\x1b "); + + txt_restore_colours(&colors); + + /* Choose background color */ + + if (focused && spincontrol->editing) + { + txt_bgcolour(TXT_COLOR_BLACK, 0); + } + else + { + txt_set_widget_bg(spincontrol); + } + + if (!spincontrol->editing) + { + set_buffer(spincontrol); + } + + i = 0; + + bw = txt_utf8_strlen(spincontrol->buffer); + padding = spincontrol->widget.w - bw - 4; + + while (i < padding) + { + txt_draw_string(" "); + ++i; + } + + txt_draw_string(spincontrol->buffer); + i += bw; + + while (i < spincontrol->widget.w - 4) + { + txt_draw_string(" "); + ++i; + } + + txt_restore_colours(&colors); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + txt_draw_code_page_string(" \x1a"); +} + +static void txt_spin_control_destructor(TXT_UNCAST_ARG(spincontrol)) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + + free(spincontrol->buffer); +} + +static void add_character(txt_spincontrol_t *spincontrol, int key) +{ + if (txt_utf8_strlen(spincontrol->buffer) < + spin_control_width(spincontrol) && + strlen(spincontrol->buffer) < spincontrol->buffer_len - 2) + { + spincontrol->buffer[strlen(spincontrol->buffer) + 1] = '\0'; + spincontrol->buffer[strlen(spincontrol->buffer)] = key; + } +} + +static void backspace(txt_spincontrol_t *spincontrol) +{ + if (txt_utf8_strlen(spincontrol->buffer) > 0) + { + spincontrol->buffer[strlen(spincontrol->buffer) - 1] = '\0'; + } +} + +static void enforce_limits(txt_spincontrol_t *spincontrol) +{ + switch (spincontrol->type) + { + case TXT_SPINCONTROL_INT: + if (spincontrol->value->i > spincontrol->max.i) + spincontrol->value->i = spincontrol->max.i; + else if (spincontrol->value->i < spincontrol->min.i) + spincontrol->value->i = spincontrol->min.i; + break; + + case TXT_SPINCONTROL_FLOAT: + if (spincontrol->value->f > spincontrol->max.f) + spincontrol->value->f = spincontrol->max.f; + else if (spincontrol->value->f < spincontrol->min.f) + spincontrol->value->f = spincontrol->min.f; + break; + } +} + +static void finish_editing(txt_spincontrol_t *spincontrol) +{ + switch (spincontrol->type) + { + case TXT_SPINCONTROL_INT: + spincontrol->value->i = atoi(spincontrol->buffer); + break; + + case TXT_SPINCONTROL_FLOAT: + spincontrol->value->f = (float)atof(spincontrol->buffer); + break; + } + + spincontrol->editing = 0; + enforce_limits(spincontrol); +} + +static int txt_spincontrol_keypress(TXT_UNCAST_ARG(spincontrol), int key) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + + /* Enter to enter edit mode */ + + if (spincontrol->editing) + { + if (key == KEY_ENTER) + { + finish_editing(spincontrol); + return 1; + } + + if (key == KEY_ESCAPE) + { + /* Abort without saving value */ + + spincontrol->editing = 0; + return 1; + } + + if (isdigit(key) || key == '-' || key == '.') + { + add_character(spincontrol, key); + return 1; + } + + if (key == KEY_BACKSPACE) + { + backspace(spincontrol); + return 1; + } + } + else + { + /* Non-editing mode */ + + if (key == KEY_ENTER) + { + spincontrol->editing = 1; + txt_string_copy(spincontrol->buffer, "", spincontrol->buffer_len); + return 1; + } + + if (key == KEY_LEFTARROW) + { + switch (spincontrol->type) + { + case TXT_SPINCONTROL_INT: + spincontrol->value->i -= spincontrol->step.i; + break; + + case TXT_SPINCONTROL_FLOAT: + spincontrol->value->f -= spincontrol->step.f; + break; + } + + enforce_limits(spincontrol); + + return 1; + } + + if (key == KEY_RIGHTARROW) + { + switch (spincontrol->type) + { + case TXT_SPINCONTROL_INT: + spincontrol->value->i += spincontrol->step.i; + break; + + case TXT_SPINCONTROL_FLOAT: + spincontrol->value->f += spincontrol->step.f; + break; + } + + enforce_limits(spincontrol); + + return 1; + } + } + + return 0; +} + +static void txt_spincontrol_mousepress(TXT_UNCAST_ARG(spincontrol), int x, + int y, int b) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + unsigned int rel_x; + + rel_x = x - spincontrol->widget.x; + + if (rel_x < 2) + { + txt_spincontrol_keypress(spincontrol, KEY_LEFTARROW); + } + else if (rel_x >= spincontrol->widget.w - 2) + { + txt_spincontrol_keypress(spincontrol, KEY_RIGHTARROW); + } +} + +static void txt_spincontrol_focused(TXT_UNCAST_ARG(spincontrol), int focused) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + + finish_editing(spincontrol); +} + +static txt_spincontrol_t *txt_base_spincontrol(void) +{ + txt_spincontrol_t *spincontrol; + + spincontrol = malloc(sizeof(txt_spincontrol_t)); + + txt_init_widget(spincontrol, &txt_spincontrol_class); + spincontrol->buffer_len = 25; + spincontrol->buffer = malloc(spincontrol->buffer_len); + txt_string_copy(spincontrol->buffer, "", spincontrol->buffer_len); + spincontrol->editing = 0; + + return spincontrol; +} + +txt_spincontrol_t *txt_newspin_control(int *value, int min, int max) +{ + txt_spincontrol_t *spincontrol; + + spincontrol = txt_base_spincontrol(); + spincontrol->type = TXT_SPINCONTROL_INT; + spincontrol->value = (void *)value; + spincontrol->min.i = min; + spincontrol->max.i = max; + spincontrol->step.i = 1; + set_buffer(spincontrol); + + return spincontrol; +} + +txt_spincontrol_t *txt_new_float_spincontrol(float *value, float min, + float max) +{ + txt_spincontrol_t *spincontrol; + + spincontrol = txt_base_spincontrol(); + spincontrol->type = TXT_SPINCONTROL_FLOAT; + spincontrol->value = (void *)value; + spincontrol->min.f = min; + spincontrol->max.f = max; + spincontrol->step.f = 0.1f; + set_buffer(spincontrol); + + return spincontrol; +} diff --git a/games/NXDoom/textscreen/txt_spinctrl.h b/games/NXDoom/textscreen/txt_spinctrl.h new file mode 100644 index 00000000000..d6f663ed0ee --- /dev/null +++ b/games/NXDoom/textscreen/txt_spinctrl.h @@ -0,0 +1,94 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_spinctrl.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_SPINCONTROL_H +#define TXT_SPINCONTROL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef enum +{ + TXT_SPINCONTROL_INT, + TXT_SPINCONTROL_FLOAT, +} txt_spincontrol_type_t; + +union _float_n_int +{ + float f; + int i; +}; + +/* Spin control widget. + * + * A spin control widget works as an input box that can be used to + * set numeric values, but also has buttons that allow its value + * to be increased or decreased. + */ + +struct txt_spincontrol_s +{ + txt_widget_t widget; + txt_spincontrol_type_t type; + union _float_n_int min; + union _float_n_int max; + union _float_n_int *value; + union _float_n_int step; + int editing; + char *buffer; + size_t buffer_len; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new spin control widget tracking an integer value. + * + * @param value Pointer to the variable containing the value + * displayed in the widget. + * @param min Minimum value that may be set. + * @param max Maximum value that may be set. + * @return Pointer to the new spin control widget. + */ + +txt_spincontrol_t *txt_newspin_control(int *value, int min, int max); + +/** + * Create a new spin control widget tracking a float value. + * + * @param value Pointer to the variable containing the value + * displayed in the widget. + * @param min Minimum value that may be set. + * @param max Maximum value that may be set. + * @return Pointer to the new spin control widget. + */ + +txt_spincontrol_t *txt_new_float_spincontrol(float *value, float min, + float max); + +#endif /* TXT_SPINCONTROL_H */ diff --git a/games/NXDoom/textscreen/txt_strut.c b/games/NXDoom/textscreen/txt_strut.c new file mode 100644 index 00000000000..e1da6647e68 --- /dev/null +++ b/games/NXDoom/textscreen/txt_strut.c @@ -0,0 +1,101 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_strut.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomkeys.h" + +#include "txt_io.h" +#include "txt_main.h" +#include "txt_strut.h" +#include "txt_window.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +static void txt_strut_size_calc(TXT_UNCAST_ARG(strut)); +static void txt_strut_drawer(TXT_UNCAST_ARG(strut)); +static void txt_strut_destructor(TXT_UNCAST_ARG(strut)); +static int txt_strut_keypress(TXT_UNCAST_ARG(strut), int key); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_strut_class = +{ + txt_never_selectable, + txt_strut_size_calc, + txt_strut_drawer, + txt_strut_keypress, + txt_strut_destructor, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_strut_size_calc(TXT_UNCAST_ARG(strut)) +{ + TXT_CAST_ARG(txt_strut_t, strut); + + /* Minimum width is the string length + two spaces for padding */ + + strut->widget.w = strut->width; + strut->widget.h = strut->height; +} + +static void txt_strut_drawer(TXT_UNCAST_ARG(strut)) +{ + /* Nothing is drawn for a strut. */ +} + +static void txt_strut_destructor(TXT_UNCAST_ARG(strut)) +{ +} + +static int txt_strut_keypress(TXT_UNCAST_ARG(strut), int key) +{ + return 0; +} + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +txt_strut_t *txt_new_strut(int width, int height) +{ + txt_strut_t *strut; + + strut = malloc(sizeof(txt_strut_t)); + + txt_init_widget(strut, &txt_strut_class); + strut->width = width; + strut->height = height; + + return strut; +} diff --git a/games/NXDoom/textscreen/txt_strut.h b/games/NXDoom/textscreen/txt_strut.h new file mode 100644 index 00000000000..7d988fb038d --- /dev/null +++ b/games/NXDoom/textscreen/txt_strut.h @@ -0,0 +1,60 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_strut.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_STRUT_H +#define TXT_STRUT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Strut widget. + * + * A strut is a widget that takes up a fixed amount of space. It can + * be visualised as a transparent box. Struts are used to provide + * spacing between widgets. + */ + +struct txt_strut_s +{ + txt_widget_t widget; + int width; + int height; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new strut. + * + * @param width Width of the strut, in characters. + * @param height Height of the strut, in characters. + */ + +txt_strut_t *txt_new_strut(int width, int height); + +#endif /* TXT_STRUT_H */ diff --git a/games/NXDoom/textscreen/txt_table.c b/games/NXDoom/textscreen/txt_table.c new file mode 100644 index 00000000000..3d77e3731d4 --- /dev/null +++ b/games/NXDoom/textscreen/txt_table.c @@ -0,0 +1,1330 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_table.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_desktop.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_separator.h" +#include "txt_strut.h" +#include "txt_table.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int txt_table_selectable(TXT_UNCAST_ARG(table)); +static void txt_calc_table_size(TXT_UNCAST_ARG(table)); +static void txt_table_drawer(TXT_UNCAST_ARG(table)); +static int txt_table_keypress(TXT_UNCAST_ARG(table), int key); +static void txt_table_destructor(TXT_UNCAST_ARG(table)); +static void txt_table_mouse_press(TXT_UNCAST_ARG(table), int x, + int y, int b); +static void txt_table_layout(TXT_UNCAST_ARG(table)); +static void txt_table_focused(TXT_UNCAST_ARG(table), int focused); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_t txt_table_overflow_right; +txt_widget_t txt_table_overflow_down; +txt_widget_t txt_table_eol; +txt_widget_t txt_table_empty; + +txt_widget_class_t txt_table_class = +{ + txt_table_selectable, txt_calc_table_size, txt_table_drawer, + txt_table_keypress, txt_table_destructor, txt_table_mouse_press, + txt_table_layout, txt_table_focused, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Returns true if the given widget in the table's widgets[] array refers + * to an actual widget - not NULL, or one of the special overflow pointers. + */ + +static int is_actual_widget(txt_widget_t *widget) +{ + return widget != NULL && widget != &txt_table_overflow_right && + widget != &txt_table_overflow_down; +} + +static void txt_table_destructor(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + + txt_clear_table(table); +} + +static int table_rows(txt_table_t *table) +{ + return (table->num_widgets + table->columns - 1) / table->columns; +} + +/* Most widgets occupy just one cell of a table, but if the special + * overflow constants are used, they can occupy multiple cells. + * This function figures out for a widget in a given cell, which + * cells it should actually occupy (always a rectangle). + */ + +static void cell_overflowed_size(txt_table_t *table, int x, int y, int *w, + int *h) +{ + txt_widget_t *widget; + int x1; + int y1; + + if (!is_actual_widget(table->widgets[y * table->columns + x])) + { + *w = 0; + *h = 0; + return; + } + + *w = table->columns - x; + *h = 0; + for (y1 = y; y1 < table_rows(table); ++y1) + { + /* Every overflow cell must point to either (x, y) or another + * overflow cell. This means the first in every row must be + * txt_table_overflow_down. + */ + + if (y1 * table->columns + x >= table->num_widgets) + { + break; + } + + widget = table->widgets[y1 * table->columns + x]; + + if (y1 != y && widget != &txt_table_overflow_down) + { + break; + } + + for (x1 = x + 1; x1 < x + *w; ++x1) + { + if (y1 * table->columns + x1 >= table->num_widgets) + { + break; + } + + /* Can be either type of overflow, except on the first row. + * Otherwise we impose a limit on the width. + */ + + widget = table->widgets[y1 * table->columns + x1]; + if (widget != &txt_table_overflow_right && + (widget != &txt_table_overflow_down || y1 == y)) + { + *w = x1 - x; + break; + } + } + + ++*h; + } +} + +static int is_overflowing_cell(txt_table_t *table, int x, int y) +{ + int w; + int h; + cell_overflowed_size(table, x, y, &w, &h); + return w > 1 || h > 1; +} + +/* Using the given column/row size tables, calculate the size of the given + * widget, storing the result in (w, h). + */ + +static void calculate_widget_dimensions(txt_table_t *table, int x, int y, + unsigned int *column_widths, + unsigned int *row_heights, + unsigned int *w, unsigned int *h) +{ + int cell_w; + int cell_h; + int x1; + int y1; + + /* Find which cells this widget occupies. */ + + cell_overflowed_size(table, x, y, &cell_w, &cell_h); + + /* Add up column / row widths / heights to get the actual dimensions. */ + + *w = 0; + for (x1 = x; x1 < x + cell_w; ++x1) + { + *w += column_widths[x1]; + } + + *h = 0; + for (y1 = y; y1 < y + cell_h; ++y1) + { + *h += row_heights[y1]; + } +} + +static void calc_row_col_sizes(txt_table_t *table, unsigned int *row_heights, + unsigned int *col_widths) +{ + int x; + int y; + int rows; + txt_widget_t *widget; + + rows = table_rows(table); + + memset(col_widths, 0, sizeof(int) * table->columns); + + for (y = 0; y < rows; ++y) + { + row_heights[y] = 0; + + for (x = 0; x < table->columns; ++x) + { + if (y * table->columns + x >= table->num_widgets) break; + + widget = table->widgets[y * table->columns + x]; + + if (is_actual_widget(widget)) + { + txt_calc_widget_size(widget); + } + + /* In the first pass we ignore overflowing cells. */ + + if (is_overflowing_cell(table, x, y)) + { + continue; + } + + /* NULL represents an empty spacer */ + + if (is_actual_widget(widget)) + { + if (widget->h > row_heights[y]) row_heights[y] = widget->h; + if (widget->w > col_widths[x]) col_widths[x] = widget->w; + } + } + } + + /* In the second pass, we go through again and process overflowing + * widgets, to ensure that they will fit. + */ + + for (y = 0; y < rows; ++y) + { + for (x = 0; x < table->columns; ++x) + { + unsigned int w; + unsigned int h; + + if (y * table->columns + x >= table->num_widgets) break; + + widget = table->widgets[y * table->columns + x]; + if (!is_actual_widget(widget)) + { + continue; + } + + /* Expand column width and row heights as needed. */ + + calculate_widget_dimensions(table, x, y, col_widths, row_heights, + &w, &h); + if (w < widget->w) + { + col_widths[x] += widget->w - w; + } + + if (h < widget->h) + { + row_heights[y] += widget->h - h; + } + } + } +} + +static void txt_calc_table_size(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + unsigned int *column_widths; + unsigned int *row_heights; + int x; + int y; + int rows; + + rows = table_rows(table); + + row_heights = malloc(sizeof(int) * rows); + column_widths = malloc(sizeof(int) * table->columns); + + calc_row_col_sizes(table, row_heights, column_widths); + + table->widget.w = 0; + + for (x = 0; x < table->columns; ++x) + { + table->widget.w += column_widths[x]; + } + + table->widget.h = 0; + + for (y = 0; y < rows; ++y) + { + table->widget.h += row_heights[y]; + } + + free(row_heights); + free(column_widths); +} + +static void fill_row_to_end(txt_table_t *table) +{ + while ((table->num_widgets % table->columns) != 0) + { + txt_add_widget(table, &txt_table_overflow_right); + } +} + +static int selectable_cell(txt_table_t *table, int x, int y) +{ + txt_widget_t *widget; + int i; + + if (x < 0 || x >= table->columns) + { + return 0; + } + + i = y * table->columns + x; + + if (i >= 0 && i < table->num_widgets) + { + widget = table->widgets[i]; + return is_actual_widget(widget) && txt_selectable_widget(widget) && + widget->visible; + } + + return 0; +} + +/* Tries to locate a selectable widget in the given row, returning + * the column number of the first column available or -1 if none are + * available in the given row. + * + * Starts from start_col, then searches nearby columns. + */ + +static int find_selectable_column(txt_table_t *table, int row, int start_col) +{ + int x; + + for (x = 0; x < table->columns; ++x) + { + /* Search to the right */ + + if (selectable_cell(table, start_col + x, row)) + { + return start_col + x; + } + + /* Search to the left */ + + if (selectable_cell(table, start_col - x, row)) + { + return start_col - x; + } + } + + /* None available */ + + return -1; +} + +/* Change the selected widget. */ + +static void change_selection(txt_table_t *table, int x, int y) +{ + txt_widget_t *cur_widget; + txt_widget_t *new_widget; + int i; + + /* No change? */ + + if (x == table->selected_x && y == table->selected_y) + { + return; + } + + /* Unfocus current widget: */ + + i = table->selected_y * table->columns + table->selected_x; + + if (i < table->num_widgets) + { + cur_widget = table->widgets[i]; + + if (table->widget.focused && is_actual_widget(cur_widget)) + { + txt_set_widget_focus(cur_widget, 0); + } + } + + /* Focus new widget. */ + + new_widget = table->widgets[y * table->columns + x]; + + table->selected_x = x; + table->selected_y = y; + + if (table->widget.focused && new_widget != NULL) + { + txt_set_widget_focus(new_widget, 1); + } +} + +static int txt_table_keypress(TXT_UNCAST_ARG(table), int key) +{ + TXT_CAST_ARG(txt_table_t, table); + int selected; + int rows; + + rows = table_rows(table); + + /* Send to the currently selected widget first */ + + selected = table->selected_y * table->columns + table->selected_x; + + if (selected >= 0 && selected < table->num_widgets) + { + if (is_actual_widget(table->widgets[selected]) && + txt_selectable_widget(table->widgets[selected]) && + txt_widget_key_press(table->widgets[selected], key)) + { + return 1; + } + } + + if (key == KEY_TAB) + { + int dir; + int i; + + dir = txt_get_modifier_state(TXT_MOD_SHIFT) ? -1 : 1; + + /* Cycle through all widgets until we find one that can be selected. */ + + for (i = table->selected_y * table->columns + table->selected_x + dir; + i >= 0 && i < table->num_widgets; i += dir) + { + if (is_actual_widget(table->widgets[i]) && + txt_selectable_widget(table->widgets[i])) + { + change_selection(table, + i % table->columns, i / table->columns); + return 1; + } + } + + return 0; + } + + if (key == KEY_DOWNARROW) + { + int new_x; + int new_y; + + /* Move cursor down to the next selectable widget */ + + for (new_y = table->selected_y + 1; new_y < rows; ++new_y) + { + new_x = find_selectable_column(table, new_y, table->selected_x); + + if (new_x >= 0) + { + /* Found a selectable widget in this column! */ + + change_selection(table, new_x, new_y); + + return 1; + } + } + } + + if (key == KEY_UPARROW) + { + int new_x; + int new_y; + + /* Move cursor up to the next selectable widget */ + + for (new_y = table->selected_y - 1; new_y >= 0; --new_y) + { + new_x = find_selectable_column(table, new_y, table->selected_x); + + if (new_x >= 0) + { + /* Found a selectable widget in this column! */ + + change_selection(table, new_x, new_y); + + return 1; + } + } + } + + if (key == KEY_LEFTARROW) + { + int new_x; + + /* Move cursor left */ + + for (new_x = table->selected_x - 1; new_x >= 0; --new_x) + { + if (selectable_cell(table, new_x, table->selected_y)) + { + /* Found a selectable widget! */ + + change_selection(table, new_x, table->selected_y); + + return 1; + } + } + } + + if (key == KEY_RIGHTARROW) + { + int new_x; + + /* Move cursor left */ + + for (new_x = table->selected_x + 1; new_x < table->columns; ++new_x) + { + if (selectable_cell(table, new_x, table->selected_y)) + { + /* Found a selectable widget! */ + + change_selection(table, new_x, table->selected_y); + + return 1; + } + } + } + + return 0; +} + +/* Check the currently selected widget in the table is valid. */ + +static void check_valid_selection(txt_table_t *table) +{ + int rows; + int new_x; + int new_y; + + rows = table_rows(table); + + for (new_y = table->selected_y; new_y < rows; ++new_y) + { + new_x = find_selectable_column(table, new_y, table->selected_x); + + if (new_x >= 0) + { + /* Found a selectable column. */ + + change_selection(table, new_x, new_y); + + break; + } + } +} + +static void layout_cell(txt_table_t *table, int x, int y, int draw_x, + int draw_y) +{ + txt_widget_t *widget; + int col_width; + + widget = table->widgets[y * table->columns + x]; + + col_width = widget->w; + + /* Adjust x position based on alignment property */ + + switch (widget->align) + { + case TXT_HORIZ_LEFT: + widget->w = col_width; + break; + + case TXT_HORIZ_CENTER: + txt_calc_widget_size(widget); + + /* Separators are always drawn left-aligned. */ + + if (widget->widget_class != &txt_separator_class) + { + draw_x += (col_width - widget->w) / 2; + } + + break; + + case TXT_HORIZ_RIGHT: + txt_calc_widget_size(widget); + + if (widget->widget_class != &txt_separator_class) + { + draw_x += col_width - widget->w; + } + break; + } + + /* Set the position for this widget */ + + widget->x = draw_x; + widget->y = draw_y; + + /* Recursively lay out any widgets contained in the widget */ + + txt_layout_widget(widget); +} + +static void txt_table_layout(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + unsigned int *column_widths; + unsigned int *row_heights; + txt_widget_t *widget; + int draw_x; + int draw_y; + int x; + int y; + int i; + int rows; + + /* Work out the column widths and row heights */ + + rows = table_rows(table); + + column_widths = malloc(sizeof(int) * table->columns); + row_heights = malloc(sizeof(int) * rows); + + calc_row_col_sizes(table, row_heights, column_widths); + + /* If this table only has one column, expand column size to fit + * the display width. Ensures that separators reach the window edges + * when drawing windows. + */ + + if (table->columns == 1) + { + column_widths[0] = table->widget.w; + } + + /* Draw all cells */ + + draw_y = table->widget.y; + + for (y = 0; y < rows; ++y) + { + draw_x = table->widget.x; + + for (x = 0; x < table->columns; ++x) + { + i = y * table->columns + x; + + if (i >= table->num_widgets) break; + + widget = table->widgets[i]; + + if (is_actual_widget(widget)) + { + calculate_widget_dimensions(table, x, y, column_widths, + row_heights, &widget->w, + &widget->h); + layout_cell(table, x, y, draw_x, draw_y); + } + + draw_x += column_widths[x]; + } + + draw_y += row_heights[y]; + } + + free(row_heights); + free(column_widths); +} + +static void txt_table_drawer(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + txt_widget_t *widget; + int i; + + /* Check the table's current selection points at something valid before + * drawing. + */ + + check_valid_selection(table); + + /* Draw all cells */ + + for (i = 0; i < table->num_widgets; ++i) + { + widget = table->widgets[i]; + + if (is_actual_widget(widget)) + { + txt_goto_xy(widget->x, widget->y); + txt_draw_widget(widget); + } + } +} + +/* Responds to mouse presses */ + +static void txt_table_mouse_press(TXT_UNCAST_ARG(table), int x, int y, int b) +{ + TXT_CAST_ARG(txt_table_t, table); + txt_widget_t *widget; + int i; + + for (i = 0; i < table->num_widgets; ++i) + { + widget = table->widgets[i]; + + /* NULL widgets are spacers */ + + if (is_actual_widget(widget)) + { + if (x >= widget->x && x < (signed)(widget->x + widget->w) && + y >= widget->y && y < (signed)(widget->y + widget->h)) + { + /* This is the widget that was clicked! + * + * Select the cell if the widget is selectable + */ + + if (txt_selectable_widget(widget)) + { + change_selection(table, i % table->columns, + i / table->columns); + } + + /* Propagate click */ + + txt_widget_mouse_press(widget, x, y, b); + + break; + } + } + } +} + +/* Determine whether the table is selectable. */ + +static int txt_table_selectable(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + int i; + + /* Is the currently-selected cell selectable? */ + + if (selectable_cell(table, table->selected_x, table->selected_y)) + { + return 1; + } + + /* Find the first selectable cell and set selected_x, selected_y. */ + + for (i = 0; i < table->num_widgets; ++i) + { + if (is_actual_widget(table->widgets[i]) && + txt_selectable_widget(table->widgets[i])) + { + change_selection(table, i % table->columns, i / table->columns); + return 1; + } + } + + /* No selectable widgets exist within the table. */ + + return 0; +} + +/* Need to pass through focus changes to the selected child widget. */ + +static void txt_table_focused(TXT_UNCAST_ARG(table), int focused) +{ + TXT_CAST_ARG(txt_table_t, table); + int i; + + i = table->selected_y * table->columns + table->selected_x; + + if (i < table->num_widgets) + { + if (is_actual_widget(table->widgets[i])) + { + txt_set_widget_focus(table->widgets[i], focused); + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Remove all entries from a table */ + +void txt_clear_table(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + int i; + + /* Free all widgets + * Skip over the first (num_columns) widgets in the array, as these + * are the column struts used to control column width + */ + + for (i = table->columns; i < table->num_widgets; ++i) + { + if (is_actual_widget(table->widgets[i])) + { + txt_destroy_widget(table->widgets[i]); + } + } + + /* Shrink the table to just the column strut widgets */ + + table->num_widgets = table->columns; +} + +void txt_add_widget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_table_t, table); + TXT_CAST_ARG(txt_widget_t, widget); + int is_separator; + int i; + + /* Convenience alias for NULL: */ + + if (widget == &txt_table_empty) + { + widget = NULL; + } + else if (widget == &txt_table_eol) + { + fill_row_to_end(table); + return; + } + + /* We have special handling for the separator widget: */ + + is_separator = is_actual_widget(widget) && + widget->widget_class == &txt_separator_class; + + /* If we add two separators consecutively, the new separator replaces the + * first. This allows us to override the "implicit" separator that is + * added at the top of a window when it is created. + */ + + if (is_separator) + { + for (i = table->num_widgets - 1; i >= 0; --i) + { + txt_widget_t *last_widget; + last_widget = table->widgets[i]; + + if (is_actual_widget(last_widget) && + widget->widget_class == &txt_separator_class && + last_widget->widget_class == &txt_separator_class) + { + table->widgets[i] = widget; + txt_destroy_widget(last_widget); + return; + } + else if (last_widget != &txt_table_overflow_right) + { + break; + } + } + } + + /* Separators begin on a new line. */ + + if (is_separator) + { + fill_row_to_end(table); + } + + table->widgets = realloc( + table->widgets, + sizeof(txt_widget_t *) * (table->num_widgets + 1)); + table->widgets[table->num_widgets] = widget; + ++table->num_widgets; + + /* Maintain parent pointer. */ + + if (is_actual_widget(widget)) + { + widget->parent = &table->widget; + } + + /* Separators always take up the entire line. */ + + if (is_separator) + { + fill_row_to_end(table); + } +} + +/* Add multiple widgets to a table. */ + +void txt_add_widgets(TXT_UNCAST_ARG(table), ...) +{ + TXT_CAST_ARG(txt_table_t, table); + va_list args; + txt_widget_t *widget; + + va_start(args, TXT_UNCAST_ARG_NAME(table)); + + /* Keep adding widgets until a NULL is reached. */ + + for (; ; ) + { + widget = va_arg(args, txt_widget_t *); + + if (widget == NULL) + { + break; + } + + txt_add_widget(table, widget); + } + + va_end(args); +} + +void txt_init_table(txt_table_t *table, int columns) +{ + int i; + + txt_init_widget(table, &txt_table_class); + table->columns = columns; + table->widgets = NULL; + table->num_widgets = 0; + table->selected_x = 0; + table->selected_y = 0; + + /* Add a strut for each column at the start of the table. + * These are used by the txt_set_column_widths function below: + * the struts are created with widths of 0 each, but this + * function changes them. + */ + + for (i = 0; i < columns; ++i) + { + txt_add_widget(table, txt_new_strut(0, 0)); + } +} + +txt_table_t *txt_new_table(int columns) +{ + txt_table_t *table; + + table = malloc(sizeof(txt_table_t)); + + txt_init_table(table, columns); + + return table; +} + +/* Alternative to txt_new_table() that allows a list of widgets to be + * provided in its arguments. + */ + +txt_table_t *txt_make_table(int columns, ...) +{ + txt_table_t *table; + va_list args; + + table = txt_new_table(columns); + va_start(args, columns); + + for (; ; ) + { + txt_widget_t *widget; + widget = va_arg(args, txt_widget_t *); + + if (widget == NULL) + { + break; + } + + txt_add_widget(table, widget); + } + + va_end(args); + + return table; +} + +/* Create a horizontal table from a list of widgets. */ + +txt_table_t *txt_new_horiz_box(TXT_UNCAST_ARG(first_widget), ...) +{ + TXT_CAST_ARG(txt_widget_t, first_widget); + txt_table_t *result; + va_list args; + int num_args; + + /* First, find the number of arguments to determine the width of + * the box. + */ + + va_start(args, TXT_UNCAST_ARG_NAME(first_widget)); + + num_args = 1; + + for (; ; ) + { + txt_widget_t *widget; + + widget = va_arg(args, txt_widget_t *); + + if (widget == NULL) + { + /* End of list */ + + break; + } + else + { + ++num_args; + } + } + + va_end(args); + + /* Create the table. */ + + result = txt_new_table(num_args); + txt_add_widget(result, first_widget); + + /* Go through the list again and add each widget. */ + + va_start(args, TXT_UNCAST_ARG_NAME(first_widget)); + + for (; ; ) + { + txt_widget_t *widget; + + widget = va_arg(args, txt_widget_t *); + + if (widget == NULL) + { + /* End of list */ + + break; + } + else + { + txt_add_widget(result, widget); + } + } + + va_end(args); + + return result; +} + +/* Get the currently-selected widget in a table, recursively searching + * through sub-tables if necessary. + */ + +txt_widget_t *txt_get_selected_widget(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + txt_widget_t *result; + int index; + + index = table->selected_y * table->columns + table->selected_x; + + result = NULL; + + if (index >= 0 && index < table->num_widgets) + { + result = table->widgets[index]; + + if (!is_actual_widget(result)) + { + result = NULL; + } + } + + if (result != NULL && result->widget_class == &txt_table_class) + { + result = txt_get_selected_widget(result); + } + + return result; +} + +/* Selects a given widget in a table, recursively searching any tables + * within this table. Returns 1 if successful, 0 if unsuccessful. + */ + +int txt_select_widget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_table_t, table); + TXT_CAST_ARG(txt_widget_t, widget); + int i; + + for (i = 0; i < table->num_widgets; ++i) + { + if (!is_actual_widget(table->widgets[i])) + { + continue; + } + + if (table->widgets[i] == widget) + { + /* Found the item! Select it and return. */ + + change_selection(table, i % table->columns, i / table->columns); + + return 1; + } + + if (table->widgets[i]->widget_class == &txt_table_class) + { + /* This item is a subtable. Recursively search this table. */ + + if (txt_select_widget(table->widgets[i], widget)) + { + /* Found it in the subtable. Select this subtable and return. + */ + + change_selection(table, i % table->columns, + i / table->columns); + + return 1; + } + } + } + + /* Not found. */ + + return 0; +} + +void txt_set_table_columns(TXT_UNCAST_ARG(table), int new_columns) +{ + TXT_CAST_ARG(txt_table_t, table); + txt_widget_t **new_widgets; + txt_widget_t *widget; + int new_num_widgets; + int i; + int j; + int x; + + /* We need as many full rows as are in the current list, plus the + * remainder from the last row. + */ + + new_num_widgets = (table->num_widgets / table->columns) * new_columns + + (table->num_widgets % table->columns); + new_widgets = calloc(new_num_widgets, sizeof(txt_widget_t *)); + + /* Reset and add one by one from the old table. */ + + new_num_widgets = 0; + + for (i = 0; i < table->num_widgets; ++i) + { + widget = table->widgets[i]; + x = i % table->columns; + + if (x < new_columns) + { + new_widgets[new_num_widgets] = widget; + ++new_num_widgets; + } + else if (is_actual_widget(widget)) + { + txt_destroy_widget(widget); + } + + /* When we reach the last column of a row, we must pad it out with + * extra widgets to reach the next row. + */ + + if (x == table->columns - 1) + { + for (j = table->columns; j < new_columns; ++j) + { + /* First row? We need to add struts that are used to apply + * the column widths. + */ + + if (i < table->columns) + { + widget = &txt_new_strut(0, 0)->widget; + } + else + { + widget = &txt_table_overflow_right; + } + + new_widgets[new_num_widgets] = widget; + ++new_num_widgets; + } + } + } + + free(table->widgets); + table->widgets = new_widgets; + table->num_widgets = new_num_widgets; + table->columns = new_columns; +} + +/* Sets the widths of columns in a table. */ + +void txt_set_column_widths(TXT_UNCAST_ARG(table), ...) +{ + TXT_CAST_ARG(txt_table_t, table); + va_list args; + txt_strut_t *strut; + int i; + int width; + + va_start(args, TXT_UNCAST_ARG_NAME(table)); + + for (i = 0; i < table->columns; ++i) + { + width = va_arg(args, int); + + strut = (txt_strut_t *)table->widgets[i]; + strut->width = width; + } + + va_end(args); +} + +/* Moves the select by at least the given number of characters. + * Currently quietly ignores pagex, as we don't use it. + */ + +int txt_page_table(TXT_UNCAST_ARG(table), int pagex, int pagey) +{ + TXT_CAST_ARG(txt_table_t, table); + unsigned int *column_widths; + unsigned int *row_heights; + int rows; + int changed = 0; + + rows = table_rows(table); + + row_heights = malloc(sizeof(int) * rows); + column_widths = malloc(sizeof(int) * table->columns); + + calc_row_col_sizes(table, row_heights, column_widths); + + if (pagex) + { + /* @todo Jump selection to the left or right as needed */ + } + + if (pagey) + { + int new_x; + int new_y; + int distance = 0; + int dir; + + /* What direction are we moving? */ + + if (pagey > 0) + { + dir = 1; + } + else + { + dir = -1; + } + + /* Move the cursor until the desired distance is reached. */ + + new_y = table->selected_y; + + while (new_y >= 0 && new_y < rows) + { + /* We are about to travel a distance equal to the height of the row + * we are about to leave. + */ + + distance += row_heights[new_y]; + + /* *Now* increment the loop. */ + + new_y += dir; + + new_x = find_selectable_column(table, new_y, table->selected_x); + + if (new_x >= 0) + { + /* Found a selectable widget in this column! + * Select it anyway in case we don't find something better. + */ + + change_selection(table, new_x, new_y); + changed = 1; + + /* ...but is it far enough away? */ + + if (distance >= abs(pagey)) + { + break; + } + } + } + } + + free(row_heights); + free(column_widths); + + return changed; +} diff --git a/games/NXDoom/textscreen/txt_table.h b/games/NXDoom/textscreen/txt_table.h new file mode 100644 index 00000000000..d1dffbeff63 --- /dev/null +++ b/games/NXDoom/textscreen/txt_table.h @@ -0,0 +1,265 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_table.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_TABLE_H +#define TXT_TABLE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/** + * Magic value that if used in a table, will indicate that the cell is + * empty and the widget in the cell to the left can overflow into it. + */ + +#define TXT_TABLE_OVERFLOW_RIGHT (&txt_table_overflow_right) + +/** + * Magic value that if used in a table, will indicate that the cell is + * empty and the widget in the cell above it can overflow down into it. + */ + +#define TXT_TABLE_OVERFLOW_DOWN (&txt_table_overflow_down) + +/** + * Magic value that if given to @ref txt_add_widget(), will pad out all + * columns until the end of line. + */ + +#define TXT_TABLE_EOL (&txt_table_eol) + +/** + * Indicates an empty space to @ref txt_add_widgets(). Equivalent to + * txt_add_widget(table, NULL), except that NULL is used by txt_add_widgets() + * to indicate the end of input. + */ + +#define TXT_TABLE_EMPTY (&txt_table_empty) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Table widget. + * + * A table is a widget that contains other widgets. It may have + * multiple columns, in which case the child widgets are laid out + * in a grid. Columns automatically grow as necessary, although + * minimum column widths can be set using @ref txt_set_column_widths. + * + * To create a new table, use @ref txt_new_table. It is also + * possible to use @ref txt_new_horiz_box to create a table, specifying + * widgets to place inside a horizontal list. A vertical list is + * possible simply by creating a table containing a single column. + */ + +struct txt_table_s +{ + txt_widget_t widget; + + /* Widgets in this table + * The widget at (x,y) in the table is widgets[columns * y + x] + */ + + txt_widget_t **widgets; + int num_widgets; + + /* Number of columns */ + + int columns; + + /* Currently selected: */ + + int selected_x; + int selected_y; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern txt_widget_class_t txt_table_class; +extern txt_widget_t txt_table_overflow_right; +extern txt_widget_t txt_table_overflow_down; +extern txt_widget_t txt_table_eol; +extern txt_widget_t txt_table_empty; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void txt_init_table(txt_table_t *table, int columns); + +/** + * Create a new table. + * + * @param columns The number of columns in the new table. + * @return Pointer to the new table structure. + */ + +txt_table_t *txt_new_table(int columns); + +/** + * Create a new table and populate it with provided widgets. + * + * The arguments to this function are variable. Each argument must be a + * pointer to a widget, and the list is terminated with a NULL. + * + * @param columns The number of columns in the new table. + * @return Pointer to the new table structure. + */ + +txt_table_t *txt_make_table(int columns, ...); + +/** + * Create a table containing the specified widgets packed horizontally, + * from left to right. + * + * The arguments to this function are variable. Each argument must + * be a pointer to a widget, and the list is terminated with a + * NULL. + * + * @return Pointer to the new table structure. + */ + +txt_table_t *txt_new_horiz_box(TXT_UNCAST_ARG(first_widget), ...); + +/** + * Get the currently selected widget within a table. + * + * This function will recurse through subtables if necessary. + * + * @param table The table. + * @return Pointer to the widget that is currently selected. + */ + +txt_widget_t *txt_get_selected_widget(TXT_UNCAST_ARG(table)); + +/** + * Add a widget to a table. + * + * Widgets are added to tables horizontally, from left to right. + * For example, for a table with three columns, the first call + * to this function will add a widget to the first column, the second + * call to the second column, the third call to the third column, + * and the fourth will return to the first column, starting a new + * row. + * + * For adding many widgets, it may be easier to use + * @ref txt_add_widgets. + * + * @param table The table. + * @param widget The widget to add. + */ + +void txt_add_widget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget)); + +/** + * Add multiple widgets to a table. + * + * Widgets are added as described in the documentation for the + * @ref txt_add_widget function. This function adds multiple + * widgets. The number of arguments is variable, and the argument + * list must be terminated by a NULL pointer. + * + * @param table The table. + */ + +void txt_add_widgets(TXT_UNCAST_ARG(table), ...); + +/** + * Select the given widget that is contained within the specified + * table. + * + * This function will recursively search through subtables if + * necessary. + * + * @param table The table. + * @param widget The widget to select. + * @return Non-zero (true) if it has been selected, + * or zero (false) if it was not found within + * this table. + */ + +int txt_select_widget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget)); + +/** + * Change the number of columns in the table. + * + * Existing widgets in the table will be preserved, unless the change + * reduces the number of columns, in which case the widgets from the + * 'deleted' columns will be freed. + * + * This function can be useful for changing the number of columns in + * a window, which by default are tables containing a single column. + * + * @param table The table. + * @param new_columns The new number of columns. + */ + +void txt_set_table_columns(TXT_UNCAST_ARG(table), int new_columns); + +/** + * Set the widths of the columns of the table. + * + * The arguments to this function are variable, and correspond + * to the number of columns in the table. For example, if a table + * has five columns, the width of each of the five columns must be + * specified. + * + * The width values are in number of characters. + * + * Note that this function only sets the minimum widths for columns; + * if the columns contain widgets that are wider than the widths + * specified, they will be larger. + * + * @param table The table. + */ + +void txt_set_column_widths(TXT_UNCAST_ARG(table), ...); + +/** + * Remove all widgets from a table. + * + * @param table The table. + */ + +void txt_clear_table(TXT_UNCAST_ARG(table)); + +/** + * Hack to move the selection in a table by a 'page', triggered by the + * scrollpane. This acts as per the keyboard events for the arrows, but moves + * the selection by at least the specified number of characters. + * + * @param table The table. + * @param pagex Minimum distance to move the selection horizontally. + * @param pagey Minimum distance to move the selection vertically. + * @return Non-zero if the selection has been changed. + */ + +int txt_page_table(TXT_UNCAST_ARG(table), int pagex, int pagey); + +#endif /* #ifndef TXT_TABLE_T */ diff --git a/games/NXDoom/textscreen/txt_utf8.c b/games/NXDoom/textscreen/txt_utf8.c new file mode 100644 index 00000000000..08602599488 --- /dev/null +++ b/games/NXDoom/textscreen/txt_utf8.c @@ -0,0 +1,159 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_utf8.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "txt_utf8.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Encode a Unicode character as UTF-8, storing it in the buffer 'p' + * and returning the new, incremented position. + */ + +char *txt_encode_utf8(char *p, unsigned int c) +{ + if (c < 0x80) /* 1 character (ASCII): */ + { + p[0] = c; + return p + 1; + } + else if (c < 0x800) /* 2 character: */ + { + p[0] = 0xc0 | (c >> 6); + p[1] = 0x80 | (c & 0x3f); + return p + 2; + } + else if (c < 0x10000) /* 3 chacater: */ + { + p[0] = 0xe0 | (c >> 12); + p[1] = 0x80 | ((c >> 6) & 0x3f); + p[2] = 0x80 | (c & 0x3f); + return p + 3; + } + else if (c < 0x200000) /* 4 character: */ + { + p[0] = 0xf0 | (c >> 18); + p[1] = 0x80 | ((c >> 12) & 0x3f); + p[2] = 0x80 | ((c >> 6) & 0x3f); + p[3] = 0x80 | (c & 0x3f); + return p + 4; + } + else + { + return p; /* Too big! */ + } +} + +/* Decode UTF-8 character, incrementing *ptr over the decoded bytes. */ + +unsigned int txt_decode_utf8(const char **ptr) +{ + const char *p = *ptr; + unsigned int c; + + /* UTF-8 decode. */ + + if ((*p & 0x80) == 0) /* 1 character (ASCII): */ + { + c = *p; + *ptr += 1; + } + else if ((p[0] & 0xe0) == 0xc0 /* 2 character: */ + && (p[1] & 0xc0) == 0x80) + { + c = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f); + *ptr += 2; + } + else if ((p[0] & 0xf0) == 0xe0 /* 3 character: */ + && (p[1] & 0xc0) == 0x80 && (p[2] & 0xc0) == 0x80) + { + c = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); + *ptr += 3; + } + else if ((p[0] & 0xf8) == 0xf0 /* 4 character: */ + && (p[1] & 0xc0) == 0x80 && (p[2] & 0xc0) == 0x80 && + (p[3] & 0xc0) == 0x80) + { + c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | + ((p[2] & 0x3f) << 6) | (p[3] & 0x3f); + *ptr += 4; + } + else + { + /* Decode failure. + * Don't bother with 5/6 byte sequences. + */ + + c = 0; + } + + return c; +} + +/* Count the number of characters in a UTF-8 string. */ + +unsigned int txt_utf8_strlen(const char *s) +{ + const char *p; + unsigned int result = 0; + unsigned int c; + + for (p = s; *p != '\0'; ) + { + c = txt_decode_utf8(&p); + + if (c == 0) + { + break; + } + + ++result; + } + + return result; +} + +/* Skip past the first n characters in a UTF-8 string. */ + +char *txt_utf8_skip_chars(const char *s, unsigned int n) +{ + unsigned int i; + const char *p; + + p = s; + + for (i = 0; i < n; ++i) + { + if (txt_decode_utf8(&p) == 0) + { + break; + } + } + + return (char *)p; +} diff --git a/games/NXDoom/textscreen/txt_utf8.h b/games/NXDoom/textscreen/txt_utf8.h new file mode 100644 index 00000000000..0d2f98ef384 --- /dev/null +++ b/games/NXDoom/textscreen/txt_utf8.h @@ -0,0 +1,38 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_utf8.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_UTF8_H +#define TXT_UTF8_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +char *txt_encode_utf8(char *p, unsigned int c); +unsigned int txt_decode_utf8(const char **ptr); +unsigned int txt_utf8_strlen(const char *s); +char *txt_utf8_skip_chars(const char *s, unsigned int n); + +#endif /* TXT_UTF8_H */ diff --git a/games/NXDoom/textscreen/txt_widget.c b/games/NXDoom/textscreen/txt_widget.c new file mode 100644 index 00000000000..01c44643c4e --- /dev/null +++ b/games/NXDoom/textscreen/txt_widget.c @@ -0,0 +1,350 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_widget.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_desktop.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_widget.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + char *signal_name; + txt_widget_signal_f func; + void *user_data; +} txt_callback_t; + +struct txt_callback_table_s +{ + int refcount; + txt_callback_t *callbacks; + int num_callbacks; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int txt_contains_widget(TXT_UNCAST_ARG(haystack), + TXT_UNCAST_ARG(needle)) +{ + TXT_CAST_ARG(txt_widget_t, haystack); + TXT_CAST_ARG(txt_widget_t, needle); + + while (needle != NULL) + { + if (needle == haystack) + { + return 1; + } + + needle = needle->parent; + } + + return 0; +} + +static txt_callback_table_t *txt_new_callback_table(void) +{ + txt_callback_table_t *table; + + table = malloc(sizeof(txt_callback_table_t)); + table->callbacks = NULL; + table->num_callbacks = 0; + table->refcount = 1; + + return table; +} + +static void txt_ref_callback_table(txt_callback_table_t *table) +{ + ++table->refcount; +} + +static void txt_unref_callback_table(txt_callback_table_t *table) +{ + int i; + + --table->refcount; + + if (table->refcount == 0) + { + /* No more references to this table */ + + for (i = 0; i < table->num_callbacks; ++i) + { + free(table->callbacks[i].signal_name); + } + + free(table->callbacks); + free(table); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_init_widget(TXT_UNCAST_ARG(widget), + txt_widget_class_t *widget_class) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + widget->widget_class = widget_class; + widget->callback_table = txt_new_callback_table(); + widget->parent = NULL; + + /* Not focused until we hear otherwise. */ + + widget->focused = 0; + + /* Visible by default. */ + + widget->visible = 1; + + /* Align left by default */ + + widget->align = TXT_HORIZ_LEFT; +} + +void txt_signal_connect(TXT_UNCAST_ARG(widget), const char *signal_name, + txt_widget_signal_f func, void *user_data) +{ + TXT_CAST_ARG(txt_widget_t, widget); + txt_callback_table_t *table; + txt_callback_t *callback; + + table = widget->callback_table; + + /* Add a new callback to the table */ + + table->callbacks = realloc( + table->callbacks, sizeof(txt_callback_t) * (table->num_callbacks + 1)); + callback = &table->callbacks[table->num_callbacks]; + ++table->num_callbacks; + + callback->signal_name = strdup(signal_name); + callback->func = func; + callback->user_data = user_data; +} + +void txt_emit_signal(TXT_UNCAST_ARG(widget), const char *signal_name) +{ + TXT_CAST_ARG(txt_widget_t, widget); + txt_callback_table_t *table; + int i; + + table = widget->callback_table; + + /* Don't destroy the table while we're searching through it + * (one of the callbacks may destroy this window) + */ + + txt_ref_callback_table(table); + + /* Search the table for all callbacks with this name and invoke + * the functions. + */ + + for (i = 0; i < table->num_callbacks; ++i) + { + if (!strcmp(table->callbacks[i].signal_name, signal_name)) + { + table->callbacks[i].func(widget, table->callbacks[i].user_data); + } + } + + /* Finished using the table */ + + txt_unref_callback_table(table); +} + +void txt_calc_widget_size(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + widget->widget_class->size_calc(widget); +} + +void txt_draw_widget(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + txt_saved_colors_t colors; + + /* The drawing function might change the fg/bg colors, + * so make sure we restore them after it's done. + */ + + txt_save_colours(&colors); + + /* For convenience... */ + + txt_goto_xy(widget->x, widget->y); + + /* Call drawer method */ + + widget->widget_class->drawer(widget); + + txt_restore_colours(&colors); +} + +void txt_destroy_widget(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + widget->widget_class->destructor(widget); + txt_unref_callback_table(widget->callback_table); + free(widget); +} + +int txt_widget_key_press(TXT_UNCAST_ARG(widget), int key) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget->widget_class->key_press != NULL) + { + return widget->widget_class->key_press(widget, key); + } + + return 0; +} + +void txt_set_widget_focus(TXT_UNCAST_ARG(widget), int focused) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget == NULL) + { + return; + } + + if (widget->focused != focused) + { + widget->focused = focused; + + if (widget->widget_class->focus_change != NULL) + { + widget->widget_class->focus_change(widget, focused); + } + } +} + +void txt_set_widget_align(TXT_UNCAST_ARG(widget), + txt_horiz_align_t horiz_align) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + widget->align = horiz_align; +} + +void txt_widget_mouse_press(TXT_UNCAST_ARG(widget), int x, int y, int b) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget->widget_class->mouse_press != NULL) + { + widget->widget_class->mouse_press(widget, x, y, b); + } +} + +void txt_layout_widget(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget->widget_class->layout != NULL) + { + widget->widget_class->layout(widget); + } +} + +int txt_always_selectable(TXT_UNCAST_ARG(widget)) +{ + return 1; +} + +int txt_never_selectable(TXT_UNCAST_ARG(widget)) +{ + return 0; +} + +int txt_selectable_widget(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget->widget_class->selectable != NULL) + { + return widget->widget_class->selectable(widget); + } + else + { + return 0; + } +} + +int txt_hovering_over_widget(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + txt_window_t *active_window; + int x; + int y; + + /* We can only be hovering over widgets in the active window. */ + + active_window = txt_get_active_window(); + + if (active_window == NULL || !txt_contains_widget(active_window, widget)) + { + return 0; + } + + /* Is the mouse cursor within the bounds of the widget? */ + + txt_get_mouse_position(&x, &y); + + return (x >= widget->x && x < widget->x + widget->w && y >= widget->y && + y < widget->y + widget->h); +} + +void txt_set_widget_bg(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget->focused) + { + txt_bgcolour(TXT_COLOR_GREY, 0); + } + else if (txt_hovering_over_widget(widget)) + { + txt_bgcolour(TXT_HOVER_BACKGROUND, 0); + } + else + { + /* Use normal window background. */ + } +} diff --git a/games/NXDoom/textscreen/txt_widget.h b/games/NXDoom/textscreen/txt_widget.h new file mode 100644 index 00000000000..109be5e9cbc --- /dev/null +++ b/games/NXDoom/textscreen/txt_widget.h @@ -0,0 +1,190 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_widget.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_WIDGET_H +#define TXT_WIDGET_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TXT_UNCAST_ARG_NAME(name) uncast_##name +#define TXT_UNCAST_ARG(name) void *TXT_UNCAST_ARG_NAME(name) +#define TXT_CAST_ARG(type, name) type *name = (type *)uncast_##name + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Forward definitions */ + +typedef struct txt_table_s txt_table_t; +typedef struct txt_strut_s txt_strut_t; +typedef struct txt_spincontrol_s txt_spincontrol_t; +typedef struct txt_separator_s txt_separator_t; +typedef struct txt_scrollpane_s txt_scrollpane_t; +typedef struct txt_radiobutton_s txt_radiobutton_t; +typedef struct txt_inputbox_s txt_inputbox_t; +typedef struct txt_dropdown_list_s txt_dropdown_list_t; +typedef struct txt_checkbox_s txt_checkbox_t; +typedef struct txt_button_s txt_button_t; + +typedef enum +{ + TXT_VERT_TOP, + TXT_VERT_CENTER, + TXT_VERT_BOTTOM, +} txt_vert_align_t; + +typedef enum +{ + TXT_HORIZ_LEFT, + TXT_HORIZ_CENTER, + TXT_HORIZ_RIGHT, +} txt_horiz_align_t; + +/* A GUI widget. + * + * A widget is an individual component of a GUI. Various different widget + * types exist. + * + * Widgets may emit signals. The types of signal emitted by a widget + * depend on the type of the widget. It is possible to be notified + * when a signal occurs using the @ref txt_signal_connect function. + */ + +typedef struct txt_widget_s txt_widget_t; + +typedef struct txt_widget_class_s txt_widget_class_t; +typedef struct txt_callback_table_s txt_callback_table_t; + +typedef void (*txt_widget_size_calc_f)(TXT_UNCAST_ARG(widget)); +typedef void (*txt_widget_drawer_f)(TXT_UNCAST_ARG(widget)); +typedef void (*txt_widget_destroy_f)(TXT_UNCAST_ARG(widget)); +typedef int (*txt_widget_keypress_f)(TXT_UNCAST_ARG(widget), int key); +typedef void (*txt_widget_signal_f)(TXT_UNCAST_ARG(widget), void *user_data); +typedef void (*txt_mouse_press_f)(TXT_UNCAST_ARG(widget), int x, int y, + int b); +typedef void (*txt_widget_layout_f)(TXT_UNCAST_ARG(widget)); +typedef int (*txt_widget_selectable_f)(TXT_UNCAST_ARG(widget)); +typedef void (*txt_widget_focus_f)(TXT_UNCAST_ARG(widget), int focused); + +struct txt_widget_class_s +{ + txt_widget_selectable_f selectable; + txt_widget_size_calc_f size_calc; + txt_widget_drawer_f drawer; + txt_widget_keypress_f key_press; + txt_widget_destroy_f destructor; + txt_mouse_press_f mouse_press; + txt_widget_layout_f layout; + txt_widget_focus_f focus_change; +}; + +struct txt_widget_s +{ + txt_widget_class_t *widget_class; + txt_callback_table_t *callback_table; + int visible; + txt_horiz_align_t align; + int focused; + + /* These are set automatically when the window is drawn and should + * not be set manually. + */ + + int x; + int y; + unsigned int w; + unsigned int h; + + /* Pointer up to parent widget that contains this widget. */ + + txt_widget_t *parent; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void txt_init_widget(TXT_UNCAST_ARG(widget), + txt_widget_class_t *widget_class); +void txt_calc_widget_size(TXT_UNCAST_ARG(widget)); +void txt_draw_widget(TXT_UNCAST_ARG(widget)); +void txt_emit_signal(TXT_UNCAST_ARG(widget), const char *signal_name); +int txt_widget_key_press(TXT_UNCAST_ARG(widget), int key); +void txt_widget_mouse_press(TXT_UNCAST_ARG(widget), int x, int y, int b); +void txt_destroy_widget(TXT_UNCAST_ARG(widget)); +void txt_layout_widget(TXT_UNCAST_ARG(widget)); +int txt_always_selectable(TXT_UNCAST_ARG(widget)); +int txt_never_selectable(TXT_UNCAST_ARG(widget)); +void txt_set_widget_focus(TXT_UNCAST_ARG(widget), int focused); + +/** + * Set a callback function to be invoked when a signal occurs. + * + * @param widget The widget to watch. + * @param signal_name The signal to watch. + * @param func The callback function to invoke. + * @param user_data User-specified pointer to pass to the callback + * function. + */ + +void txt_signal_connect(TXT_UNCAST_ARG(widget), const char *signal_name, + txt_widget_signal_f func, void *user_data); + +/** + * Set the policy for how a widget should be aligned within a table. + * By default, widgets are aligned to the left of the column. + * + * @param widget The widget. + * @param horiz_align The alignment to use. + */ + +void txt_set_widget_align(TXT_UNCAST_ARG(widget), + txt_horiz_align_t horiz_align); + +/** + * Query whether a widget is selectable with the cursor. + * + * @param widget The widget. + * @return Non-zero if the widget is selectable. + */ + +int txt_selectable_widget(TXT_UNCAST_ARG(widget)); + +/** + * Query whether the mouse is hovering over the specified widget. + * + * @param widget The widget. + * @return Non-zero if the mouse cursor is over the widget. + */ + +int txt_hovering_over_widget(TXT_UNCAST_ARG(widget)); + +/** + * Set the background to draw the specified widget, depending on + * whether it is selected and the mouse is hovering over it. + * + * @param widget The widget. + */ + +void txt_set_widget_bg(TXT_UNCAST_ARG(widget)); + +#endif /* TXT_WIDGET_H */ diff --git a/games/NXDoom/textscreen/txt_window.c b/games/NXDoom/textscreen/txt_window.c new file mode 100644 index 00000000000..1f48eac5a7a --- /dev/null +++ b/games/NXDoom/textscreen/txt_window.c @@ -0,0 +1,609 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_window.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_desktop.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_label.h" +#include "txt_main.h" +#include "txt_separator.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void calc_window_position(txt_window_t *window) +{ + switch (window->horiz_align) + { + case TXT_HORIZ_LEFT: + window->window_x = window->x; + break; + case TXT_HORIZ_CENTER: + window->window_x = window->x - (window->window_w / 2); + break; + case TXT_HORIZ_RIGHT: + window->window_x = window->x - (window->window_w - 1); + break; + } + + switch (window->vert_align) + { + case TXT_VERT_TOP: + window->window_y = window->y; + break; + case TXT_VERT_CENTER: + window->window_y = window->y - (window->window_h / 2); + break; + case TXT_VERT_BOTTOM: + window->window_y = window->y - (window->window_h - 1); + break; + } +} + +static void layout_action_area(txt_window_t *window) +{ + txt_widget_t *widget; + int space_available; + int space_left_offset; + + /* We need to calculate the available horizontal space for the center + * action widget, so that we can center it within it. + * To start with, we have the entire action area available. + */ + + space_available = window->window_w; + space_left_offset = 0; + + /* Left action */ + + if (window->actions[TXT_HORIZ_LEFT] != NULL) + { + widget = window->actions[TXT_HORIZ_LEFT]; + + txt_calc_widget_size(widget); + + widget->x = window->window_x + 1; + widget->y = window->window_y + window->window_h - widget->h - 1; + + /* Adjust available space: */ + + space_available -= widget->w; + space_left_offset += widget->w; + + txt_layout_widget(widget); + } + + /* Draw the right action */ + + if (window->actions[TXT_HORIZ_RIGHT] != NULL) + { + widget = window->actions[TXT_HORIZ_RIGHT]; + + txt_calc_widget_size(widget); + + widget->x = window->window_x + window->window_w - 1 - widget->w; + widget->y = window->window_y + window->window_h - widget->h - 1; + + /* Adjust available space: */ + + space_available -= widget->w; + + txt_layout_widget(widget); + } + + /* Draw the center action */ + + if (window->actions[TXT_HORIZ_CENTER] != NULL) + { + widget = window->actions[TXT_HORIZ_CENTER]; + + txt_calc_widget_size(widget); + + /* The left and right widgets have left a space sandwiched between + * them. Center this widget within that space. + */ + + widget->x = window->window_x + space_left_offset + + (space_available - widget->w) / 2; + widget->y = window->window_y + window->window_h - widget->h - 1; + + txt_layout_widget(widget); + } +} + +static void draw_action_area(txt_window_t *window) +{ + int i; + + for (i = 0; i < 3; ++i) + { + if (window->actions[i] != NULL) + { + txt_draw_widget(window->actions[i]); + } + } +} + +static void calc_action_area_size(txt_window_t *window, unsigned int *w, + unsigned int *h) +{ + txt_widget_t *widget; + int i; + + *w = 0; + *h = 0; + + /* Calculate the width of all the action widgets and use this + * to create an overall min. width of the action area + */ + + for (i = 0; i < 3; ++i) + { + widget = (txt_widget_t *)window->actions[i]; + + if (widget != NULL) + { + txt_calc_widget_size(widget); + *w += widget->w; + + if (widget->h > *h) + { + *h = widget->h; + } + } + } +} + +/* Sets size and position of all widgets in a window */ + +static void txt_layout_window(txt_window_t *window) +{ + txt_widget_t *widgets = (txt_widget_t *)window; + unsigned int widgets_w; + unsigned int actionarea_w; + unsigned int actionarea_h; + + /* Calculate size of table */ + + txt_calc_widget_size(window); + + /* Widgets area: add one character of padding on each side */ + + widgets_w = widgets->w + 2; + + /* Calculate the size of the action area + * Make window wide enough to action area + */ + + calc_action_area_size(window, &actionarea_w, &actionarea_h); + + if (actionarea_w > widgets_w) widgets_w = actionarea_w; + + /* Set the window size based on widgets_w */ + + window->window_w = widgets_w + 2; + window->window_h = widgets->h + 1; + + /* If the window has a title, add an extra two lines */ + + if (window->title != NULL) + { + window->window_h += 2; + } + + /* If the window has an action area, add extra lines */ + + if (actionarea_h > 0) + { + window->window_h += actionarea_h + 1; + } + + /* Use the x,y position as the centerpoint and find the location to + * draw the window. + */ + + calc_window_position(window); + + /* Set the table size and position */ + + widgets->w = widgets_w - 2; + + /* widgets->h (already set) */ + + widgets->x = window->window_x + 2; + widgets->y = window->window_y; + + if (window->title != NULL) + { + widgets->y += 2; + } + + /* Layout the table and action area */ + + layout_action_area(window); + txt_layout_widget(widgets); +} + +static void txt_open_url(const char *url) +{ +#if 0 + char *cmd; + size_t cmd_len; + int retval; + + cmd_len = strlen(url) + 30; + cmd = malloc(cmd_len); + + /* The Unix situation sucks as usual, but the closest thing to a + * standard that exists is the xdg-utils package. + */ + + if (system("xdg-open --version 2>/dev/null") != 0) + { + fprintf(stderr, + "xdg-utils is not installed. Can't open this URL:\n%s\n", url); + free(cmd); + return; + } + + txt_snprintf(cmd, cmd_len, "xdg-open \"%s\"", url); + + retval = system(cmd); + if (retval != 0) + { + fprintf(stderr, "txt_open_url: error executing '%s'; return code %d\n", + cmd, retval); + } + + free(cmd); +#endif +} + +static int mouse_button_press(txt_window_t *window, int b) +{ + int x; + int y; + int i; + txt_widget_t *widgets; + txt_widget_t *widget; + + /* Lay out the window, set positions and sizes of all widgets */ + + txt_layout_window(window); + + /* Get the current mouse position */ + + txt_get_mouse_position(&x, &y); + + /* Try the mouse button listener + * This happens whether it is in the window range or not + */ + + if (window->mouse_listener != NULL) + { + /* Mouse listener can eat button presses */ + + if (window->mouse_listener(window, x, y, b, + window->mouse_listener_data)) + { + return 1; + } + } + + /* Is it within the table range? */ + + widgets = (txt_widget_t *)window; + + if (x >= widgets->x && x < (signed)(widgets->x + widgets->w) && + y >= widgets->y && y < (signed)(widgets->y + widgets->h)) + { + txt_widget_mouse_press(window, x, y, b); + return 1; + } + + /* Was one of the action area buttons pressed? */ + + for (i = 0; i < 3; ++i) + { + widget = window->actions[i]; + + if (widget != NULL && x >= widget->x && + x < (signed)(widget->x + widget->w) && y >= widget->y && + y < (signed)(widget->y + widget->h)) + { + int was_focused; + + /* Main table temporarily loses focus when action area button + * is clicked. This way, any active input boxes that depend + * on having focus will save their values before the + * action is performed. + */ + + was_focused = window->table.widget.focused; + txt_set_widget_focus(window, 0); + txt_set_widget_focus(window, was_focused); + + /* Pass through mouse press. */ + + txt_widget_mouse_press(widget, x, y, b); + return 1; + } + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_set_window_action(txt_window_t *window, txt_horiz_align_t position, + TXT_UNCAST_ARG(action)) +{ + TXT_CAST_ARG(txt_widget_t, action); + + if (window->actions[position] != NULL) + { + txt_destroy_widget(window->actions[position]); + } + + window->actions[position] = action; + + /* Maintain parent pointer. */ + + if (action != NULL) + { + action->parent = &window->table.widget; + } +} + +txt_window_t *txt_new_window(const char *title) +{ + int i; + + txt_window_t *win; + + win = malloc(sizeof(txt_window_t)); + + txt_init_table(&win->table, 1); + + if (title == NULL) + { + win->title = NULL; + } + else + { + win->title = strdup(title); + } + + win->x = TXT_SCREEN_W / 2; + win->y = TXT_SCREEN_H / 2; + win->horiz_align = TXT_HORIZ_CENTER; + win->vert_align = TXT_VERT_CENTER; + win->key_listener = NULL; + win->mouse_listener = NULL; + win->help_url = NULL; + + txt_add_widget(win, txt_new_separator(NULL)); + + for (i = 0; i < 3; ++i) + { + win->actions[i] = NULL; + } + + txt_add_desktop_window(win); + + /* Default actions */ + + txt_set_window_action(win, TXT_HORIZ_LEFT, + txt_new_window_escape_action(win)); + txt_set_window_action(win, TXT_HORIZ_RIGHT, + txt_new_window_select_action(win)); + + return win; +} + +void txt_close_window(txt_window_t *window) +{ + int i; + + txt_emit_signal(window, "closed"); + txt_remove_desktop_window(window); + + free(window->title); + + /* Destroy all actions */ + + for (i = 0; i < 3; ++i) + { + if (window->actions[i] != NULL) + { + txt_destroy_widget(window->actions[i]); + } + } + + /* Destroy table and window */ + + txt_destroy_widget(window); +} + +void txt_draw_window(txt_window_t *window) +{ + txt_widget_t *widgets; + + txt_layout_window(window); + + if (window->table.widget.focused) + { + txt_bgcolour(TXT_ACTIVE_WINDOW_BACKGROUND, 0); + } + else + { + txt_bgcolour(TXT_INACTIVE_WINDOW_BACKGROUND, 0); + } + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + + /* Draw the window */ + + txt_draw_window_frame(window->title, window->window_x, window->window_y, + window->window_w, window->window_h); + + /* Draw all widgets */ + + txt_draw_widget(window); + + /* Draw an action area, if we have one */ + + widgets = (txt_widget_t *)window; + + if (widgets->y + widgets->h < window->window_y + window->window_h - 1) + { + /* Separator for action area */ + + txt_draw_separator(window->window_x, widgets->y + widgets->h, + window->window_w); + + /* Action area at the window bottom */ + + draw_action_area(window); + } +} + +void txt_set_window_position(txt_window_t *window, + txt_horiz_align_t horiz_align, + txt_vert_align_t vert_align, int x, int y) +{ + window->vert_align = vert_align; + window->horiz_align = horiz_align; + window->x = x; + window->y = y; +} + +int txt_window_keypress(txt_window_t *window, int c) +{ + int i; + + /* Is this a mouse button ? */ + + if (c >= TXT_MOUSE_BASE && c < TXT_MOUSE_BASE + TXT_MAX_MOUSE_BUTTONS) + { + return mouse_button_press(window, c); + } + + /* Try the window key spy */ + + if (window->key_listener != NULL) + { + /* key listener can eat keys */ + + if (window->key_listener(window, c, window->key_listener_data)) + { + return 1; + } + } + + /* Send to the currently selected widget: */ + + if (txt_widget_key_press(window, c)) + { + return 1; + } + + /* Try all of the action buttons */ + + for (i = 0; i < 3; ++i) + { + if (window->actions[i] != NULL && + txt_widget_key_press(window->actions[i], c)) + { + return 1; + } + } + + return 0; +} + +void txt_set_key_listener(txt_window_t *window, + txt_window_keypress_t key_listener, + void *user_data) +{ + window->key_listener = key_listener; + window->key_listener_data = user_data; +} + +void txt_set_mouse_listener(txt_window_t *window, + txt_window_mouse_press_t mouse_listener, + void *user_data) +{ + window->mouse_listener = mouse_listener; + window->mouse_listener_data = user_data; +} + +void txt_set_window_focus(txt_window_t *window, int focused) +{ + txt_set_widget_focus(window, focused); +} + +void txt_set_window_help_url(txt_window_t *window, const char *help_url) +{ + window->help_url = help_url; +} + +void txt_open_window_help_url(txt_window_t *window) +{ + if (window->help_url != NULL) + { + txt_open_url(window->help_url); + } +} + +txt_window_t *txt_message_box(const char *title, const char *message, ...) +{ + txt_window_t *window; + char buf[256]; + va_list args; + + va_start(args, message); + txt_vsnprintf(buf, sizeof(buf), message, args); + va_end(args); + + window = txt_new_window(title); + txt_add_widget(window, txt_new_label(buf)); + + txt_set_window_action(window, TXT_HORIZ_LEFT, NULL); + txt_set_window_action(window, TXT_HORIZ_CENTER, + txt_new_window_escape_action(window)); + txt_set_window_action(window, TXT_HORIZ_RIGHT, NULL); + + return window; +} diff --git a/games/NXDoom/textscreen/txt_window.h b/games/NXDoom/textscreen/txt_window.h new file mode 100644 index 00000000000..b2803ce564d --- /dev/null +++ b/games/NXDoom/textscreen/txt_window.h @@ -0,0 +1,215 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_window.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_WINDOW_H +#define TXT_WINDOW_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_table.h" +#include "txt_widget.h" + +#include "txt_window_action.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Callback function for window key presses */ + +typedef int (*txt_window_keypress_t)(txt_window_t *window, int key, + void *user_data); +typedef int (*txt_window_mouse_press_t)(txt_window_t *window, int x, int y, + int b, void *user_data); + +struct txt_window_s +{ + /* Base class: all windows are tables with one column. */ + + txt_table_t table; + + /* Window title */ + + char *title; + + /* Screen coordinates of the window */ + + txt_vert_align_t vert_align; + txt_horiz_align_t horiz_align; + int x; + int y; + + /* Actions that appear in the box at the bottom of the window */ + + txt_widget_t *actions[3]; + + /* Callback functions to invoke when keys/mouse buttons are pressed */ + + txt_window_keypress_t key_listener; + void *key_listener_data; + txt_window_mouse_press_t mouse_listener; + void *mouse_listener_data; + + /* These are set automatically when the window is drawn */ + + int window_x; + int window_y; + unsigned int window_w; + unsigned int window_h; + + /* URL of a webpage with help about this window. If set, a help key + * indicator is shown while this window is active. + */ + + const char *help_url; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Open a new window. + * + * @param title Title to display in the titlebar of the new window + * (UTF-8 format). + * @return Pointer to a new @ref txt_window_t structure + * representing the new window. + */ + +txt_window_t *txt_new_window(const char *title); + +/** + * Close a window. + * + * @param window Tine window to close. + */ + +void txt_close_window(txt_window_t *window); + +/** + * Set the position of a window on the screen. + * + * The position is specified as a pair of x, y, coordinates on the + * screen. These specify the position of a particular point on the + * window. The following are some examples: + * + * + * // Centered on the screen: + * + * txt_set_window_position(window, TXT_HORIZ_CENTER, TXT_VERT_CENTER, + * TXT_SCREEN_W / 2, TXT_SCREEN_H / 2); + * + * // Horizontally centered, with top of the window on line 6: + * + * txt_set_window_position(window, TXT_HORIZ_CENTER, TXT_VERT_TOP, + * TXT_SCREEN_W / 2, 6); + * + * // Top-left of window at 20, 6: + * + * txt_set_window_position(window, TXT_HORIZ_LEFT, TXT_VERT_TOP, 20, 6); + * + * + * @param window The window. + * @param horiz_align Horizontal location on the window for the X + * position. + * @param vert_align Vertical location on the window for the Y position. + * @param x X coordinate (horizontal axis) for window position. + * @param y Y coordinate (vertical axis) for window position. + */ + +void txt_set_window_position(txt_window_t *window, + txt_horiz_align_t horiz_align, + txt_vert_align_t vert_align, int x, int y); + +/** + * Set a window action for a given window. + * + * Each window can have up to three window actions, which provide + * keyboard shortcuts that can be used within a given window. + * + * @param window The window. + * @param position The window action slot to set (left, center or right). + * @param action The window action widget. If this is NULL, any + * current window action in the given slot is removed. + */ + +void txt_set_window_action(txt_window_t *window, txt_horiz_align_t position, + TXT_UNCAST_ARG(action)); + +/** + * Set a callback function to be invoked whenever a key is pressed within + * a window. + * + * @param window The window. + * @param key_listener Callback function. + * @param user_data User-specified pointer to pass to the callback + * function. + */ + +void txt_set_key_listener(txt_window_t *window, + txt_window_keypress_t key_listener, + void *user_data); + +/** + * Set a callback function to be invoked whenever a mouse button is pressed + * within a window. + * + * @param window The window. + * @param mouse_listener Callback function. + * @param user_data User-specified pointer to pass to the callback + * function. + */ + +void txt_set_mouse_listener(txt_window_t *window, + txt_window_mouse_press_t mouse_listener, + void *user_data); + +/** + * Open a window displaying a message. + * + * @param title Title of the window (UTF-8 format). + * @param message The message to display in the window + * (UTF-8 format). + * @return The new window. + */ + +txt_window_t *txt_message_box(const char *title, const char *message, ...); + +/** + * Set the help URL for the given window. + * + * @param window The window. + * @param help_url String containing URL of the help page for this + * window, or NULL to set no help for this window. + */ + +void txt_set_window_help_url(txt_window_t *window, const char *help_url); + +/** + * Open the help URL for the given window, if one is set. + * + * @param window The window. + */ + +void txt_open_window_help_url(txt_window_t *window); + +#endif /* TXT_WINDOW_H */ diff --git a/games/NXDoom/textscreen/txt_window_action.c b/games/NXDoom/textscreen/txt_window_action.c new file mode 100644 index 00000000000..d14f41d6676 --- /dev/null +++ b/games/NXDoom/textscreen/txt_window_action.c @@ -0,0 +1,203 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_window_action.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" +#include "txt_window_action.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_window_action_size_calc(TXT_UNCAST_ARG(action)); +static void txt_window_action_drawer(TXT_UNCAST_ARG(action)); +static void txt_window_action_destructor(TXT_UNCAST_ARG(action)); +static int txt_window_action_key_press(TXT_UNCAST_ARG(action), int key); +static void txt_window_action_mouse_press(TXT_UNCAST_ARG(action), int x, + int y, int b); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_window_action_class = +{ + txt_always_selectable, + txt_window_action_size_calc, + txt_window_action_drawer, + txt_window_action_key_press, + txt_window_action_destructor, + txt_window_action_mouse_press, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_window_action_size_calc(TXT_UNCAST_ARG(action)) +{ + TXT_CAST_ARG(txt_window_action_t, action); + char buf[10]; + + txt_get_key_description(action->key, buf, sizeof(buf)); + + /* Width is label length, plus key description length, plus '=' + * and two surrounding spaces. + */ + + action->widget.w = + txt_utf8_strlen(action->label) + txt_utf8_strlen(buf) + 3; + action->widget.h = 1; +} + +static void txt_window_action_drawer(TXT_UNCAST_ARG(action)) +{ + TXT_CAST_ARG(txt_window_action_t, action); + int hovering; + char buf[10]; + + txt_get_key_description(action->key, buf, sizeof(buf)); + + hovering = txt_hovering_over_widget(action); + txt_set_widget_bg(action); + + txt_draw_string(" "); + txt_fgcolour(hovering ? TXT_COLOR_BRIGHT_WHITE : TXT_COLOR_BRIGHT_GREEN); + txt_draw_string(buf); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + txt_draw_string("="); + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + txt_draw_string(action->label); + txt_draw_string(" "); +} + +static void txt_window_action_destructor(TXT_UNCAST_ARG(action)) +{ + TXT_CAST_ARG(txt_window_action_t, action); + + free(action->label); +} + +static int txt_window_action_key_press(TXT_UNCAST_ARG(action), int key) +{ + TXT_CAST_ARG(txt_window_action_t, action); + + if (tolower(key) == tolower(action->key)) + { + txt_emit_signal(action, "pressed"); + return 1; + } + + return 0; +} + +static void txt_window_action_mouse_press(TXT_UNCAST_ARG(action), int x, + int y, int b) +{ + TXT_CAST_ARG(txt_window_action_t, action); + + /* Simulate a press of the key */ + + if (b == TXT_MOUSE_LEFT) + { + txt_window_action_key_press(action, action->key); + } +} + +static void window_close_callback(TXT_UNCAST_ARG(widget), + TXT_UNCAST_ARG(window)) +{ + TXT_CAST_ARG(txt_window_t, window); + + txt_close_window(window); +} + +static void window_select_callback(TXT_UNCAST_ARG(widget), + TXT_UNCAST_ARG(window)) +{ + TXT_CAST_ARG(txt_window_t, window); + + txt_widget_key_press(window, KEY_ENTER); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_window_action_t *txt_new_window_action(int key, const char *label) +{ + txt_window_action_t *action; + + action = malloc(sizeof(txt_window_action_t)); + + txt_init_widget(action, &txt_window_action_class); + action->key = key; + action->label = strdup(label); + + return action; +} + +/* An action with the name "close" the closes the window */ + +txt_window_action_t *txt_new_window_escape_action(txt_window_t *window) +{ + txt_window_action_t *action; + + action = txt_new_window_action(KEY_ESCAPE, "Close"); + txt_signal_connect(action, "pressed", window_close_callback, window); + + return action; +} + +/* Exactly the same as the above, but the button is named "abort" */ + +txt_window_action_t *txt_new_window_abort_action(txt_window_t *window) +{ + txt_window_action_t *action; + + action = txt_new_window_action(KEY_ESCAPE, "Abort"); + txt_signal_connect(action, "pressed", window_close_callback, window); + + return action; +} + +txt_window_action_t *txt_new_window_select_action(txt_window_t *window) +{ + txt_window_action_t *action; + + action = txt_new_window_action(KEY_ENTER, "Select"); + txt_signal_connect(action, "pressed", window_select_callback, window); + + return action; +} diff --git a/games/NXDoom/textscreen/txt_window_action.h b/games/NXDoom/textscreen/txt_window_action.h new file mode 100644 index 00000000000..c76c6ecaec5 --- /dev/null +++ b/games/NXDoom/textscreen/txt_window_action.h @@ -0,0 +1,125 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_window_action.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * 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. + * + ****************************************************************************/ + +#ifndef TXT_WINDOW_ACTION_H +#define TXT_WINDOW_ACTION_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" +#include "txt_window.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/** + * @file txt_window_action.h + * + * Window action widget. + */ + +/** + * Window action widget. + * + * A window action is attached to a window and corresponds to a + * keyboard shortcut that is active within that window. When the + * key is pressed, the action is triggered. + * + * When a window action is triggered, the "pressed" signal is emitted. + */ + +typedef struct txt_window_action_s txt_window_action_t; + +struct txt_window_action_s +{ + txt_widget_t widget; + char *label; + int key; +}; + +/* A window. + * + * A window contains widgets, and may also be treated as a table + * (@ref txt_table_t) containing a single column. + * + * Windows can be created using @ref txt_new_window and closed using + * @ref txt_close_window. When a window is closed, it emits the + * "closed" signal. + * + * In addition to the widgets within a window, windows also have + * a "tray" area at their bottom containing window action widgets. + * These widgets allow keyboard shortcuts to trigger common actions. + * Each window has three slots for keyboard shortcuts. By default, + * the left slot contains an action to close the window when the + * escape button is pressed, while the right slot contains an + * action to activate the currently-selected widget. + */ + +typedef struct txt_window_s txt_window_t; /* Forward definition */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new window action. + * + * @param key The keyboard key that triggers this action. + * @param label Label to display for this action in the tray + * at the bottom of the window (UTF-8 format). + * @return Pointer to the new window action widget. + */ + +txt_window_action_t *txt_new_window_action(int key, const char *label); + +/** + * Create a new window action that closes the window when the + * escape key is pressed. The label "Close" is used. + * + * @param window The window to close. + * @return Pointer to the new window action widget. + */ + +txt_window_action_t *txt_new_window_escape_action(txt_window_t *window); + +/** + * Create a new window action that closes the window when the + * escape key is pressed. The label "Abort" is used. + * + * @param window The window to close. + * @return Pointer to the new window action widget. + */ + +txt_window_action_t *txt_new_window_abort_action(txt_window_t *window); + +/** + * Create a new "select" window action. This does not really do + * anything, but reminds the user that "enter" can be pressed to + * activate the currently-selected widget. + * + * @param window The window. + * @return Pointer to the new window action widget. + */ + +txt_window_action_t *txt_new_window_select_action(txt_window_t *window); + +#endif /* TXT_WINDOW_ACTION_H */